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%
3.3 KB · 72 lines tsx
Raw Blame History
1import Link from "next/link";2import { ListTree } from "lucide-react";3import { getWorkspace } from "@/lib/session";4import { API_PUBLIC_URL } from "@/lib/utils";5import { parseRequestFilters, requestsHref, REQUESTS_PAGE_SIZE, type SearchParams } from "@/lib/requests-filters";6import { listRequests, projectHasRequests, type Scope } from "@/lib/queries/dashboard";7import { PageHeader } from "@/components/ui/page-header";8import { Card } from "@/components/ui/card";9import { Button } from "@/components/ui/button";10import { EmptyState } from "@/components/ui/empty-state";11import { CodeBlock } from "@/components/ui/code-block";12import { RequestsTable } from "@/components/dashboard/requests/requests-table";13import { ActiveFilterChips, RequestsFilterForm } from "@/components/dashboard/requests/requests-filters";14import { Pagination } from "@/components/dashboard/requests/pagination";1516export const dynamic = "force-dynamic";1718const CURL_SNIPPET = `curl -X POST ${API_PUBLIC_URL}/v1/fetch \\19  -H "Authorization: Bearer fch_live_YOUR_KEY" \\20  -H "Content-Type: application/json" \\21  -d '{ "url": "https://example.com", "country": "CA", "format": "text" }'`;2223export default async function RequestsPage({ searchParams }: { searchParams: Promise<SearchParams> }) {24  const [ws, sp] = await Promise.all([getWorkspace(), searchParams]);25  const scope: Scope = { organizationId: ws.organization.id, projectId: ws.project.id };26  const filters = parseRequestFilters(sp);27  const [hasAny, result] = await Promise.all([projectHasRequests(scope), listRequests(scope, filters, REQUESTS_PAGE_SIZE)]);2829  return (30    <div className="flex flex-col gap-5">31      <PageHeader32        title="Requests"33        description={`Every request routed for ${ws.project.name}, with its resolved network, latency, attempts and billed price. Logs are retained according to your plan.`}34      />3536      {!hasAny ? (37        <EmptyState38          icon={ListTree}39          title="No requests yet"40          description="Requests appear here the moment you send one. Start in the Playground, or call the API directly with a key from API Keys."41          action={42            <>43              <Button asChild variant="primary" size="sm">44                <Link href="/dashboard/playground">Open Playground</Link>45              </Button>46              <Button asChild variant="outline" size="sm">47                <Link href="/dashboard/api-keys">Create API key</Link>48              </Button>49            </>50          }51          className="pb-8"52        />53      ) : null}5455      {!hasAny ? (56        <div className="mx-auto w-full max-w-2xl">57          <CodeBlock code={CURL_SNIPPET} lang="bash" title="Your first request (replace the key)" />58        </div>59      ) : (60        <>61          <RequestsFilterForm filters={filters} exportCount={result.total} />62          <ActiveFilterChips filters={filters} />63          <Card className="overflow-hidden">64            <RequestsTable rows={result.rows} emptyMessage={<span>No requests match these filters. Widen the date range or <Link href="/dashboard/requests" className="text-accent underline-offset-4 hover:underline">clear the filters</Link>.</span>} />65          </Card>66          <Pagination page={result.page} pageCount={result.pageCount} total={result.total} pageSize={REQUESTS_PAGE_SIZE} hrefFor={(p) => requestsHref(filters, { page: p })} />67        </>68      )}69    </div>70  );71}72