TypeScript 97.5%
SQL 1.4%
Python 0.8%
1import Link from "next/link";2import { ListTree } from "lucide-react";3import { requireAdmin } from "@/lib/session";4import { CONCRETE_NETWORKS, PROVIDER_IDS, getRequestFilterOptions, listRequests, requestFiltersFromParams, str } from "@/lib/queries/admin";5import { formatBytes, formatMs, formatNumber, formatUsd } from "@/lib/format";6import { PageHeader } from "@/components/ui/page-header";7import { StatusBadge } from "@/components/ui/badge";8import { Button } from "@/components/ui/button";9import { Input, NativeSelect } from "@/components/ui/input";10import { Field, Label } from "@/components/ui/label";11import { EmptyState } from "@/components/ui/empty-state";12import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";13import { DateCell, Mono, NetworkBadge, Pagination, Panel, ProviderBadge, numCell } from "@/components/admin/primitives";1415export const dynamic = "force-dynamic";1617export default async function AdminRequestsPage({ searchParams }: { searchParams: Promise<Record<string, string | string[] | undefined>> }) {18 await requireAdmin();19 const sp = await searchParams;20 const f = requestFiltersFromParams(sp);21 const [data, opts] = await Promise.all([listRequests(f), getRequestFilterOptions()]);22 const params: Record<string, string | undefined> = {23 org: f.org,24 project: f.project,25 domain: f.domain,26 status: f.status,27 provider: f.provider,28 network: f.network,29 country: f.country,30 source: f.source,31 errorCode: f.errorCode,32 from: f.from,33 to: f.to,34 id: f.id,35 };36 const active = Object.values(params).filter(Boolean).length;3738 return (39 <>40 <PageHeader eyebrow="Explorer" title="Requests" description={`${formatNumber(data.total)} matching requests. Provider chain is shown per request (admin-only).`} />4142 <Panel className="mb-4" title="Filters" right={active ? <Button asChild variant="ghost" size="xs"><Link href="/admin/requests">Clear {active}</Link></Button> : null}>43 <form method="get" action="/admin/requests" className="grid gap-3 sm:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6">44 <Field>45 <Label htmlFor="f-id">Request id</Label>46 <Input id="f-id" name="id" defaultValue={f.id} placeholder="req_…" className="h-8 font-mono text-[12.5px]" />47 </Field>48 <Field>49 <Label htmlFor="f-org">Organization</Label>50 <NativeSelect id="f-org" name="org" defaultValue={f.org ?? ""} className="h-8 text-[13px]">51 <option value="">Any</option>52 {opts.orgs.map((o) => (53 <option key={o.id} value={o.id}>54 {o.name}55 </option>56 ))}57 </NativeSelect>58 </Field>59 <Field>60 <Label htmlFor="f-project">Project id</Label>61 <Input id="f-project" name="project" defaultValue={f.project} placeholder="proj_…" className="h-8 font-mono text-[12.5px]" />62 </Field>63 <Field>64 <Label htmlFor="f-domain">Domain</Label>65 <Input id="f-domain" name="domain" defaultValue={f.domain} placeholder="contains…" className="h-8 text-[13px]" />66 </Field>67 <Field>68 <Label htmlFor="f-status">Status</Label>69 <NativeSelect id="f-status" name="status" defaultValue={f.status ?? ""} className="h-8 text-[13px]">70 <option value="">Any</option>71 <option value="success">success</option>72 <option value="failed">failed</option>73 <option value="pending">pending</option>74 </NativeSelect>75 </Field>76 <Field>77 <Label htmlFor="f-provider">Provider</Label>78 <NativeSelect id="f-provider" name="provider" defaultValue={f.provider ?? ""} className="h-8 font-mono text-[12.5px]">79 <option value="">Any</option>80 {PROVIDER_IDS.map((p) => (81 <option key={p} value={p}>82 {p}83 </option>84 ))}85 </NativeSelect>86 </Field>87 <Field>88 <Label htmlFor="f-network">Network</Label>89 <NativeSelect id="f-network" name="network" defaultValue={f.network ?? ""} className="h-8 text-[13px]">90 <option value="">Any</option>91 {CONCRETE_NETWORKS.map((n) => (92 <option key={n} value={n}>93 {n}94 </option>95 ))}96 <option value="direct">direct</option>97 </NativeSelect>98 </Field>99 <Field>100 <Label htmlFor="f-country">Country</Label>101 <Input id="f-country" name="country" defaultValue={f.country} placeholder="CA" maxLength={2} className="h-8 font-mono uppercase text-[12.5px]" />102 </Field>103 <Field>104 <Label htmlFor="f-source">Source</Label>105 <NativeSelect id="f-source" name="source" defaultValue={f.source ?? ""} className="h-8 text-[13px]">106 <option value="">Any</option>107 <option value="api">api</option>108 <option value="playground">playground</option>109 <option value="sdk">sdk</option>110 </NativeSelect>111 </Field>112 <Field>113 <Label htmlFor="f-error">Error code</Label>114 <NativeSelect id="f-error" name="errorCode" defaultValue={f.errorCode ?? ""} className="h-8 font-mono text-[12.5px]">115 <option value="">Any</option>116 {opts.errorCodes.map((c) => (117 <option key={c} value={c}>118 {c}119 </option>120 ))}121 </NativeSelect>122 </Field>123 <Field>124 <Label htmlFor="f-from">From</Label>125 <Input id="f-from" name="from" type="datetime-local" defaultValue={f.from} className="h-8 text-[12.5px]" />126 </Field>127 <Field>128 <Label htmlFor="f-to">To</Label>129 <Input id="f-to" name="to" type="datetime-local" defaultValue={f.to} className="h-8 text-[12.5px]" />130 </Field>131 <div className="flex items-end gap-2 sm:col-span-2 lg:col-span-4 xl:col-span-6">132 <Button type="submit" size="sm">133 Apply filters134 </Button>135 <span className="text-[12px] text-fg-subtle">Times are interpreted in the server timezone.</span>136 </div>137 </form>138 </Panel>139140 <Panel flush>141 {data.total === 0 ? (142 <div className="p-4">143 <EmptyState icon={ListTree} title="No requests match" description="Loosen a filter or widen the date range." compact />144 </div>145 ) : (146 <>147 <Table>148 <TableHeader>149 <TableRow>150 <TableHead>Request</TableHead>151 <TableHead>Org / project</TableHead>152 <TableHead>Domain</TableHead>153 <TableHead>Status</TableHead>154 <TableHead>Network</TableHead>155 <TableHead>Providers</TableHead>156 <TableHead>Geo</TableHead>157 <TableHead className="text-right">Att.</TableHead>158 <TableHead className="text-right">Latency</TableHead>159 <TableHead className="text-right">Bytes</TableHead>160 <TableHead className="text-right">Cost</TableHead>161 <TableHead className="text-right">Price</TableHead>162 <TableHead className="text-right">When</TableHead>163 </TableRow>164 </TableHeader>165 <TableBody>166 {data.rows.map((r) => (167 <TableRow key={r.id}>168 <TableCell>169 <Link href={`/admin/requests/${r.id}`} className="hover:underline">170 <Mono>{r.id}</Mono>171 </Link>172 <div className="text-[11.5px] text-fg-subtle">{r.source}</div>173 </TableCell>174 <TableCell className="max-w-[180px]">175 <Link href={`/admin/organizations/${r.organizationId}`} className="block truncate hover:underline">176 {r.orgName ?? r.organizationId}177 </Link>178 <div className="truncate text-[11.5px] text-fg-subtle">{r.projectName ?? r.projectId}</div>179 </TableCell>180 <TableCell className="max-w-[220px] truncate" title={r.domain}>181 <Link href={`/admin/domains/${encodeURIComponent(r.domain)}`} className="hover:underline">182 {r.domain}183 </Link>184 </TableCell>185 <TableCell>186 <div className="flex items-center gap-1.5">187 <StatusBadge status={r.status} />188 {r.httpStatus ? <Mono className="text-fg-muted">{r.httpStatus}</Mono> : null}189 </div>190 {r.errorCode ? <div className="font-mono text-[11px] text-danger">{r.errorCode}</div> : null}191 </TableCell>192 <TableCell>193 <NetworkBadge network={r.network} />194 {r.requestedNetwork !== r.network ? <div className="text-[11px] text-fg-subtle">asked {r.requestedNetwork}</div> : null}195 </TableCell>196 <TableCell>197 <div className="flex flex-wrap gap-1">198 {r.providers ? r.providers.split(",").map((p, i) => <ProviderBadge key={i} id={p} />) : <span className="text-fg-subtle">—</span>}199 </div>200 </TableCell>201 <TableCell>202 <Mono>{r.country ?? "—"}</Mono>203 </TableCell>204 <TableCell className={numCell}>{r.attempts}</TableCell>205 <TableCell className={numCell}>{formatMs(r.latencyMs)}</TableCell>206 <TableCell className={numCell}>{formatBytes(r.bytesIn)}</TableCell>207 <TableCell className={numCell}>{formatUsd(r.costUsd, true)}</TableCell>208 <TableCell className={numCell}>{formatUsd(r.priceUsd, true)}</TableCell>209 <TableCell className="text-right">210 <DateCell value={r.createdAt} />211 </TableCell>212 </TableRow>213 ))}214 </TableBody>215 </Table>216 <Pagination page={data.page} pages={data.pages} total={data.total} pageSize={data.pageSize} basePath="/admin/requests" params={params} />217 </>218 )}219 </Panel>220 {str(sp.page) && data.rows.length === 0 && data.total > 0 ? <p className="mt-2 text-[12px] text-fg-subtle">This page is empty — go back to page 1.</p> : null}221 </>222 );223}224