"use client"; import * as React from "react"; import { useRouter } from "next/navigation"; import { Plus } from "lucide-react"; import { COUNTRIES } from "@fetcha/core/client"; import { createDashboardSession, type DashboardSession } from "@/actions/sessions"; import { Button } from "@/components/ui/button"; import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; import { Field, FieldError, Hint, Label } from "@/components/ui/label"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Alert } from "@/components/ui/alert"; import { CodeBlock } from "@/components/ui/code-block"; import { CopyButton } from "@/components/ui/copy-button"; import { Badge } from "@/components/ui/badge"; const ANY = "__any__"; function curlFor(sessionId: string, apiBase: string, country?: string): string { const body: Record = { url: "https://example.com", session: sessionId }; if (country) body.country = country; return `curl -X POST ${apiBase}/v1/fetch \\ -H "Authorization: Bearer fch_live_YOUR_KEY" \\ -H "Content-Type: application/json" \\ -d '${JSON.stringify(body)}'`; } export function CreateSessionDialog({ apiBase, defaultCountry }: { apiBase: string; defaultCountry?: string | null }) { const router = useRouter(); const [open, setOpen] = React.useState(false); const [pending, startTransition] = React.useTransition(); const [error, setError] = React.useState(null); const [created, setCreated] = React.useState(null); const [country, setCountry] = React.useState(defaultCountry && defaultCountry in COUNTRIES ? defaultCountry : ANY); const [network, setNetwork] = React.useState("auto"); const [region, setRegion] = React.useState(""); const [city, setCity] = React.useState(""); const [ttl, setTtl] = React.useState(600); const [label, setLabel] = React.useState(""); function reset() { setError(null); setCreated(null); setRegion(""); setCity(""); setTtl(600); setLabel(""); setNetwork("auto"); } function submit(e: React.FormEvent) { e.preventDefault(); setError(null); startTransition(async () => { const res = await createDashboardSession({ country: country === ANY ? undefined : country, region: region || undefined, city: city || undefined, network: network as "auto" | "residential", ttl, label: label || undefined, }); if (!res.ok) { setError(res.error); return; } setCreated(res.data ?? null); router.refresh(); }); } const ttlLabel = ttl >= 60 ? `${Math.floor(ttl / 60)} min${ttl % 60 ? ` ${ttl % 60} s` : ""}` : `${ttl} s`; return ( { setOpen(o); if (!o) reset(); }} > {created ? ( <> Session created Pass this id as session in each request. It keeps the same exit IP and cookies until it expires or you close it.
{created.id} {created.status}
Network
{created.network}
Country
{created.country ?? "any"}
Expires
{new Date(created.expires_at).toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit" })}
) : (
Create a sticky session A session pins one exit IP and carries cookies across requests, for logins, carts and paginated crawls. Sessions live 1 to 30 minutes. {error ? {error} : null}
Auto resolves to residential today. setRegion(e.target.value)} placeholder="quebec" maxLength={64} autoComplete="off" /> setCity(e.target.value)} placeholder="montreal" maxLength={128} autoComplete="off" />
{ttlLabel}
setTtl(Number(e.target.value))} className="h-1.5 flex-1 cursor-pointer appearance-none rounded-full bg-bg-muted accent-[var(--accent)]" aria-valuetext={ttlLabel} /> setTtl(Math.min(1800, Math.max(60, Number(e.target.value) || 60)))} className="w-24 font-mono" aria-label="TTL in seconds" />
Seconds, 60–1800. The clock does not reset on use.
setLabel(e.target.value)} placeholder="checkout-flow-ca" maxLength={128} autoComplete="off" />
)}
); }