"use client"; import { useEffect, useMemo, useState } from "react"; import Link from "next/link"; import { Download, Search, Building2, CheckCircle2, Clock, Archive } from "lucide-react"; import { api, apiBase } from "@/lib/api"; function bar(used: number, limit: number | null) { if (limit == null) return 12; return Math.min(100, Math.round((used / limit) * 100)); } export default function AdminOrgs() { const [orgs, setOrgs] = useState([]); const [loading, setLoading] = useState(true); const [q, setQ] = useState(""); const [status, setStatus] = useState(""); const [plan, setPlan] = useState(""); useEffect(() => { api("/admin/organizations").then(setOrgs).catch(() => {}).finally(() => setLoading(false)); }, []); const filtered = useMemo(() => orgs.filter((o) => (!q || o.name.toLowerCase().includes(q.toLowerCase())) && (!status || o.subscription_status === status) && (!plan || o.plan === plan)), [orgs, q, status, plan]); const kpis = { total: orgs.length, active: orgs.filter((o) => o.subscription_status === "active").length, grace: orgs.filter((o) => o.subscription_status === "grace").length, archived: orgs.filter((o) => o.subscription_status === "archived").length, }; const plans = [...new Set(orgs.map((o) => o.plan).filter(Boolean))]; return (

All Organizations

Utilization across every client org.

CSV PDF
} tint="bg-blue-50 text-blue-600" /> } tint="bg-emerald-50 text-emerald-600" /> } tint="bg-amber-50 text-amber-600" /> } tint="bg-slate-100 text-slate-600" />
setQ(e.target.value)} placeholder="Search org…" className="pl-9 pr-3 py-2 w-52 rounded-lg border border-slate-200 text-sm" />
{filtered.map((o) => ( ))} {!loading && filtered.length === 0 && }
Organization Plan Products Users QRs Scans Status
{o.name} {o.plan ?? "—"}
{o.products_used} / {o.product_limit ?? "∞"}
{o.users_used} / {o.user_limit ?? "∞"} {o.qr_codes} {o.total_scans} {o.subscription_status}
No organizations match.
); } function Kpi({ label, value, icon, tint }: any) { return (
{icon}
{label}
{value}
); }