diff --git a/surfsense_web/app/dashboard/[workspace_id]/playground/components/api-reference.tsx b/surfsense_web/app/dashboard/[workspace_id]/playground/components/api-reference.tsx index 1bc4fe1d3..caf7cf9eb 100644 --- a/surfsense_web/app/dashboard/[workspace_id]/playground/components/api-reference.tsx +++ b/surfsense_web/app/dashboard/[workspace_id]/playground/components/api-reference.tsx @@ -1,6 +1,6 @@ "use client"; -import { Check, Copy } from "lucide-react"; +import { Check, ChevronRight, Copy } from "lucide-react"; import { useMemo, useState } from "react"; import { Button } from "@/components/ui/button"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; @@ -45,8 +45,9 @@ function SchemaBlock({ title, schema }: { title: string; schema: Record JSON.stringify(schema, null, 2), [schema]); return (
- - {title} + + {title} +
diff --git a/surfsense_web/app/dashboard/[workspace_id]/playground/components/playground-index.tsx b/surfsense_web/app/dashboard/[workspace_id]/playground/components/playground-index.tsx index 17f0591c8..7e9d45373 100644 --- a/surfsense_web/app/dashboard/[workspace_id]/playground/components/playground-index.tsx +++ b/surfsense_web/app/dashboard/[workspace_id]/playground/components/playground-index.tsx @@ -45,7 +45,7 @@ export function PlaygroundIndex({ workspaceId }: { workspaceId: number }) {
-

Runs

+

API Runs

See every API run in this workspace

diff --git a/surfsense_web/app/dashboard/[workspace_id]/playground/components/playground-runner.tsx b/surfsense_web/app/dashboard/[workspace_id]/playground/components/playground-runner.tsx index effce913b..d80af42f6 100644 --- a/surfsense_web/app/dashboard/[workspace_id]/playground/components/playground-runner.tsx +++ b/surfsense_web/app/dashboard/[workspace_id]/playground/components/playground-runner.tsx @@ -1,14 +1,25 @@ "use client"; -import { AlertTriangle, Coins, Hash, Loader2, Play, Timer, X } from "lucide-react"; -import Link from "next/link"; +import { + Check, + Coins, + Copy, + Hash, + Info, + Loader2, + Play, + Timer, + X, +} from "lucide-react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { toast } from "sonner"; +import { Alert, AlertDescription } from "@/components/ui/alert"; import { Button } from "@/components/ui/button"; import { Spinner } from "@/components/ui/spinner"; import { useRunStream } from "@/hooks/use-run-stream"; import { useScraperCapabilities } from "@/hooks/use-scraper-capabilities"; import { scrapersApiService } from "@/lib/apis/scrapers-api.service"; -import { AbortedError, AppError } from "@/lib/error"; +import { AppError } from "@/lib/error"; import { findVerb } from "@/lib/playground/catalog"; import { formatCost, formatDuration, formatPricing } from "@/lib/playground/format"; import { buildPayload, initialFormValues, parseSchemaFields } from "@/lib/playground/json-schema"; @@ -58,36 +69,44 @@ function RunStat({ ); } -function ErrorPanel({ error, workspaceId }: { error: unknown; workspaceId: number }) { - if (error instanceof AbortedError) { - return null; - } +function getRunErrorMessage(error: unknown): string { const status = error instanceof AppError ? error.status : undefined; if (status === 402) { - return ( -
-

Insufficient credits

-

You don't have enough credits to run this API.

- -
- ); + return "Insufficient credits. Add credits to run this API."; } - const message = - status === 422 - ? "Invalid input. Check the fields above and try again." - : error instanceof Error && error.message - ? error.message - : "Something went wrong running this API."; + if (status === 422) { + return "Invalid input. Check the fields above and try again."; + } + + return error instanceof Error && error.message + ? error.message + : "Something went wrong running this API."; +} + +function EndpointCopyButton({ endpoint }: { endpoint: string }) { + const [copied, setCopied] = useState(false); + + const handleCopy = () => { + navigator.clipboard.writeText(endpoint).then(() => { + setCopied(true); + setTimeout(() => setCopied(false), 1500); + }); + }; return ( -
- - {message} -
+ ); } @@ -110,6 +129,8 @@ export function PlaygroundRunner({ workspaceId, platform, verb }: PlaygroundRunn const [values, setValues] = useState>({}); const run = useRunStream(workspaceId); const isRunning = run.status === "running"; + const previousStatusRef = useRef(run.status); + const notifiedRunRef = useRef(null); // Seed form defaults once the schema is available. useEffect(() => { @@ -158,6 +179,29 @@ export function PlaygroundRunner({ workspaceId, platform, verb }: PlaygroundRunn () => (run.detail ? parseJsonlOutput(run.detail.output_text) : null), [run.detail] ); + const endpoint = `POST /workspaces/${workspaceId}/scrapers/${platform}/${verb}`; + + useEffect(() => { + const previousStatus = previousStatusRef.current; + previousStatusRef.current = run.status; + + if (previousStatus !== "running") return; + + if (run.status === "success") { + const key = `${run.runId ?? "run"}:success`; + if (notifiedRunRef.current === key) return; + notifiedRunRef.current = key; + toast.success("API run completed."); + return; + } + + if (run.status === "error") { + const key = `${run.runId ?? "run"}:error`; + if (notifiedRunRef.current === key) return; + notifiedRunRef.current = key; + toast.error(getRunErrorMessage(run.error)); + } + }, [run.status, run.runId, run.error]); if (isLoading) { return ( @@ -186,26 +230,26 @@ export function PlaygroundRunner({ workspaceId, platform, verb }: PlaygroundRunn return (
-
+
+ {capability.description && ( + + + +

{capability.description}

+
+
+ )} +
-
-

- {catalogVerb.label} ยท {platform} -

- {capability.description && ( -

{capability.description}

- )} - - POST /workspaces/{workspaceId}/scrapers/{platform}/{verb} - -
+
+ +
{formatPricing(capability.pricing)}
- - {run.status === "error" && }
diff --git a/surfsense_web/app/dashboard/[workspace_id]/playground/layout-shell.tsx b/surfsense_web/app/dashboard/[workspace_id]/playground/layout-shell.tsx index e03667944..e17a9a584 100644 --- a/surfsense_web/app/dashboard/[workspace_id]/playground/layout-shell.tsx +++ b/surfsense_web/app/dashboard/[workspace_id]/playground/layout-shell.tsx @@ -30,7 +30,7 @@ export function PlaygroundLayoutShell({ workspaceId, children }: PlaygroundLayou }, { value: "runs", - label: "Runs", + label: "API Runs", href: `${base}/runs`, icon: , },