diff --git a/src/components/DetailViewPanel.tsx b/src/components/DetailViewPanel.tsx
index 30b6153..b2f5584 100644
--- a/src/components/DetailViewPanel.tsx
+++ b/src/components/DetailViewPanel.tsx
@@ -318,18 +318,22 @@ export default function DetailViewPanel({ viewId, instanceId, onTitle }: Props)
)}
-
- {instance.loading ? (
-
- ) : (
-
- )}
-
+ {/* 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 && (
+
+ {instance.loading ? (
+
+ ) : (
+
+ )}
+
+ )}
{instance.loading ? (
diff --git a/src/components/FormModal.tsx b/src/components/FormModal.tsx
index fbc2647..ef5a091 100644
--- a/src/components/FormModal.tsx
+++ b/src/components/FormModal.tsx
@@ -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(null);
- const { data, isPending } = useQuery({
+ const { data, isPending, isError, error: loadError } = useQuery({
queryKey: ["form-screen", activityId, instanceId],
queryFn: () => getFormScreen(activityId, instanceId) as Promise,
+ // 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
))}
+ ) : isError ? (
+
) : (