import Link from "next/link"; import { ArrowRight, BookOpen, CheckCircle2, Circle, KeyRound, Play } from "lucide-react"; import { PLAN_LIMITS, isUnlimited, normalizePlan } from "@fetcha/core"; import { getWorkspace } from "@/lib/session"; import { formatBytes, formatCompact, formatMs, formatNumber, formatPercent, formatUsd, formatDateOnly } from "@/lib/format"; import { getActiveSessionCount, getMonthStats, getNetworkDistribution, getOnboardingState, getRecentRequests, getRequestSeries, type Scope } from "@/lib/queries/dashboard"; import { PageHeader, SectionTitle } from "@/components/ui/page-header"; import { Stat, StatGrid } from "@/components/ui/stat"; import { Badge } from "@/components/ui/badge"; import { Alert } from "@/components/ui/alert"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { EmptyState } from "@/components/ui/empty-state"; import { ChartFrame } from "@/components/dashboard/charts/chart-frame"; import { RequestsBarChart } from "@/components/dashboard/charts/requests-bar-chart"; import { NetworkDonut } from "@/components/dashboard/charts/network-donut"; import { QuotaBar } from "@/components/dashboard/charts/quota-bar"; import { RequestsTable } from "@/components/dashboard/requests/requests-table"; export const dynamic = "force-dynamic"; export default async function DashboardOverviewPage({ searchParams }: { searchParams: Promise> }) { const [ws, sp] = await Promise.all([getWorkspace(), searchParams]); const scope: Scope = { organizationId: ws.organization.id, projectId: ws.project.id }; const now = new Date(); const monthStart = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1)); const thirtyDaysAgo = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() - 29)); const [month, series, network, recent, onboarding, activeSessions] = await Promise.all([ getMonthStats(scope, monthStart), getRequestSeries(scope, thirtyDaysAgo, "day"), getNetworkDistribution(scope, thirtyDaysAgo), getRecentRequests(scope, 8), getOnboardingState(scope), getActiveSessionCount(scope), ]); const limits = PLAN_LIMITS[normalizePlan(ws.organization.plan)]; const unlimited = isUnlimited(limits); const projectLimit = ws.project.monthlyRequestLimit; const effectiveLimit = projectLimit && (unlimited || projectLimit < limits.monthly_requests) ? projectLimit : unlimited ? null : limits.monthly_requests; const firstName = ws.user.name?.trim().split(/\s+/)[0] || ws.user.email.split("@")[0]; const verified = sp.verified === "1"; const seriesHasData = series.some((p) => p.success + p.failed > 0); const onboardingDone = onboarding.hasApiKey && onboarding.hasRequest; const softLimit = ws.project.softLimitUsd ?? ws.organization.softLimitUsd ?? null; const hardLimit = ws.project.hardLimitUsd ?? ws.organization.hardLimitUsd ?? null; const limitSource = ws.project.softLimitUsd !== null || ws.project.hardLimitUsd !== null ? "project" : "organization"; return (
{verified ? ( Production API access is unlocked for this organization. ) : null} {ws.project.name} · {ws.project.environment} } title={ Welcome back, {firstName} {limits.label} plan } description={`Here is what happened in ${ws.project.name} since ${formatDateOnly(monthStart)}. Figures reflect requests billed to this project.`} actions={ <> } /> {!onboardingDone ? ( Getting started Three steps to your first routed request. This card disappears once they are all done.
    {[ { done: onboarding.hasApiKey, icon: KeyRound, title: "Create an API key", body: "Keys are shown once at creation. Use a test key while you explore.", href: "/dashboard/api-keys", cta: "Create key" }, { done: onboarding.hasRequest, icon: Play, title: "Run your first request", body: "Try the Playground or send a POST /v1/fetch with cURL.", href: "/dashboard/playground", cta: "Open Playground" }, { done: false, icon: BookOpen, title: "Read the quickstart", body: "Five minutes on routing, network classes, sessions and error codes.", href: "/docs/quickstart", cta: "Read docs", external: true }, ].map((step) => (
  1. {step.done ? : }
    {step.title}

    {step.body}

    {!step.done ? ( ) : null}
  2. ))}
) : null}
manage} />
Quota and limits {unlimited ? "Requests are unlimited on this private platform" : `Monthly request allowance on the ${limits.label} plan`} {projectLimit ? ` (project cap: ${formatNumber(projectLimit)})` : ""}. Spending limits stop new requests with USAGE_LIMIT_REACHED.
Soft limit
{softLimit === null ? not set : formatUsd(softLimit)}
{softLimit === null ? "alerts only" : `${limitSource} · email alert`}
Hard limit
{hardLimit === null ? not set : formatUsd(hardLimit)}
{hardLimit === null ? "requests never blocked" : `${limitSource} · blocks requests`}
Spend so far: {formatUsd(month.spendUsd)} {hardLimit ? <> of {formatUsd(hardLimit)} ({formatPercent(Math.min(100, (month.spendUsd / hardLimit) * 100), 0)}) : null} ·{" "} Edit limits
p.success + p.failed > 0).map((p) => [p.t.slice(0, 10), p.success, p.failed]) }} > {network.unresolved > 0 ?

{formatNumber(network.unresolved)} request(s) failed before a network was selected and are not counted.

: null}
All requests } > Recent requests {recent.length ? ( ) : ( } /> )}
); }