"use client"; import * as React from "react"; import { COUNTRIES } from "@fetcha/core/client"; import { createProject, updateProject } from "@/actions/projects"; import { Button } from "@/components/ui/button"; import { Input, NativeSelect, Textarea } from "@/components/ui/input"; import { Field, FieldError, Hint, Label } from "@/components/ui/label"; import { Alert } from "@/components/ui/alert"; import { Badge } from "@/components/ui/badge"; import { cn } from "@/lib/utils"; export interface ProjectFormValues { name: string; description: string | null; environment: string; defaultCountry: string | null; defaultNetwork: string; logLevel: string; softLimitUsd: number | null; hardLimitUsd: number | null; monthlyRequestLimit: number | null; } const LOG_LEVELS = [ { v: "none", title: "None", desc: "Only billing counters. No URL, headers or timing are kept — nothing to inspect in Requests." }, { v: "metadata", title: "Metadata", desc: "URL, status, network, country, timing and size. Recommended for production.", recommended: true }, { v: "headers", title: "Headers", desc: "Metadata plus request and response headers. Authorization and Cookie are always redacted." }, { v: "full", title: "Full", desc: "Headers plus a response body preview, kept for your plan's retention window. Use for debugging only." }, ] as const; const NETWORKS = [ { v: "auto", label: "Auto (recommended)", live: true }, { v: "residential", label: "Residential", live: true }, { v: "datacenter", label: "Datacenter — coming soon", live: false }, { v: "isp", label: "ISP — coming soon", live: false }, { v: "mobile", label: "Mobile — coming soon", live: false }, ]; const COUNTRY_OPTIONS = Object.entries(COUNTRIES).sort((a, b) => a[1].localeCompare(b[1])); export function ProjectForm({ mode, projectId, initial, onSuccess, onCancel, submitLabel, compact, }: { mode: "create" | "edit"; projectId?: string; initial?: Partial; onSuccess?: (id: string) => void; onCancel?: () => void; submitLabel?: string; compact?: boolean; }) { const [pending, startTransition] = React.useTransition(); const [error, setError] = React.useState(null); const [fieldError, setFieldError] = React.useState(null); const [saved, setSaved] = React.useState(false); const [logLevel, setLogLevel] = React.useState(initial?.logLevel ?? "metadata"); const [environment, setEnvironment] = React.useState(initial?.environment ?? "production"); function submit(e: React.FormEvent) { e.preventDefault(); setError(null); setFieldError(null); setSaved(false); const fd = new FormData(e.currentTarget); startTransition(async () => { const res = mode === "create" ? await createProject(fd) : await updateProject(projectId!, fd); if (!res.ok) { setError(res.error); setFieldError(res.field ?? null); return; } setSaved(true); const id = mode === "create" && "data" in res && res.data ? res.data.id : projectId!; onSuccess?.(id); }); } const err = (f: string) => (fieldError === f ? error : null); return (
{error && !fieldError ? {error} : null} {saved && mode === "edit" ? Project settings saved. : null}
{err("name")}
{(["production", "development"] as const).map((env) => ( ))}