fix: hide AI activity panel when monitor unreachable; show access message on form 4xx
This commit is contained in:
parent
63ac64fffe
commit
9b1c3fbc32
@ -318,18 +318,22 @@ export default function DetailViewPanel({ viewId, instanceId, onTitle }: Props)
|
||||
</Block>
|
||||
)}
|
||||
|
||||
<Block label="AI Activity" icon={Bot}>
|
||||
{instance.loading ? (
|
||||
<Skeleton className="h-16 w-full rounded-lg" />
|
||||
) : (
|
||||
<AiActivity
|
||||
decisions={instance.decisions}
|
||||
escalations={instance.escalations}
|
||||
traces={instance.traces}
|
||||
unavailable={instance.aiUnavailable}
|
||||
/>
|
||||
)}
|
||||
</Block>
|
||||
{/* Hidden entirely when the monitoring service can't be read —
|
||||
app-user tokens have no scope on /monitor/*, so for them this
|
||||
panel can never load. */}
|
||||
{!instance.aiUnavailable && (
|
||||
<Block label="AI Activity" icon={Bot}>
|
||||
{instance.loading ? (
|
||||
<Skeleton className="h-16 w-full rounded-lg" />
|
||||
) : (
|
||||
<AiActivity
|
||||
decisions={instance.decisions}
|
||||
escalations={instance.escalations}
|
||||
traces={instance.traces}
|
||||
/>
|
||||
)}
|
||||
</Block>
|
||||
)}
|
||||
|
||||
<Block label="History" icon={History}>
|
||||
{instance.loading ? (
|
||||
|
||||
@ -24,6 +24,7 @@ import {
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import {
|
||||
ApiError,
|
||||
getFormScreen,
|
||||
performActivity,
|
||||
startWorkflow,
|
||||
@ -68,11 +69,18 @@ export default function FormModal({ activityId, instanceId, title, onClose, onSu
|
||||
const [succeeded, setSucceeded] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const { data, isPending } = useQuery({
|
||||
const { data, isPending, isError, error: loadError } = useQuery({
|
||||
queryKey: ["form-screen", activityId, instanceId],
|
||||
queryFn: () => getFormScreen(activityId, instanceId) as Promise<any>,
|
||||
// A 4xx (no permission / wrong state) won't change on retry.
|
||||
retry: (count, err) => (err instanceof ApiError && err.status < 500 ? false : count < 1),
|
||||
});
|
||||
|
||||
const loadErrorMessage =
|
||||
loadError instanceof ApiError && loadError.status === 403
|
||||
? "You don't have access to this form."
|
||||
: "This form couldn't be loaded. Please try again.";
|
||||
|
||||
const fields: FormField[] = data?.fields ?? data?.form_json?.fields ?? [];
|
||||
const gridConfig: GridItem[] = data?.grid_config ?? [];
|
||||
const formTitle = data?.activity_name || title || "Form";
|
||||
@ -234,6 +242,14 @@ export default function FormModal({ activityId, instanceId, title, onClose, onSu
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : isError ? (
|
||||
<div
|
||||
role="alert"
|
||||
className="flex items-start gap-2 rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2.5 text-base text-destructive"
|
||||
>
|
||||
<AlertCircle className="mt-0.5 size-4 shrink-0" aria-hidden />
|
||||
<span>{loadErrorMessage}</span>
|
||||
</div>
|
||||
) : (
|
||||
<form id="activity-form" onSubmit={handleSubmit} className="space-y-4">
|
||||
{rows.map((row, ri) => (
|
||||
@ -300,9 +316,17 @@ export default function FormModal({ activityId, instanceId, title, onClose, onSu
|
||||
)}
|
||||
</div>
|
||||
|
||||
{isError && (
|
||||
<DialogFooter className="mx-0 mb-0 border-t border-border px-5 py-3">
|
||||
<Button type="button" variant="ghost" onClick={onClose}>
|
||||
Close
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
)}
|
||||
|
||||
{/* mx-0/mb-0 cancel DialogFooter's default negative margins, which
|
||||
assume the content keeps its own padding — this dialog is p-0. */}
|
||||
{!isPending && (
|
||||
{!isPending && !isError && (
|
||||
<DialogFooter className="mx-0 mb-0 items-center border-t border-border px-5 py-3 sm:justify-between">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
<span className="text-destructive">*</span> Required
|
||||
|
||||
Loading…
Reference in New Issue
Block a user