SPB Git forge

spb/fetcha

Public
11commits 1branches 0releases
1.5 MBsize
maindefault branch
16 days agolast push
TypeScript 97.5% SQL 1.4% Python 0.8%
7.7 KB · 142 lines tsx
Raw Blame History
1import Link from "next/link";2import { requireAdmin } from "@/lib/session";3import { getPlatformKpis, getProviderStats, getRecentFailures, getRecentSignups, parseRange, rangeLabel, rangeStart } from "@/lib/queries/admin";4import { formatBytes, formatCompact, formatMs, formatNumber, formatPercent, formatUsd } from "@/lib/format";5import { PageHeader } from "@/components/ui/page-header";6import { Stat, StatGrid } from "@/components/ui/stat";7import { Table, TableBody, TableCell, TableEmpty, TableHead, TableHeader, TableRow } from "@/components/ui/table";8import { Badge, StatusBadge } from "@/components/ui/badge";9import { DateCell, MarginText, Mono, Panel, ProviderBadge, RangeTabs, numCell } from "@/components/admin/primitives";1011export const dynamic = "force-dynamic";1213export default async function AdminOverview({ searchParams }: { searchParams: Promise<Record<string, string | string[] | undefined>> }) {14  await requireAdmin();15  const sp = await searchParams;16  const range = parseRange(sp.range);17  const since = rangeStart(range);18  const [kpi, providers, signups, failures] = await Promise.all([getPlatformKpis(since), getProviderStats(since), getRecentSignups(8), getRecentFailures(10)]);1920  return (21    <>22      <PageHeader eyebrow="Platform" title="Overview" description={`${rangeLabel(range)} · all organizations. Costs shown here are upstream costs and must never leave this area.`} actions={<RangeTabs current={range} basePath="/admin" />} />2324      <div className="space-y-4">25        <StatGrid cols={4}>26          <Stat label="Requests" value={formatNumber(kpi.requests)} hint={`${formatNumber(kpi.successes)} ok · ${formatNumber(kpi.failed)} failed`} />27          <Stat label="Success rate" value={formatPercent(kpi.successRate)} hint={`avg ${kpi.avgAttempts ? kpi.avgAttempts.toFixed(2) : "—"} attempts · ${formatMs(kpi.avgLatency)}`} />28          <Stat label="Active organizations" value={formatNumber(kpi.activeOrgs)} hint={`${kpi.newOrgs} new orgs · ${kpi.newUsers} new users`} />29          <Stat label="Bandwidth" value={formatBytes(kpi.bytes)} hint="in + out, all networks" />30        </StatGrid>31        <StatGrid cols={4}>32          <Stat label="Revenue (billed)" value={formatUsd(kpi.revenue)} hint="sum of price_usd" />33          <Stat label="Upstream cost" value={formatUsd(kpi.cost)} hint="sum of cost_usd" />34          <Stat label="Gross margin" value={<MarginText value={kpi.margin} pct={kpi.marginPct} />} hint="revenue − upstream cost" />35          <Stat label="Cost / successful request" value={formatUsd(kpi.costPerSuccess, true)} hint="key unit-economics metric" />36        </StatGrid>37      </div>3839      <div className="mt-6 grid gap-6 xl:grid-cols-5">40        <Panel title="Providers" description="Attempt-level stats from request_attempts. Provider names are admin-only." right={<Link href="/admin/providers" className="text-[12.5px] text-accent hover:underline">Manage →</Link>} flush className="xl:col-span-3">41          <Table>42            <TableHeader>43              <TableRow>44                <TableHead>Provider</TableHead>45                <TableHead className="text-right">Attempts</TableHead>46                <TableHead className="text-right">Success</TableHead>47                <TableHead className="text-right">Blocked</TableHead>48                <TableHead className="text-right">Avg latency</TableHead>49                <TableHead className="text-right">Cost</TableHead>50                <TableHead className="text-right">Cost / success</TableHead>51              </TableRow>52            </TableHeader>53            <TableBody>54              {providers.length === 0 ? (55                <TableEmpty colSpan={7}>No attempts in this range.</TableEmpty>56              ) : (57                providers.map((p) => (58                  <TableRow key={p.provider}>59                    <TableCell>60                      <ProviderBadge id={p.provider} />61                    </TableCell>62                    <TableCell className={numCell}>{formatNumber(p.requests)}</TableCell>63                    <TableCell className={numCell}>{formatPercent(p.successRate)}</TableCell>64                    <TableCell className={numCell}>{formatPercent(p.blockedRate)}</TableCell>65                    <TableCell className={numCell}>{formatMs(p.avgLatency)}</TableCell>66                    <TableCell className={numCell}>{formatUsd(p.cost, true)}</TableCell>67                    <TableCell className={`${numCell} font-semibold`}>{formatUsd(p.costPerSuccess, true)}</TableCell>68                  </TableRow>69                ))70              )}71            </TableBody>72          </Table>73        </Panel>7475        <Panel title="Recent signups" right={<Link href="/admin/users" className="text-[12.5px] text-accent hover:underline">All users →</Link>} flush className="xl:col-span-2">76          <ul className="divide-y divide-border">77            {signups.length === 0 ? <li className="px-4 py-8 text-center text-[13px] text-fg-muted">No users yet.</li> : null}78            {signups.map((u) => (79              <li key={u.id} className="flex items-center gap-3 px-4 py-2.5 text-[13px]">80                <div className="min-w-0 flex-1">81                  <Link href={`/admin/users/${u.id}`} className="block truncate font-medium hover:underline">82                    {u.email}83                  </Link>84                  <div className="truncate text-[12px] text-fg-muted">{u.name || "—"}</div>85                </div>86                {u.emailVerified ? <Badge variant="success" dot>verified</Badge> : <Badge variant="outline">unverified</Badge>}87                <DateCell value={u.createdAt} />88              </li>89            ))}90          </ul>91        </Panel>92      </div>9394      <Panel title="Recent failures" description="Latest failed requests across all customers." right={<Link href="/admin/requests?status=failed" className="text-[12.5px] text-accent hover:underline">Explore →</Link>} flush className="mt-6">95        <Table>96          <TableHeader>97            <TableRow>98              <TableHead>Request</TableHead>99              <TableHead>Organization</TableHead>100              <TableHead>Domain</TableHead>101              <TableHead>Error</TableHead>102              <TableHead className="text-right">HTTP</TableHead>103              <TableHead className="text-right">Attempts</TableHead>104              <TableHead className="text-right">When</TableHead>105            </TableRow>106          </TableHeader>107          <TableBody>108            {failures.length === 0 ? (109              <TableEmpty colSpan={7}>No failures recorded.</TableEmpty>110            ) : (111              failures.map((f) => (112                <TableRow key={f.id}>113                  <TableCell>114                    <Link href={`/admin/requests/${f.id}`} className="hover:underline">115                      <Mono>{f.id}</Mono>116                    </Link>117                  </TableCell>118                  <TableCell>119                    <Link href={`/admin/organizations/${f.organizationId}`} className="hover:underline">120                      {f.orgName ?? f.organizationId}121                    </Link>122                  </TableCell>123                  <TableCell className="max-w-[240px] truncate">{f.domain}</TableCell>124                  <TableCell>{f.errorCode ? <StatusBadge status={f.errorCode} /> : "—"}</TableCell>125                  <TableCell className={numCell}>{f.httpStatus ?? "—"}</TableCell>126                  <TableCell className={numCell}>{f.attempts}</TableCell>127                  <TableCell className="text-right">128                    <DateCell value={f.createdAt} />129                  </TableCell>130                </TableRow>131              ))132            )}133          </TableBody>134        </Table>135      </Panel>136      <p className="mt-4 text-[12px] text-fg-subtle">137        Compact figures: {formatCompact(kpi.requests)} requests · {formatCompact(kpi.bytes)} bytes.138      </p>139    </>140  );141}142