-
+
{botCardVO.usePipelineName}
diff --git a/web/src/app/home/components/BoxUnavailableNotice.tsx b/web/src/app/home/components/BoxUnavailableNotice.tsx
new file mode 100644
index 00000000..5fe54a80
--- /dev/null
+++ b/web/src/app/home/components/BoxUnavailableNotice.tsx
@@ -0,0 +1,53 @@
+import { useTranslation } from 'react-i18next';
+import { Info, ShieldAlert } from 'lucide-react';
+
+import { Alert, AlertDescription } from '@/components/ui/alert';
+
+/**
+ * Banner shown when a feature depends on the Box sandbox runtime but it is
+ * currently disabled in config or otherwise unavailable. Pass the ``hint``
+ * key returned by ``useBoxStatus`` (``'boxDisabled' | 'boxUnavailable'``).
+ *
+ * Renders nothing when there is no hint — safe to drop at the top of any
+ * page that may or may not need to surface the notice.
+ */
+export interface BoxUnavailableNoticeProps {
+ hint: 'boxDisabled' | 'boxUnavailable' | null;
+ /** Specific failure reason from the backend (``connector_error``). Shown
+ * on a dedicated line so the user sees WHY (e.g. ``Configured sandbox
+ * backend "nsjail" is unavailable``) instead of just the generic
+ * "unavailable" wording. Ignored when ``hint === 'boxDisabled'``
+ * because the disabled-by-config message already carries the reason. */
+ reason?: string | null;
+ className?: string;
+}
+
+export function BoxUnavailableNotice({
+ hint,
+ reason,
+ className,
+}: BoxUnavailableNoticeProps) {
+ const { t } = useTranslation();
+ if (!hint) return null;
+
+ const variant = hint === 'boxDisabled' ? 'default' : 'destructive';
+ const Icon = hint === 'boxDisabled' ? Info : ShieldAlert;
+ const showReason = hint === 'boxUnavailable' && reason;
+
+ return (
+
+
+
+
{t(`monitoring.${hint}`)}
+ {showReason && (
+
{reason}
+ )}
+
+ {t('monitoring.boxRequiredHint')}
+
+
+
+ );
+}
+
+export default BoxUnavailableNotice;
diff --git a/web/src/app/home/components/account-settings-dialog/AccountSettingsDialog.tsx b/web/src/app/home/components/account-settings-dialog/AccountSettingsDialog.tsx
index 87b438eb..b658c9fa 100644
--- a/web/src/app/home/components/account-settings-dialog/AccountSettingsDialog.tsx
+++ b/web/src/app/home/components/account-settings-dialog/AccountSettingsDialog.tsx
@@ -20,7 +20,7 @@ import {
} from '@/components/ui/item';
import { httpClient } from '@/app/infra/http/HttpClient';
import { systemInfo } from '@/app/infra/http';
-import { Loader2, ExternalLink, KeyRound } from 'lucide-react';
+import { Loader2, ExternalLink, KeyRound, Layers } from 'lucide-react';
import PasswordChangeDialog from '../password-change-dialog/PasswordChangeDialog';
interface AccountSettingsDialogProps {
@@ -136,34 +136,7 @@ export default function AccountSettingsDialog({
{/* Space Account Item */}
-
+ {t('account.spaceStatus')}
diff --git a/web/src/app/home/components/dynamic-form/DynamicFormComponent.tsx b/web/src/app/home/components/dynamic-form/DynamicFormComponent.tsx
index ffea18d6..078db6f7 100644
--- a/web/src/app/home/components/dynamic-form/DynamicFormComponent.tsx
+++ b/web/src/app/home/components/dynamic-form/DynamicFormComponent.tsx
@@ -20,8 +20,14 @@ import { useTranslation } from 'react-i18next';
import { cn } from '@/lib/utils';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
-import { Copy, Check, Globe, QrCode } from 'lucide-react';
+import { Copy, Check, Globe, Info, QrCode } from 'lucide-react';
import { copyToClipboard } from '@/app/utils/clipboard';
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from '@/components/ui/tooltip';
import { systemInfo } from '@/app/infra/http';
/**
@@ -123,13 +129,13 @@ function WebhookUrlField({
};
return (
-
- {label}
-