import { CreditCard } from "lucide-react"; import { getTranslations } from "next-intl/server"; import { StatusCard } from "@/components/admin/dashboard"; import { prisma } from "@/lib/prisma"; export const dynamic = "force-dynamic"; type SubRow = { id: number; userId: number | null; subscriptionType: string | null; expiresAt: number; }; function fmt(ts: number): string { return new Date(ts * 1000).toISOString().slice(0, 16).replace("T", " "); } export default async function AdminSubscriptions() { const t = await getTranslations("pages.admin.subscriptions"); const now = Math.floor(Date.now() / 1000); let active: SubRow[] = []; try { const rows = await prisma.usersSubscriptions.findMany({ where: { active: 1 }, orderBy: { id: "desc" }, take: 500, }); active = rows .map((r) => ({ id: r.id, userId: r.userId, subscriptionType: r.subscriptionType, expiresAt: (r.timestampStart ?? 0) + (r.duration ?? 0), })) .filter((r) => r.expiresAt > now) .sort((a, b) => a.expiresAt - b.expiresAt); } catch { active = []; } const soonCutoff = now + 7 * 24 * 60 * 60; const expiringSoon = active.filter((s) => s.expiresAt <= soonCutoff).length; const types = new Set(active.map((s) => s.subscriptionType ?? "—")).size; return (

{t("title")}

{t("subtitle")}

0 ? "warn" : "ok"} icon="⏳" />

{t("activeSubscriptions")} ({active.length})

{active.map((s) => { const soon = s.expiresAt <= soonCutoff; return ( ); })}
{t("colSubId")} {t("colUserId")} {t("colType")} {t("colStatus")} {t("colExpires")}
{s.id} {s.userId ?? "—"} {s.subscriptionType ?? "—"} {soon ? t("statusExpiring") : t("statusActive")} {fmt(s.expiresAt)}
{active.length === 0 ?
{t("noSubscriptions")}
: null}
); }