"use client"; import { useQuery, keepPreviousData } from "@tanstack/react-query"; import { useState } from "react"; import { format } from "date-fns"; import { sel } from "@nextsparkjs/core/selectors"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@nextsparkjs/core/components/ui/card"; import { Button } from "@nextsparkjs/core/components/ui/button"; import { Badge } from "@nextsparkjs/core/components/ui/badge"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@nextsparkjs/core/components/ui/table"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@nextsparkjs/core/components/ui/select"; import { ArrowLeft, CreditCard, DollarSign, TrendingUp, AlertCircle, CheckCircle, Clock, XCircle, Loader2, AlertTriangle, RefreshCw, ExternalLink, } from "lucide-react"; import Link from "next/link"; import { getTemplateOrDefaultClient } from "@nextsparkjs/registries/template-registry.client"; import { SearchInput, PaginationControls, } from "@nextsparkjs/core/components/superadmin/filters"; interface SubscriptionPlan { id: string; slug: string; name: string; type: string; priceMonthly: number | null; priceYearly: number | null; } interface SubscriptionTeam { id: string; name: string; owner: { name: string; email: string; }; } interface Subscription { id: string; team: SubscriptionTeam; plan: SubscriptionPlan; billingInterval: 'monthly' | 'yearly'; status: string; currentPeriodStart: string; currentPeriodEnd: string; trialEndsAt: string | null; canceledAt: string | null; cancelAtPeriodEnd: boolean; externalSubscriptionId: string | null; paymentProvider: string | null; providerDashboardUrl: string | null; createdAt: string; } interface PlanDistribution { slug: string; name: string; count: number; } interface SubscriptionsData { stats: { total: number; active: number; trialing: number; canceled: number; pastDue: number; free: number; paid: number; monthly: number; yearly: number; mrr: number; mrrFormatted: string; arr: number; arrFormatted: string; }; planDistribution: PlanDistribution[]; subscriptions: Subscription[]; pagination: { page: number; limit: number; total: number; totalPages: number; hasMore: boolean; }; metadata: { requestedBy: string; requestedAt: string; source: string; }; } const statusConfig = { active: { label: "Active", icon: CheckCircle, variant: "default" as const, className: "bg-green-100 text-green-800 border-green-200", }, trialing: { label: "Trial", icon: Clock, variant: "secondary" as const, className: "bg-blue-100 text-blue-800 border-blue-200", }, past_due: { label: "Past Due", icon: AlertCircle, variant: "destructive" as const, className: "bg-yellow-100 text-yellow-800 border-yellow-200", }, canceled: { label: "Canceled", icon: XCircle, variant: "outline" as const, className: "bg-gray-100 text-gray-800 border-gray-200", }, expired: { label: "Expired", icon: XCircle, variant: "outline" as const, className: "bg-red-100 text-red-800 border-red-200", }, paused: { label: "Paused", icon: Clock, variant: "secondary" as const, className: "bg-orange-100 text-orange-800 border-orange-200", }, }; /** * Subscriptions Management Page * * Comprehensive subscription overview for superadmins. * Shows stats, MRR, plan distribution, and subscription list. */ function SubscriptionsPage() { const [statusFilter, setStatusFilter] = useState("all"); const [planFilter, setPlanFilter] = useState("all"); const [intervalFilter, setIntervalFilter] = useState("all"); const [searchQuery, setSearchQuery] = useState(""); const [page, setPage] = useState(1); const [limit, setLimit] = useState(20); // Build query params const getQueryParams = () => { const params = new URLSearchParams(); if (searchQuery) params.set("search", searchQuery); if (statusFilter !== "all") params.set("status", statusFilter); if (planFilter !== "all") params.set("plan", planFilter); if (intervalFilter !== "all") params.set("interval", intervalFilter); params.set("page", String(page)); params.set("limit", String(limit)); return params.toString(); }; // Handle filter changes const handleStatusChange = (status: string) => { setStatusFilter(status); setPage(1); }; const handlePlanChange = (plan: string) => { setPlanFilter(plan); setPage(1); }; const handleIntervalChange = (interval: string) => { setIntervalFilter(interval); setPage(1); }; const handleSearchChange = (value: string) => { setSearchQuery(value); setPage(1); }; const handleLimitChange = (newLimit: number) => { setLimit(newLimit); setPage(1); }; const hasActiveFilters = searchQuery || statusFilter !== "all" || planFilter !== "all" || intervalFilter !== "all"; const clearAllFilters = () => { setSearchQuery(""); setStatusFilter("all"); setPlanFilter("all"); setIntervalFilter("all"); setPage(1); }; // Fetch subscriptions data const { data, isLoading, isFetching, error, refetch } = useQuery({ queryKey: ["superadmin-subscriptions", statusFilter, planFilter, intervalFilter, searchQuery, page, limit], queryFn: async () => { const queryString = getQueryParams(); const url = `/api/superadmin/subscriptions${queryString ? `?${queryString}` : ""}`; const response = await fetch(url); if (!response.ok) { throw new Error("Failed to fetch subscriptions data"); } return response.json(); }, retry: 2, staleTime: 30000, placeholderData: keepPreviousData, }); // Loading state if (isLoading) { return (

Subscriptions

Loading subscription data...

); } // Error state if (error) { return (

Subscriptions

Error loading data

Error Loading Subscriptions {error instanceof Error ? error.message : "Failed to load subscription data"}
); } const stats = data?.stats; const subscriptions = data?.subscriptions || []; return (
{/* Header */}

Subscriptions

Manage billing and subscription overview

{/* Stats Cards */}
{/* MRR Card */} Monthly Recurring Revenue
{stats?.mrrFormatted || "$0.00"}

From {stats?.monthly || 0} monthly subscriptions

{/* ARR Card */} Annual Recurring Revenue
{stats?.arrFormatted || "$0.00"}

From {stats?.yearly || 0} yearly subscriptions

{/* Active Subscriptions */} Active Subscriptions
{stats?.active || 0}

{stats?.trialing || 0} in trial

{/* Total Subscriptions */} Total Subscriptions
{stats?.total || 0}

{stats?.free || 0} free, {stats?.paid || 0} paid

{/* Issues */} 0 ? "border-yellow-200 bg-yellow-50/50" : ""}> Needs Attention 0 ? "text-yellow-600" : "text-muted-foreground"}`} />
{stats?.pastDue || 0}

Past due payments

{/* Plan Distribution */} {data?.planDistribution && data.planDistribution.length > 0 && ( Plan Distribution Active subscriptions by plan
{data.planDistribution.map((plan) => (
{plan.name}
{plan.count}
))}
)} {/* Filters */}
{hasActiveFilters && ( )} {isFetching && !isLoading && ( )}
{/* Subscriptions Table */} Subscriptions ({data?.pagination.total || 0}) All team subscriptions and their billing status {subscriptions.length === 0 ? (

No subscriptions found

{hasActiveFilters ? "Try adjusting your filters" : "No subscriptions have been created yet"}

) : (
Team Plan Status Price Period End Actions {subscriptions.map((sub) => { const statusInfo = statusConfig[sub.status as keyof typeof statusConfig] || statusConfig.active; const StatusIcon = statusInfo.icon; return (
{sub.team.name}
{sub.team.owner.email}
{sub.plan.name} {statusInfo.label} {sub.cancelAtPeriodEnd && (
Cancels at period end
)}
{(() => { const price = sub.billingInterval === 'yearly' ? sub.plan.priceYearly : sub.plan.priceMonthly; const suffix = sub.billingInterval === 'yearly' ? '/yr' : '/mo'; return price && price > 0 ? `$${(price / 100).toFixed(2)}${suffix}` : "Free"; })()}
{format(new Date(sub.currentPeriodEnd), "MMM dd, yyyy")}
{sub.trialEndsAt && new Date(sub.trialEndsAt) > new Date() && (
Trial ends {format(new Date(sub.trialEndsAt), "MMM dd")}
)}
{sub.providerDashboardUrl && ( )}
); })}
)} {/* Pagination */} {data?.pagination && data.pagination.totalPages > 1 && (
)}
{/* Metadata Footer */} {data?.metadata && (

Last updated:{" "} {new Date(data.metadata.requestedAt).toLocaleString()}

Requested by: {data.metadata.requestedBy}

Source: {data.metadata.source}

)}
); } export default getTemplateOrDefaultClient("app/superadmin/subscriptions/page.tsx", SubscriptionsPage);