mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-26 23:51:14 +02:00
feat: surface playground run errors inline and persist toasts
This commit is contained in:
parent
55676cfe94
commit
4414932346
2 changed files with 32 additions and 3 deletions
|
|
@ -209,11 +209,21 @@ export function PlaygroundRunner({ workspaceId, platform, verb }: PlaygroundRunn
|
|||
}
|
||||
|
||||
if (run.status === "error") {
|
||||
setFieldErrors(fieldErrorsFromError(run.error));
|
||||
const nextFieldErrors = fieldErrorsFromError(run.error);
|
||||
setFieldErrors(nextFieldErrors);
|
||||
const key = `${run.runId ?? "run"}:error`;
|
||||
if (notifiedRunRef.current === key) return;
|
||||
notifiedRunRef.current = key;
|
||||
toast.error(getRunErrorMessage(run.error));
|
||||
// Field-level failures are shown inline (the form reveals + focuses the
|
||||
// first one), so only toast global failures that have no field to anchor to.
|
||||
// Keep run errors until dismissed — they're actionable and shouldn't
|
||||
// vanish before the user reads them.
|
||||
if (Object.keys(nextFieldErrors).length === 0) {
|
||||
toast.error(getRunErrorMessage(run.error), {
|
||||
duration: Number.POSITIVE_INFINITY,
|
||||
closeButton: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [run.status, run.runId, run.error]);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import { ChevronDown } from "lucide-react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
|
|
@ -205,6 +205,25 @@ export function SchemaForm({
|
|||
return { primary: primaryFields, advanced: advancedFields };
|
||||
}, [fields]);
|
||||
|
||||
// First invalid field in display order; drives reveal + focus below.
|
||||
const firstErrorName = useMemo(
|
||||
() => (fieldErrors ? fields.find((f) => fieldErrors[f.name])?.name : undefined),
|
||||
[fields, fieldErrors]
|
||||
);
|
||||
|
||||
// Reveal the section holding an invalid field, then move focus to it, so an
|
||||
// error is never left hidden inside the collapsed "Advanced" group.
|
||||
useEffect(() => {
|
||||
if (!firstErrorName) return;
|
||||
if (advanced.some((f) => f.name === firstErrorName)) setShowAdvanced(true);
|
||||
const raf = requestAnimationFrame(() => {
|
||||
const el = document.getElementById(`field-${firstErrorName}`);
|
||||
el?.focus();
|
||||
el?.scrollIntoView({ block: "center", behavior: "smooth" });
|
||||
});
|
||||
return () => cancelAnimationFrame(raf);
|
||||
}, [firstErrorName, advanced]);
|
||||
|
||||
return (
|
||||
<div className="space-y-5">
|
||||
{primary.map((field) => (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue