@open-mercato/ui 0.6.7-develop.6862.1.c11a64ce0a → 0.6.7-develop.6869.1.d1ce9999ea
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai/AiChat.js +5 -5
- package/dist/ai/AiChat.js.map +2 -2
- package/dist/ai/LoopDisabledBanner.js +1 -1
- package/dist/ai/LoopDisabledBanner.js.map +2 -2
- package/dist/ai/parts/ConfirmationCard.js +4 -4
- package/dist/ai/parts/ConfirmationCard.js.map +2 -2
- package/dist/ai/parts/FieldDiffCard.js +2 -2
- package/dist/ai/parts/FieldDiffCard.js.map +2 -2
- package/dist/ai/parts/MutationResultCard.js +3 -3
- package/dist/ai/parts/MutationResultCard.js.map +2 -2
- package/dist/ai/ui-parts/pending-phase3-placeholder.js +1 -1
- package/dist/ai/ui-parts/pending-phase3-placeholder.js.map +2 -2
- package/dist/backend/PerspectiveSidebar.js +1 -1
- package/dist/backend/PerspectiveSidebar.js.map +2 -2
- package/dist/backend/custom-fields/FieldDefinitionsManager.js +1 -1
- package/dist/backend/custom-fields/FieldDefinitionsManager.js.map +2 -2
- package/dist/backend/dashboard/DashboardScreen.js +3 -3
- package/dist/backend/dashboard/DashboardScreen.js.map +2 -2
- package/dist/backend/filters/AdvancedFilterPanel.js +1 -1
- package/dist/backend/filters/AdvancedFilterPanel.js.map +2 -2
- package/dist/backend/inputs/ComboboxInput.js.map +1 -1
- package/dist/backend/messages/EmailThreadsPanel.js +1 -1
- package/dist/backend/messages/EmailThreadsPanel.js.map +2 -2
- package/dist/backend/version-history/VersionHistoryPanel.js +1 -1
- package/dist/backend/version-history/VersionHistoryPanel.js.map +2 -2
- package/dist/primitives/ErrorNotice.js +1 -1
- package/dist/primitives/ErrorNotice.js.map +2 -2
- package/dist/primitives/Notice.js +1 -1
- package/dist/primitives/Notice.js.map +1 -1
- package/package.json +3 -3
- package/src/ai/AiChat.tsx +6 -6
- package/src/ai/LoopDisabledBanner.tsx +1 -1
- package/src/ai/parts/ConfirmationCard.tsx +4 -4
- package/src/ai/parts/FieldDiffCard.tsx +2 -2
- package/src/ai/parts/MutationResultCard.tsx +3 -3
- package/src/ai/ui-parts/pending-phase3-placeholder.tsx +2 -2
- package/src/backend/PerspectiveSidebar.tsx +1 -1
- package/src/backend/custom-fields/FieldDefinitionsManager.tsx +1 -1
- package/src/backend/dashboard/DashboardScreen.tsx +3 -3
- package/src/backend/filters/AdvancedFilterPanel.tsx +1 -1
- package/src/backend/inputs/ComboboxInput.tsx +1 -1
- package/src/backend/messages/EmailThreadsPanel.tsx +1 -1
- package/src/backend/version-history/VersionHistoryPanel.tsx +1 -1
- package/src/primitives/ErrorNotice.tsx +1 -1
- package/src/primitives/Notice.tsx +5 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/ai/LoopDisabledBanner.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { ShieldOff } from 'lucide-react'\nimport { Alert, AlertDescription, AlertTitle } from '../primitives/alert'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\n/**\n * Banner displayed when the tenant kill switch (`loop_disabled = true`) is\n * active for the current agent. Rendered on the playground page and on\n * `<AiChat>` headers so operators are not blindsided (R11 mitigation, Phase 3\n * of spec `2026-04-28-ai-agents-agentic-loop-controls`).\n *\n * The agent still completes turns but as single-step runs. The banner is\n * informational only \u2014 operators must visit the Loop policy section of the\n * AI assistant settings to re-enable the loop.\n */\nexport function LoopDisabledBanner({ agentId }: { agentId?: string }) {\n const t = useT()\n return (\n <Alert\n
|
|
5
|
-
"mappings": ";AAoBI,SAEQ,KAFR;AAjBJ,SAAS,iBAAiB;AAC1B,SAAS,OAAO,kBAAkB,kBAAkB;AACpD,SAAS,YAAY;AAYd,SAAS,mBAAmB,EAAE,QAAQ,GAAyB;AACpE,QAAM,IAAI,KAAK;AACf,SACE;AAAA,IAAC;AAAA;AAAA,MACC,
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { ShieldOff } from 'lucide-react'\nimport { Alert, AlertDescription, AlertTitle } from '../primitives/alert'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\n/**\n * Banner displayed when the tenant kill switch (`loop_disabled = true`) is\n * active for the current agent. Rendered on the playground page and on\n * `<AiChat>` headers so operators are not blindsided (R11 mitigation, Phase 3\n * of spec `2026-04-28-ai-agents-agentic-loop-controls`).\n *\n * The agent still completes turns but as single-step runs. The banner is\n * informational only \u2014 operators must visit the Loop policy section of the\n * AI assistant settings to re-enable the loop.\n */\nexport function LoopDisabledBanner({ agentId }: { agentId?: string }) {\n const t = useT()\n return (\n <Alert\n status=\"warning\"\n icon={<ShieldOff aria-hidden=\"true\" />}\n data-ai-loop-disabled-banner\n data-ai-loop-disabled-agent-id={agentId}\n >\n <AlertTitle>\n {t('ai_assistant.loop.disabledBanner.title', 'Agent loop disabled by tenant policy')}\n </AlertTitle>\n <AlertDescription>\n {t(\n 'ai_assistant.loop.disabledBanner.description',\n 'The agentic loop has been disabled for this agent by a tenant administrator. Each turn runs as a single model call. To re-enable the loop, update the Loop policy in AI assistant settings.',\n )}\n </AlertDescription>\n </Alert>\n )\n}\n"],
|
|
5
|
+
"mappings": ";AAoBI,SAEQ,KAFR;AAjBJ,SAAS,iBAAiB;AAC1B,SAAS,OAAO,kBAAkB,kBAAkB;AACpD,SAAS,YAAY;AAYd,SAAS,mBAAmB,EAAE,QAAQ,GAAyB;AACpE,QAAM,IAAI,KAAK;AACf,SACE;AAAA,IAAC;AAAA;AAAA,MACC,QAAO;AAAA,MACP,MAAM,oBAAC,aAAU,eAAY,QAAO;AAAA,MACpC,gCAA4B;AAAA,MAC5B,kCAAgC;AAAA,MAEhC;AAAA,4BAAC,cACE,YAAE,0CAA0C,sCAAsC,GACrF;AAAA,QACA,oBAAC,oBACE;AAAA,UACC;AAAA,UACA;AAAA,QACF,GACF;AAAA;AAAA;AAAA,EACF;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -125,7 +125,7 @@ function ConfirmationErrorAlert({
|
|
|
125
125
|
return /* @__PURE__ */ jsxs(
|
|
126
126
|
Alert,
|
|
127
127
|
{
|
|
128
|
-
|
|
128
|
+
status: "warning",
|
|
129
129
|
className: "mt-3",
|
|
130
130
|
"data-ai-confirmation-error": "stale_version",
|
|
131
131
|
children: [
|
|
@@ -155,7 +155,7 @@ function ConfirmationErrorAlert({
|
|
|
155
155
|
return /* @__PURE__ */ jsxs(
|
|
156
156
|
Alert,
|
|
157
157
|
{
|
|
158
|
-
|
|
158
|
+
status: "warning",
|
|
159
159
|
className: "mt-3",
|
|
160
160
|
"data-ai-confirmation-error": "schema_drift",
|
|
161
161
|
children: [
|
|
@@ -175,7 +175,7 @@ function ConfirmationErrorAlert({
|
|
|
175
175
|
return /* @__PURE__ */ jsxs(
|
|
176
176
|
Alert,
|
|
177
177
|
{
|
|
178
|
-
|
|
178
|
+
status: "warning",
|
|
179
179
|
className: "mt-3",
|
|
180
180
|
"data-ai-confirmation-error": "invalid_status",
|
|
181
181
|
children: [
|
|
@@ -191,7 +191,7 @@ function ConfirmationErrorAlert({
|
|
|
191
191
|
}
|
|
192
192
|
);
|
|
193
193
|
}
|
|
194
|
-
return /* @__PURE__ */ jsxs(Alert, {
|
|
194
|
+
return /* @__PURE__ */ jsxs(Alert, { status: "error", className: "mt-3", "data-ai-confirmation-error": code, children: [
|
|
195
195
|
/* @__PURE__ */ jsx(AlertTitle, { children: t("ai_assistant.chat.mutation_cards.confirmation.errorTitle", "Confirm failed") }),
|
|
196
196
|
/* @__PURE__ */ jsxs(AlertDescription, { children: [
|
|
197
197
|
/* @__PURE__ */ jsx("span", { className: "mr-2 font-mono text-xs", children: code }),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/ai/parts/ConfirmationCard.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Loader2 } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Alert, AlertDescription, AlertTitle } from '../../primitives/alert'\nimport { Button } from '../../primitives/button'\nimport { Spinner } from '../../primitives/spinner'\nimport { useAiShortcuts } from '../useAiShortcuts'\nimport type { AiUiPartProps } from '../ui-part-registry'\nimport { cancelPendingAction } from './pending-action-api'\nimport { useAiPendingActionPolling } from './useAiPendingActionPolling'\nimport { MutationResultCard } from './MutationResultCard'\nimport type { AiPendingActionCardAction } from './types'\n\n/**\n * Confirmation / in-flight card rendered after the user clicks `Confirm` on\n * the preview card. Shows a spinner + the side-effects summary while the\n * server runs the re-check contract and executes the wrapped tool.\n *\n * The user can race the confirm by clicking Cancel \u2014 but only while the\n * server has not yet flipped the row to `executing`. The polling hook\n * drives the disable logic.\n *\n * Surfaces structured error envelopes from the confirm route: 412\n * `stale_version` (records changed since preview) and 412 `schema_drift`\n * (tool input schema changed) render targeted alerts with the specific\n * recovery copy. Keyboard: `Escape` triggers Cancel; `Cmd/Ctrl+Enter` is\n * intentionally inert (the user already confirmed).\n *\n * Terminal states flip this card into a {@link MutationResultCard} render\n * so the chat transcript does not need two separate cards for confirmed\n * vs pending.\n */\nexport interface ConfirmationCardPayload {\n sideEffectsSummary?: string | null\n pendingAction?: AiPendingActionCardAction\n confirmError?: {\n status: number\n code?: string\n message: string\n extra?: Record<string, unknown>\n }\n}\n\nexport interface ConfirmationCardProps extends AiUiPartProps {\n /** Optional override for tests \u2014 bypasses the polling fetch. */\n initialAction?: AiPendingActionCardAction\n /** Endpoint override (tests). */\n endpoint?: string\n /** Optional cancel handler override (tests). */\n onCancel?: () => Promise<void> | void\n}\n\nexport function ConfirmationCard(props: ConfirmationCardProps) {\n const t = useT()\n const pendingActionId = props.pendingActionId ?? ''\n const payload = (props.payload as ConfirmationCardPayload | undefined) ?? {}\n const injected = props.initialAction ?? payload.pendingAction ?? null\n\n const { action, status, refresh } = useAiPendingActionPolling({\n pendingActionId,\n endpoint: props.endpoint,\n disabled: !pendingActionId,\n })\n\n const effectiveAction = action ?? injected ?? null\n const effectiveStatus = effectiveAction?.status ?? status\n\n const [isCancelling, setIsCancelling] = React.useState(false)\n const [localError, setLocalError] = React.useState<{\n code?: string\n message: string\n extra?: Record<string, unknown>\n } | null>(\n payload.confirmError\n ? { code: payload.confirmError.code, message: payload.confirmError.message, extra: payload.confirmError.extra }\n : null,\n )\n\n const canCancel =\n !isCancelling &&\n (effectiveStatus === 'pending' || effectiveStatus === 'confirmed' || effectiveStatus == null)\n\n const handleCancel = React.useCallback(async () => {\n if (!canCancel) return\n if (props.onCancel) {\n await props.onCancel()\n return\n }\n if (!pendingActionId) return\n setIsCancelling(true)\n try {\n const result = await cancelPendingAction(pendingActionId, {\n endpoint: props.endpoint,\n })\n if (!result.ok) {\n setLocalError({\n code: result.error.code,\n message: result.error.message,\n extra: result.error.extra,\n })\n }\n await refresh()\n } finally {\n setIsCancelling(false)\n }\n }, [canCancel, pendingActionId, props, refresh])\n\n const { handleKeyDown } = useAiShortcuts({\n onCancel: () => {\n void handleCancel()\n },\n enabled: canCancel,\n })\n\n // Terminal states \u2014 hand off to the result card renderer. Also short-\n // circuit when the dispatcher has already populated an\n // `executionResult.error`: the row may not have transitioned out of\n // `executing` in the latest polling snapshot, but the handler error is\n // authoritative, and leaving the spinner up while a known error is\n // available is exactly the \"stalled at processing\" symptom the user\n // reported. Surface the error card immediately.\n const executionError = effectiveAction?.executionResult?.error\n if (\n effectiveStatus === 'confirmed' ||\n effectiveStatus === 'failed' ||\n effectiveStatus === 'cancelled' ||\n effectiveStatus === 'expired' ||\n executionError\n ) {\n return (\n <MutationResultCard\n componentId=\"mutation-result-card\"\n pendingActionId={pendingActionId}\n initialAction={effectiveAction ?? undefined}\n endpoint={props.endpoint}\n />\n )\n }\n\n const summary =\n effectiveAction?.sideEffectsSummary ??\n payload.sideEffectsSummary ??\n t(\n 'ai_assistant.chat.mutation_cards.confirmation.defaultSummary',\n 'Applying the requested changes...',\n )\n\n return (\n <section\n className=\"rounded-md border border-border bg-muted/30 p-4 text-sm outline-none\"\n tabIndex={0}\n onKeyDown={handleKeyDown}\n data-ai-confirmation-card\n data-ai-confirmation-status={effectiveStatus ?? 'pending'}\n aria-busy\n >\n <div className=\"flex items-start gap-3\">\n <Spinner size=\"sm\" className=\"mt-0.5\" />\n <div className=\"flex-1\">\n <h4 className=\"text-sm font-semibold\">\n {t(\n 'ai_assistant.chat.mutation_cards.confirmation.title',\n 'Applying action...',\n )}\n </h4>\n <p className=\"mt-1 text-sm text-muted-foreground\">{summary}</p>\n </div>\n </div>\n\n {localError ? (\n <ConfirmationErrorAlert error={localError} />\n ) : null}\n\n <div className=\"mt-3 flex items-center justify-end gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n void handleCancel()\n }}\n disabled={!canCancel}\n data-ai-confirmation-cancel\n >\n {isCancelling ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden />\n ) : null}\n <span>\n {t('ai_assistant.chat.mutation_cards.confirmation.cancel', 'Cancel')}\n </span>\n </Button>\n </div>\n </section>\n )\n}\n\nfunction ConfirmationErrorAlert({\n error,\n}: {\n error: { code?: string; message: string; extra?: Record<string, unknown> }\n}) {\n const t = useT()\n const code = error.code ?? 'unknown'\n\n if (code === 'stale_version') {\n const failedRecords = Array.isArray(error.extra?.failedRecords)\n ? (error.extra?.failedRecords as Array<{ recordId?: string }>)\n : []\n return (\n <Alert\n variant=\"warning\"\n className=\"mt-3\"\n data-ai-confirmation-error=\"stale_version\"\n >\n <AlertTitle>\n {t(\n 'ai_assistant.chat.mutation_cards.confirmation.staleVersionTitle',\n 'Re-propose required',\n )}\n </AlertTitle>\n <AlertDescription>\n <p>\n {t(\n 'ai_assistant.chat.mutation_cards.confirmation.staleVersionBody',\n 'One or more records changed since this preview was generated. Ask the assistant to re-propose the change.',\n )}\n </p>\n {failedRecords.length > 0 ? (\n <ul className=\"mt-2 list-disc pl-5 text-xs\">\n {failedRecords.map((record, idx) => (\n <li\n key={`${record.recordId ?? idx}`}\n data-ai-confirmation-stale-record={record.recordId ?? ''}\n >\n <span className=\"font-mono\">{record.recordId ?? '\u2014'}</span>\n </li>\n ))}\n </ul>\n ) : null}\n </AlertDescription>\n </Alert>\n )\n }\n\n if (code === 'schema_drift') {\n return (\n <Alert\n variant=\"warning\"\n className=\"mt-3\"\n data-ai-confirmation-error=\"schema_drift\"\n >\n <AlertTitle>\n {t(\n 'ai_assistant.chat.mutation_cards.confirmation.schemaDriftTitle',\n 'Schema changed',\n )}\n </AlertTitle>\n <AlertDescription>\n {t(\n 'ai_assistant.chat.mutation_cards.confirmation.schemaDriftBody',\n 'The tool signature changed since this preview was generated. Ask the assistant to re-propose the change.',\n )}\n </AlertDescription>\n </Alert>\n )\n }\n\n if (code === 'invalid_status') {\n return (\n <Alert\n variant=\"warning\"\n className=\"mt-3\"\n data-ai-confirmation-error=\"invalid_status\"\n >\n <AlertTitle>\n {t(\n 'ai_assistant.chat.mutation_cards.confirmation.invalidStatusTitle',\n 'Action already resolved',\n )}\n </AlertTitle>\n <AlertDescription>\n {t(\n 'ai_assistant.chat.mutation_cards.confirmation.invalidStatusBody',\n 'This action has already been confirmed, cancelled, or executed.',\n )}\n </AlertDescription>\n </Alert>\n )\n }\n\n return (\n <Alert variant=\"destructive\" className=\"mt-3\" data-ai-confirmation-error={code}>\n <AlertTitle>\n {t('ai_assistant.chat.mutation_cards.confirmation.errorTitle', 'Confirm failed')}\n </AlertTitle>\n <AlertDescription>\n <span className=\"mr-2 font-mono text-xs\">{code}</span>\n <span>{error.message}</span>\n </AlertDescription>\n </Alert>\n )\n}\n\nexport default ConfirmationCard\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Loader2 } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Alert, AlertDescription, AlertTitle } from '../../primitives/alert'\nimport { Button } from '../../primitives/button'\nimport { Spinner } from '../../primitives/spinner'\nimport { useAiShortcuts } from '../useAiShortcuts'\nimport type { AiUiPartProps } from '../ui-part-registry'\nimport { cancelPendingAction } from './pending-action-api'\nimport { useAiPendingActionPolling } from './useAiPendingActionPolling'\nimport { MutationResultCard } from './MutationResultCard'\nimport type { AiPendingActionCardAction } from './types'\n\n/**\n * Confirmation / in-flight card rendered after the user clicks `Confirm` on\n * the preview card. Shows a spinner + the side-effects summary while the\n * server runs the re-check contract and executes the wrapped tool.\n *\n * The user can race the confirm by clicking Cancel \u2014 but only while the\n * server has not yet flipped the row to `executing`. The polling hook\n * drives the disable logic.\n *\n * Surfaces structured error envelopes from the confirm route: 412\n * `stale_version` (records changed since preview) and 412 `schema_drift`\n * (tool input schema changed) render targeted alerts with the specific\n * recovery copy. Keyboard: `Escape` triggers Cancel; `Cmd/Ctrl+Enter` is\n * intentionally inert (the user already confirmed).\n *\n * Terminal states flip this card into a {@link MutationResultCard} render\n * so the chat transcript does not need two separate cards for confirmed\n * vs pending.\n */\nexport interface ConfirmationCardPayload {\n sideEffectsSummary?: string | null\n pendingAction?: AiPendingActionCardAction\n confirmError?: {\n status: number\n code?: string\n message: string\n extra?: Record<string, unknown>\n }\n}\n\nexport interface ConfirmationCardProps extends AiUiPartProps {\n /** Optional override for tests \u2014 bypasses the polling fetch. */\n initialAction?: AiPendingActionCardAction\n /** Endpoint override (tests). */\n endpoint?: string\n /** Optional cancel handler override (tests). */\n onCancel?: () => Promise<void> | void\n}\n\nexport function ConfirmationCard(props: ConfirmationCardProps) {\n const t = useT()\n const pendingActionId = props.pendingActionId ?? ''\n const payload = (props.payload as ConfirmationCardPayload | undefined) ?? {}\n const injected = props.initialAction ?? payload.pendingAction ?? null\n\n const { action, status, refresh } = useAiPendingActionPolling({\n pendingActionId,\n endpoint: props.endpoint,\n disabled: !pendingActionId,\n })\n\n const effectiveAction = action ?? injected ?? null\n const effectiveStatus = effectiveAction?.status ?? status\n\n const [isCancelling, setIsCancelling] = React.useState(false)\n const [localError, setLocalError] = React.useState<{\n code?: string\n message: string\n extra?: Record<string, unknown>\n } | null>(\n payload.confirmError\n ? { code: payload.confirmError.code, message: payload.confirmError.message, extra: payload.confirmError.extra }\n : null,\n )\n\n const canCancel =\n !isCancelling &&\n (effectiveStatus === 'pending' || effectiveStatus === 'confirmed' || effectiveStatus == null)\n\n const handleCancel = React.useCallback(async () => {\n if (!canCancel) return\n if (props.onCancel) {\n await props.onCancel()\n return\n }\n if (!pendingActionId) return\n setIsCancelling(true)\n try {\n const result = await cancelPendingAction(pendingActionId, {\n endpoint: props.endpoint,\n })\n if (!result.ok) {\n setLocalError({\n code: result.error.code,\n message: result.error.message,\n extra: result.error.extra,\n })\n }\n await refresh()\n } finally {\n setIsCancelling(false)\n }\n }, [canCancel, pendingActionId, props, refresh])\n\n const { handleKeyDown } = useAiShortcuts({\n onCancel: () => {\n void handleCancel()\n },\n enabled: canCancel,\n })\n\n // Terminal states \u2014 hand off to the result card renderer. Also short-\n // circuit when the dispatcher has already populated an\n // `executionResult.error`: the row may not have transitioned out of\n // `executing` in the latest polling snapshot, but the handler error is\n // authoritative, and leaving the spinner up while a known error is\n // available is exactly the \"stalled at processing\" symptom the user\n // reported. Surface the error card immediately.\n const executionError = effectiveAction?.executionResult?.error\n if (\n effectiveStatus === 'confirmed' ||\n effectiveStatus === 'failed' ||\n effectiveStatus === 'cancelled' ||\n effectiveStatus === 'expired' ||\n executionError\n ) {\n return (\n <MutationResultCard\n componentId=\"mutation-result-card\"\n pendingActionId={pendingActionId}\n initialAction={effectiveAction ?? undefined}\n endpoint={props.endpoint}\n />\n )\n }\n\n const summary =\n effectiveAction?.sideEffectsSummary ??\n payload.sideEffectsSummary ??\n t(\n 'ai_assistant.chat.mutation_cards.confirmation.defaultSummary',\n 'Applying the requested changes...',\n )\n\n return (\n <section\n className=\"rounded-md border border-border bg-muted/30 p-4 text-sm outline-none\"\n tabIndex={0}\n onKeyDown={handleKeyDown}\n data-ai-confirmation-card\n data-ai-confirmation-status={effectiveStatus ?? 'pending'}\n aria-busy\n >\n <div className=\"flex items-start gap-3\">\n <Spinner size=\"sm\" className=\"mt-0.5\" />\n <div className=\"flex-1\">\n <h4 className=\"text-sm font-semibold\">\n {t(\n 'ai_assistant.chat.mutation_cards.confirmation.title',\n 'Applying action...',\n )}\n </h4>\n <p className=\"mt-1 text-sm text-muted-foreground\">{summary}</p>\n </div>\n </div>\n\n {localError ? (\n <ConfirmationErrorAlert error={localError} />\n ) : null}\n\n <div className=\"mt-3 flex items-center justify-end gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n void handleCancel()\n }}\n disabled={!canCancel}\n data-ai-confirmation-cancel\n >\n {isCancelling ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden />\n ) : null}\n <span>\n {t('ai_assistant.chat.mutation_cards.confirmation.cancel', 'Cancel')}\n </span>\n </Button>\n </div>\n </section>\n )\n}\n\nfunction ConfirmationErrorAlert({\n error,\n}: {\n error: { code?: string; message: string; extra?: Record<string, unknown> }\n}) {\n const t = useT()\n const code = error.code ?? 'unknown'\n\n if (code === 'stale_version') {\n const failedRecords = Array.isArray(error.extra?.failedRecords)\n ? (error.extra?.failedRecords as Array<{ recordId?: string }>)\n : []\n return (\n <Alert\n status=\"warning\"\n className=\"mt-3\"\n data-ai-confirmation-error=\"stale_version\"\n >\n <AlertTitle>\n {t(\n 'ai_assistant.chat.mutation_cards.confirmation.staleVersionTitle',\n 'Re-propose required',\n )}\n </AlertTitle>\n <AlertDescription>\n <p>\n {t(\n 'ai_assistant.chat.mutation_cards.confirmation.staleVersionBody',\n 'One or more records changed since this preview was generated. Ask the assistant to re-propose the change.',\n )}\n </p>\n {failedRecords.length > 0 ? (\n <ul className=\"mt-2 list-disc pl-5 text-xs\">\n {failedRecords.map((record, idx) => (\n <li\n key={`${record.recordId ?? idx}`}\n data-ai-confirmation-stale-record={record.recordId ?? ''}\n >\n <span className=\"font-mono\">{record.recordId ?? '\u2014'}</span>\n </li>\n ))}\n </ul>\n ) : null}\n </AlertDescription>\n </Alert>\n )\n }\n\n if (code === 'schema_drift') {\n return (\n <Alert\n status=\"warning\"\n className=\"mt-3\"\n data-ai-confirmation-error=\"schema_drift\"\n >\n <AlertTitle>\n {t(\n 'ai_assistant.chat.mutation_cards.confirmation.schemaDriftTitle',\n 'Schema changed',\n )}\n </AlertTitle>\n <AlertDescription>\n {t(\n 'ai_assistant.chat.mutation_cards.confirmation.schemaDriftBody',\n 'The tool signature changed since this preview was generated. Ask the assistant to re-propose the change.',\n )}\n </AlertDescription>\n </Alert>\n )\n }\n\n if (code === 'invalid_status') {\n return (\n <Alert\n status=\"warning\"\n className=\"mt-3\"\n data-ai-confirmation-error=\"invalid_status\"\n >\n <AlertTitle>\n {t(\n 'ai_assistant.chat.mutation_cards.confirmation.invalidStatusTitle',\n 'Action already resolved',\n )}\n </AlertTitle>\n <AlertDescription>\n {t(\n 'ai_assistant.chat.mutation_cards.confirmation.invalidStatusBody',\n 'This action has already been confirmed, cancelled, or executed.',\n )}\n </AlertDescription>\n </Alert>\n )\n }\n\n return (\n <Alert status=\"error\" className=\"mt-3\" data-ai-confirmation-error={code}>\n <AlertTitle>\n {t('ai_assistant.chat.mutation_cards.confirmation.errorTitle', 'Confirm failed')}\n </AlertTitle>\n <AlertDescription>\n <span className=\"mr-2 font-mono text-xs\">{code}</span>\n <span>{error.message}</span>\n </AlertDescription>\n </Alert>\n )\n}\n\nexport default ConfirmationCard\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -80,7 +80,7 @@ function FieldDiffCard({ fieldDiff, records }) {
|
|
|
80
80
|
beforeHeader,
|
|
81
81
|
afterHeader
|
|
82
82
|
}
|
|
83
|
-
) : /* @__PURE__ */ jsx(Alert, {
|
|
83
|
+
) : /* @__PURE__ */ jsx(Alert, { status: "information", children: /* @__PURE__ */ jsx(AlertDescription, { children: t(
|
|
84
84
|
"ai_assistant.chat.mutation_cards.diff.empty",
|
|
85
85
|
"No field changes for this record."
|
|
86
86
|
) }) })
|
|
@@ -90,7 +90,7 @@ function FieldDiffCard({ fieldDiff, records }) {
|
|
|
90
90
|
)) });
|
|
91
91
|
}
|
|
92
92
|
if (flat.length === 0) {
|
|
93
|
-
return /* @__PURE__ */ jsx(Alert, {
|
|
93
|
+
return /* @__PURE__ */ jsx(Alert, { status: "information", "data-ai-field-diff-mode": "empty", children: /* @__PURE__ */ jsx(AlertDescription, { children: t(
|
|
94
94
|
"ai_assistant.chat.mutation_cards.diff.empty",
|
|
95
95
|
"No field changes for this record."
|
|
96
96
|
) }) });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/ai/parts/FieldDiffCard.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Alert, AlertDescription } from '../../primitives/alert'\nimport type {\n AiPendingActionCardFieldDiff,\n AiPendingActionCardRecordDiff,\n} from './types'\n\n/**\n * Presentational card rendering a `fieldDiff` list in compact three-column\n * form (field | before | after) with DS-compliant semantic-token colors.\n * Accepts either a flat `fieldDiff[]` (single-record preview) or grouped\n * `records[]` (batch preview) \u2014 when both are supplied, `records` wins,\n * matching the server-side `AiPendingAction` contract (spec \u00A78 rule 2).\n */\nexport interface FieldDiffCardProps {\n fieldDiff?: AiPendingActionCardFieldDiff[] | null\n records?: AiPendingActionCardRecordDiff[] | null\n /** Optional forwarded componentId for the registry renderer. */\n componentId?: string\n}\n\nfunction formatValue(value: unknown): string {\n if (value == null) return ''\n if (typeof value === 'string') return value\n try {\n return JSON.stringify(value)\n } catch {\n return String(value)\n }\n}\n\nfunction DiffRow({ entry }: { entry: AiPendingActionCardFieldDiff }) {\n const before = formatValue(entry.beforeDisplay ?? entry.before)\n const after = formatValue(entry.afterDisplay ?? entry.after)\n const field = entry.fieldLabel ?? entry.field\n return (\n <tr className=\"border-b border-border last:border-b-0\" data-ai-field-diff-row>\n <td className=\"py-1.5 pr-4 text-xs font-mono text-muted-foreground align-top\">\n {field}\n </td>\n <td\n className=\"py-1.5 pr-4 text-sm align-top text-status-warning-text\"\n data-ai-field-diff-before\n >\n <span className=\"line-through break-all\">{before || '\u2014'}</span>\n </td>\n <td\n className=\"py-1.5 text-sm align-top text-status-success-text\"\n data-ai-field-diff-after\n >\n <span className=\"font-medium break-all\">{after || '\u2014'}</span>\n </td>\n </tr>\n )\n}\n\nfunction DiffTable({ rows, fieldHeader, beforeHeader, afterHeader }: {\n rows: AiPendingActionCardFieldDiff[]\n fieldHeader: string\n beforeHeader: string\n afterHeader: string\n}) {\n return (\n <table className=\"w-full\" data-ai-field-diff-table>\n <thead>\n <tr className=\"border-b border-border\">\n <th className=\"py-1 pr-4 text-left text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n {fieldHeader}\n </th>\n <th className=\"py-1 pr-4 text-left text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n {beforeHeader}\n </th>\n <th className=\"py-1 text-left text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n {afterHeader}\n </th>\n </tr>\n </thead>\n <tbody>\n {rows.map((entry, idx) => (\n <DiffRow key={`${entry.field}-${idx}`} entry={entry} />\n ))}\n </tbody>\n </table>\n )\n}\n\nexport function FieldDiffCard({ fieldDiff, records }: FieldDiffCardProps) {\n const t = useT()\n const fieldHeader = t(\n 'ai_assistant.chat.mutation_cards.diff.fieldHeader',\n 'Field',\n )\n const beforeHeader = t(\n 'ai_assistant.chat.mutation_cards.diff.beforeHeader',\n 'Before',\n )\n const afterHeader = t(\n 'ai_assistant.chat.mutation_cards.diff.afterHeader',\n 'After',\n )\n\n const batch = Array.isArray(records) && records.length > 0 ? records : null\n const flat = Array.isArray(fieldDiff) ? fieldDiff : []\n\n if (batch) {\n return (\n <div className=\"flex flex-col gap-3\" data-ai-field-diff-mode=\"batch\">\n {batch.map((record) => (\n <section\n key={record.recordId}\n className=\"rounded-md border border-border bg-background p-3\"\n data-ai-field-diff-record={record.recordId}\n >\n <header className=\"mb-2 flex items-baseline justify-between gap-2\">\n <h4 className=\"text-sm font-semibold\">{record.label}</h4>\n <span className=\"text-xs font-mono text-muted-foreground\">\n {record.entityType}\n </span>\n </header>\n {record.fieldDiff.length > 0 ? (\n <DiffTable\n rows={record.fieldDiff}\n fieldHeader={fieldHeader}\n beforeHeader={beforeHeader}\n afterHeader={afterHeader}\n />\n ) : (\n <Alert
|
|
5
|
-
"mappings": ";AAuCI,SACE,KADF;AApCJ,SAAS,YAAY;AACrB,SAAS,OAAO,wBAAwB;AAoBxC,SAAS,YAAY,OAAwB;AAC3C,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI;AACF,WAAO,KAAK,UAAU,KAAK;AAAA,EAC7B,QAAQ;AACN,WAAO,OAAO,KAAK;AAAA,EACrB;AACF;AAEA,SAAS,QAAQ,EAAE,MAAM,GAA4C;AACnE,QAAM,SAAS,YAAY,MAAM,iBAAiB,MAAM,MAAM;AAC9D,QAAM,QAAQ,YAAY,MAAM,gBAAgB,MAAM,KAAK;AAC3D,QAAM,QAAQ,MAAM,cAAc,MAAM;AACxC,SACE,qBAAC,QAAG,WAAU,0CAAyC,0BAAsB,MAC3E;AAAA,wBAAC,QAAG,WAAU,iEACX,iBACH;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,6BAAyB;AAAA,QAEzB,8BAAC,UAAK,WAAU,0BAA0B,oBAAU,UAAI;AAAA;AAAA,IAC1D;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,4BAAwB;AAAA,QAExB,8BAAC,UAAK,WAAU,yBAAyB,mBAAS,UAAI;AAAA;AAAA,IACxD;AAAA,KACF;AAEJ;AAEA,SAAS,UAAU,EAAE,MAAM,aAAa,cAAc,YAAY,GAK/D;AACD,SACE,qBAAC,WAAM,WAAU,UAAS,4BAAwB,MAChD;AAAA,wBAAC,WACC,+BAAC,QAAG,WAAU,0BACZ;AAAA,0BAAC,QAAG,WAAU,4FACX,uBACH;AAAA,MACA,oBAAC,QAAG,WAAU,4FACX,wBACH;AAAA,MACA,oBAAC,QAAG,WAAU,uFACX,uBACH;AAAA,OACF,GACF;AAAA,IACA,oBAAC,WACE,eAAK,IAAI,CAAC,OAAO,QAChB,oBAAC,WAAsC,SAAzB,GAAG,MAAM,KAAK,IAAI,GAAG,EAAkB,CACtD,GACH;AAAA,KACF;AAEJ;AAEO,SAAS,cAAc,EAAE,WAAW,QAAQ,GAAuB;AACxE,QAAM,IAAI,KAAK;AACf,QAAM,cAAc;AAAA,IAClB;AAAA,IACA;AAAA,EACF;AACA,QAAM,eAAe;AAAA,IACnB;AAAA,IACA;AAAA,EACF;AACA,QAAM,cAAc;AAAA,IAClB;AAAA,IACA;AAAA,EACF;AAEA,QAAM,QAAQ,MAAM,QAAQ,OAAO,KAAK,QAAQ,SAAS,IAAI,UAAU;AACvE,QAAM,OAAO,MAAM,QAAQ,SAAS,IAAI,YAAY,CAAC;AAErD,MAAI,OAAO;AACT,WACE,oBAAC,SAAI,WAAU,uBAAsB,2BAAwB,SAC1D,gBAAM,IAAI,CAAC,WACV;AAAA,MAAC;AAAA;AAAA,QAEC,WAAU;AAAA,QACV,6BAA2B,OAAO;AAAA,QAElC;AAAA,+BAAC,YAAO,WAAU,kDAChB;AAAA,gCAAC,QAAG,WAAU,yBAAyB,iBAAO,OAAM;AAAA,YACpD,oBAAC,UAAK,WAAU,2CACb,iBAAO,YACV;AAAA,aACF;AAAA,UACC,OAAO,UAAU,SAAS,IACzB;AAAA,YAAC;AAAA;AAAA,cACC,MAAM,OAAO;AAAA,cACb;AAAA,cACA;AAAA,cACA;AAAA;AAAA,UACF,IAEA,oBAAC,SAAM,
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Alert, AlertDescription } from '../../primitives/alert'\nimport type {\n AiPendingActionCardFieldDiff,\n AiPendingActionCardRecordDiff,\n} from './types'\n\n/**\n * Presentational card rendering a `fieldDiff` list in compact three-column\n * form (field | before | after) with DS-compliant semantic-token colors.\n * Accepts either a flat `fieldDiff[]` (single-record preview) or grouped\n * `records[]` (batch preview) \u2014 when both are supplied, `records` wins,\n * matching the server-side `AiPendingAction` contract (spec \u00A78 rule 2).\n */\nexport interface FieldDiffCardProps {\n fieldDiff?: AiPendingActionCardFieldDiff[] | null\n records?: AiPendingActionCardRecordDiff[] | null\n /** Optional forwarded componentId for the registry renderer. */\n componentId?: string\n}\n\nfunction formatValue(value: unknown): string {\n if (value == null) return ''\n if (typeof value === 'string') return value\n try {\n return JSON.stringify(value)\n } catch {\n return String(value)\n }\n}\n\nfunction DiffRow({ entry }: { entry: AiPendingActionCardFieldDiff }) {\n const before = formatValue(entry.beforeDisplay ?? entry.before)\n const after = formatValue(entry.afterDisplay ?? entry.after)\n const field = entry.fieldLabel ?? entry.field\n return (\n <tr className=\"border-b border-border last:border-b-0\" data-ai-field-diff-row>\n <td className=\"py-1.5 pr-4 text-xs font-mono text-muted-foreground align-top\">\n {field}\n </td>\n <td\n className=\"py-1.5 pr-4 text-sm align-top text-status-warning-text\"\n data-ai-field-diff-before\n >\n <span className=\"line-through break-all\">{before || '\u2014'}</span>\n </td>\n <td\n className=\"py-1.5 text-sm align-top text-status-success-text\"\n data-ai-field-diff-after\n >\n <span className=\"font-medium break-all\">{after || '\u2014'}</span>\n </td>\n </tr>\n )\n}\n\nfunction DiffTable({ rows, fieldHeader, beforeHeader, afterHeader }: {\n rows: AiPendingActionCardFieldDiff[]\n fieldHeader: string\n beforeHeader: string\n afterHeader: string\n}) {\n return (\n <table className=\"w-full\" data-ai-field-diff-table>\n <thead>\n <tr className=\"border-b border-border\">\n <th className=\"py-1 pr-4 text-left text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n {fieldHeader}\n </th>\n <th className=\"py-1 pr-4 text-left text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n {beforeHeader}\n </th>\n <th className=\"py-1 text-left text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n {afterHeader}\n </th>\n </tr>\n </thead>\n <tbody>\n {rows.map((entry, idx) => (\n <DiffRow key={`${entry.field}-${idx}`} entry={entry} />\n ))}\n </tbody>\n </table>\n )\n}\n\nexport function FieldDiffCard({ fieldDiff, records }: FieldDiffCardProps) {\n const t = useT()\n const fieldHeader = t(\n 'ai_assistant.chat.mutation_cards.diff.fieldHeader',\n 'Field',\n )\n const beforeHeader = t(\n 'ai_assistant.chat.mutation_cards.diff.beforeHeader',\n 'Before',\n )\n const afterHeader = t(\n 'ai_assistant.chat.mutation_cards.diff.afterHeader',\n 'After',\n )\n\n const batch = Array.isArray(records) && records.length > 0 ? records : null\n const flat = Array.isArray(fieldDiff) ? fieldDiff : []\n\n if (batch) {\n return (\n <div className=\"flex flex-col gap-3\" data-ai-field-diff-mode=\"batch\">\n {batch.map((record) => (\n <section\n key={record.recordId}\n className=\"rounded-md border border-border bg-background p-3\"\n data-ai-field-diff-record={record.recordId}\n >\n <header className=\"mb-2 flex items-baseline justify-between gap-2\">\n <h4 className=\"text-sm font-semibold\">{record.label}</h4>\n <span className=\"text-xs font-mono text-muted-foreground\">\n {record.entityType}\n </span>\n </header>\n {record.fieldDiff.length > 0 ? (\n <DiffTable\n rows={record.fieldDiff}\n fieldHeader={fieldHeader}\n beforeHeader={beforeHeader}\n afterHeader={afterHeader}\n />\n ) : (\n <Alert status=\"information\">\n <AlertDescription>\n {t(\n 'ai_assistant.chat.mutation_cards.diff.empty',\n 'No field changes for this record.',\n )}\n </AlertDescription>\n </Alert>\n )}\n </section>\n ))}\n </div>\n )\n }\n\n if (flat.length === 0) {\n return (\n <Alert status=\"information\" data-ai-field-diff-mode=\"empty\">\n <AlertDescription>\n {t(\n 'ai_assistant.chat.mutation_cards.diff.empty',\n 'No field changes for this record.',\n )}\n </AlertDescription>\n </Alert>\n )\n }\n\n return (\n <div data-ai-field-diff-mode=\"flat\">\n <DiffTable\n rows={flat}\n fieldHeader={fieldHeader}\n beforeHeader={beforeHeader}\n afterHeader={afterHeader}\n />\n </div>\n )\n}\n\nexport default FieldDiffCard\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -34,7 +34,7 @@ function MutationResultCard(props) {
|
|
|
34
34
|
return null;
|
|
35
35
|
}
|
|
36
36
|
if (isSuccessStatus(status) && failedRecords && failedRecords.length > 0) {
|
|
37
|
-
return /* @__PURE__ */ jsxs(Alert, {
|
|
37
|
+
return /* @__PURE__ */ jsxs(Alert, { status: "warning", "data-ai-mutation-result": "partial", children: [
|
|
38
38
|
/* @__PURE__ */ jsx(AlertTitle, { children: t(
|
|
39
39
|
"ai_assistant.chat.mutation_cards.result.partialTitle",
|
|
40
40
|
"Action applied with failures"
|
|
@@ -64,7 +64,7 @@ function MutationResultCard(props) {
|
|
|
64
64
|
if (isSuccessStatus(status)) {
|
|
65
65
|
const recordId = result?.recordId ?? action.targetRecordId ?? null;
|
|
66
66
|
const href = payload.recordHref ?? null;
|
|
67
|
-
return /* @__PURE__ */ jsxs(Alert, {
|
|
67
|
+
return /* @__PURE__ */ jsxs(Alert, { status: "success", "data-ai-mutation-result": "success", children: [
|
|
68
68
|
/* @__PURE__ */ jsx(AlertTitle, { children: t("ai_assistant.chat.mutation_cards.result.successTitle", "Action applied") }),
|
|
69
69
|
/* @__PURE__ */ jsxs("div", { className: "text-sm leading-relaxed", children: [
|
|
70
70
|
/* @__PURE__ */ jsx("p", { children: t(
|
|
@@ -174,7 +174,7 @@ function MutationResultCard(props) {
|
|
|
174
174
|
});
|
|
175
175
|
};
|
|
176
176
|
const visibleIssues = Array.isArray(errorDetails?.issues) ? errorDetails.issues.filter((entry) => entry && (entry.message || entry.path)) : [];
|
|
177
|
-
return /* @__PURE__ */ jsxs(Alert, {
|
|
177
|
+
return /* @__PURE__ */ jsxs(Alert, { status: "error", "data-ai-mutation-result": "failure", children: [
|
|
178
178
|
/* @__PURE__ */ jsx(AlertTitle, { children: t(
|
|
179
179
|
"ai_assistant.chat.mutation_cards.result.failureTitle",
|
|
180
180
|
"Action failed"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/ai/parts/MutationResultCard.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Wand2 } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Alert, AlertDescription, AlertTitle } from '../../primitives/alert'\nimport { Button } from '../../primitives/button'\nimport type { AiUiPartProps } from '../ui-part-registry'\nimport { useAiPendingActionPolling } from './useAiPendingActionPolling'\nimport type { AiPendingActionCardAction, AiPendingActionCardStatus } from './types'\n\n/** Custom DOM event the failure card dispatches when the operator clicks\n * \"Fix with AI\". `<AiChat>` listens for this and sends a follow-up user\n * message asking the agent to diagnose and retry the failed call. */\nexport const AI_CHAT_FIX_REQUEST_EVENT = 'om-ai-chat-fix-request'\n\nexport interface AiChatFixRequestDetail {\n message: string\n toolName?: string\n pendingActionId?: string\n}\n\nfunction dispatchFixRequest(detail: AiChatFixRequestDetail): void {\n if (typeof window === 'undefined') return\n window.dispatchEvent(new CustomEvent<AiChatFixRequestDetail>(AI_CHAT_FIX_REQUEST_EVENT, { detail }))\n}\n\n/**\n * Terminal-state card that renders the `executionResult` of a pending\n * action. Success \u2192 `Alert variant=\"success\"` with a record link; partial\n * success (batch `failedRecords[]`) \u2192 `variant=\"warning\"` with the list;\n * failure \u2192 `variant=\"destructive\"` with the error code + message.\n *\n * Reads the pending action via the shared polling hook so page reloads\n * still recover state. The hook short-circuits once the row is terminal\n * (spec's reconnect behavior), which is always the case for this card.\n */\nexport interface MutationResultCardPayload {\n /** Server-serialized pending action snapshot (optional \u2014 the hook refetches). */\n pendingAction?: AiPendingActionCardAction\n /** Optional link target for the success record. */\n recordHref?: string\n}\n\nexport interface MutationResultCardProps extends AiUiPartProps {\n /** Optional injected action for tests \u2014 bypasses the polling fetch. */\n initialAction?: AiPendingActionCardAction\n /** Poll endpoint override for tests. */\n endpoint?: string\n}\n\nfunction isSuccessStatus(status: AiPendingActionCardStatus | null): boolean {\n return status === 'confirmed'\n}\n\nfunction isFailureStatus(status: AiPendingActionCardStatus | null): boolean {\n return status === 'failed' || status === 'cancelled' || status === 'expired'\n}\n\nexport function MutationResultCard(props: MutationResultCardProps) {\n const t = useT()\n const pendingActionId = props.pendingActionId ?? ''\n const payload = (props.payload as MutationResultCardPayload | undefined) ?? {}\n const injected = props.initialAction ?? payload.pendingAction ?? null\n\n const { action: polled } = useAiPendingActionPolling({\n pendingActionId,\n endpoint: props.endpoint,\n disabled: !pendingActionId || Boolean(injected),\n })\n const action = injected ?? polled\n const status = action?.status ?? null\n const failedRecords = action?.failedRecords ?? null\n const result = action?.executionResult ?? null\n\n if (!action) {\n return null\n }\n\n if (isSuccessStatus(status) && failedRecords && failedRecords.length > 0) {\n return (\n <Alert variant=\"warning\" data-ai-mutation-result=\"partial\">\n <AlertTitle>\n {t(\n 'ai_assistant.chat.mutation_cards.result.partialTitle',\n 'Action applied with failures',\n )}\n </AlertTitle>\n <div className=\"text-sm leading-relaxed\">\n <p>\n {t(\n 'ai_assistant.chat.mutation_cards.result.partialBody',\n 'Some records could not be updated.',\n )}\n </p>\n <ul\n className=\"mt-2 list-disc space-y-1 pl-5 text-xs\"\n data-ai-mutation-failed-records\n >\n {failedRecords.map((record) => (\n <li key={record.recordId} data-ai-mutation-failed-record={record.recordId}>\n <span className=\"font-mono\">{record.recordId}</span>\n <span className=\"mx-1 text-muted-foreground\">\u2022</span>\n <span className=\"font-mono\">{record.error.code}</span>\n <span className=\"mx-1 text-muted-foreground\">\u2014</span>\n <span>{record.error.message}</span>\n </li>\n ))}\n </ul>\n </div>\n </Alert>\n )\n }\n\n if (isSuccessStatus(status)) {\n const recordId = result?.recordId ?? action.targetRecordId ?? null\n const href = payload.recordHref ?? null\n return (\n <Alert variant=\"success\" data-ai-mutation-result=\"success\">\n <AlertTitle>\n {t('ai_assistant.chat.mutation_cards.result.successTitle', 'Action applied')}\n </AlertTitle>\n <div className=\"text-sm leading-relaxed\">\n <p>\n {t(\n 'ai_assistant.chat.mutation_cards.result.successBody',\n 'The requested change was saved successfully.',\n )}\n </p>\n {recordId && href ? (\n <p className=\"mt-1 text-xs\">\n <a\n className=\"text-primary underline\"\n href={href}\n data-ai-mutation-result-link\n >\n {t(\n 'ai_assistant.chat.mutation_cards.result.viewRecord',\n 'View record',\n )}\n </a>\n </p>\n ) : null}\n </div>\n </Alert>\n )\n }\n\n // Render the failure alert when the action is in a failure status OR when\n // the dispatcher already captured an `executionResult.error` (the row may\n // not yet have transitioned out of `executing` in the polling snapshot,\n // but the handler error is authoritative \u2014 surface it immediately so the\n // operator never sees a stuck \"applying\u2026\" state silently masking a real\n // failure).\n if (isFailureStatus(status) || result?.error) {\n const code = result?.error?.code ?? status ?? 'failed'\n const message =\n result?.error?.message ??\n t(\n 'ai_assistant.chat.mutation_cards.result.failureBody',\n 'The mutation could not be applied.',\n )\n const errorObj = result?.error\n const errorDetails = errorObj?.details\n const errorInput = errorObj?.input\n const errorName = errorObj?.name\n const onFixWithAi = () => {\n // Build a structured prompt that gives the agent enough context to\n // diagnose and retry without copy/paste from the operator. Keeping\n // it explicit (\"retry with corrected arguments\") nudges the model\n // away from re-issuing the same args (which would just hit the\n // same error). The repository's idempotency check only dedupes\n // active `pending` rows, so a fresh prepareMutation call after a\n // terminal failure always produces a new pending action \u2014 the\n // retry is never silently collapsed.\n //\n // The prompt now embeds the full structured failure context the\n // server captured (Zod issues / fieldErrors, original arguments,\n // failedRecords for batch tools, error name + cause). Without this\n // the operator routinely saw \"Invalid input\" with no field path \u2014\n // the model literally could not fix what it could not see.\n const promptLines: string[] = [\n `The previous call to tool \"${action.toolName}\" failed.`,\n `Error: ${code} \u2014 ${message}.`,\n ]\n if (errorName && errorName !== 'Error') {\n promptLines.push(`Error class: ${errorName}.`)\n }\n if (action.targetEntityType || action.targetRecordId) {\n promptLines.push(\n `Target: ${action.targetEntityType ?? '?'}${action.targetRecordId ? ' / ' + action.targetRecordId : ''}.`,\n )\n }\n\n // Field-level validation issues (Zod, custom). Render as a bulleted\n // list of `path: message` so the model can locate the offender by\n // schema path instead of guessing from a generic message.\n const issues = errorDetails?.issues ?? []\n if (Array.isArray(issues) && issues.length > 0) {\n promptLines.push('', 'Validation issues:')\n for (const issue of issues) {\n const path = Array.isArray(issue?.path) && issue.path.length > 0\n ? issue.path.join('.')\n : '(root)'\n const msg = issue?.message ?? '(no message)'\n const codeHint = issue?.code ? ` [${issue.code}]` : ''\n const expHint =\n issue?.expected || issue?.received\n ? ` (expected ${issue?.expected ?? '?'}, got ${issue?.received ?? '?'})`\n : ''\n promptLines.push(`- ${path}: ${msg}${codeHint}${expHint}`)\n }\n } else if (errorDetails?.fieldErrors && typeof errorDetails.fieldErrors === 'object') {\n const entries = Object.entries(errorDetails.fieldErrors)\n if (entries.length > 0) {\n promptLines.push('', 'Field errors:')\n for (const [path, msgs] of entries) {\n const list = Array.isArray(msgs) ? msgs.join('; ') : String(msgs)\n promptLines.push(`- ${path}: ${list}`)\n }\n }\n }\n\n // Echo the arguments the handler was invoked with so the model can\n // see exactly what it sent and change at least one parameter on\n // retry. JSON-stringified inline for compactness; non-serializable\n // values are dropped on the server side already.\n if (errorInput !== undefined) {\n try {\n const json = JSON.stringify(errorInput, null, 2)\n if (json && json !== '{}' && json.length <= 4000) {\n promptLines.push('', 'Arguments you sent:', '```json', json, '```')\n } else if (json && json.length > 4000) {\n promptLines.push(\n '',\n 'Arguments you sent (truncated):',\n '```json',\n json.slice(0, 4000) + '\\n\u2026 [truncated]',\n '```',\n )\n }\n } catch {\n // ignore\n }\n }\n\n // Surface root-cause when the handler nested another error inside.\n if (errorDetails?.cause !== undefined) {\n try {\n const causeJson = JSON.stringify(errorDetails.cause, null, 2)\n if (causeJson && causeJson !== '{}' && causeJson.length <= 1500) {\n promptLines.push('', 'Underlying cause:', '```json', causeJson, '```')\n }\n } catch {\n // ignore\n }\n }\n\n // Per-record failures from batch tools (Step 5.14). These usually\n // carry the most actionable information for partial-success cases.\n if (failedRecords && failedRecords.length > 0) {\n promptLines.push('', 'Records that failed:')\n for (const rec of failedRecords) {\n promptLines.push(\n `- ${rec.recordId} \u2192 ${rec.error.code}: ${rec.error.message}`,\n )\n }\n }\n\n promptLines.push(\n '',\n 'Diagnose what went wrong using the validation issues / cause / arguments above, correct the arguments, and call the tool again. If the failure indicates missing prerequisites (e.g. a deal needs a linked person/company before commenting), tell me what to fix on the platform side instead of retrying blindly. Do not repeat the exact same arguments \u2014 you must change at least one parameter or stop and explain.',\n )\n dispatchFixRequest({\n message: promptLines.join('\\n'),\n toolName: action.toolName,\n pendingActionId: action.id,\n })\n }\n const visibleIssues = Array.isArray(errorDetails?.issues)\n ? errorDetails!.issues!.filter((entry) => entry && (entry.message || entry.path))\n : []\n return (\n <Alert variant=\"destructive\" data-ai-mutation-result=\"failure\">\n <AlertTitle>\n {t(\n 'ai_assistant.chat.mutation_cards.result.failureTitle',\n 'Action failed',\n )}\n </AlertTitle>\n <div className=\"text-sm leading-relaxed\">\n <div>\n <span className=\"mr-2 font-mono text-xs\" data-ai-mutation-result-code>\n {code}\n </span>\n <span>{message}</span>\n </div>\n {visibleIssues.length > 0 ? (\n <ul\n className=\"mt-2 list-disc space-y-0.5 pl-5 text-xs\"\n data-ai-mutation-result-issues\n >\n {visibleIssues.map((issue, index) => {\n const path =\n Array.isArray(issue?.path) && issue.path.length > 0\n ? issue.path.join('.')\n : null\n return (\n <li key={index}>\n {path ? (\n <span className=\"font-mono\">{path}</span>\n ) : null}\n {path && issue?.message ? <span className=\"mx-1\">\u2014</span> : null}\n {issue?.message ? <span>{issue.message}</span> : null}\n </li>\n )\n })}\n </ul>\n ) : null}\n <div className=\"mt-2 flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={onFixWithAi}\n data-ai-mutation-result-fix\n >\n <Wand2 className=\"size-4\" aria-hidden />\n <span>\n {t(\n 'ai_assistant.chat.mutation_cards.result.fixWithAi',\n 'Fix with AI',\n )}\n </span>\n </Button>\n </div>\n </div>\n </Alert>\n )\n }\n\n return null\n}\n\nexport default MutationResultCard\n"],
|
|
5
|
-
"mappings": ";AAkFQ,cAkBM,YAlBN;AA/ER,SAAS,aAAa;AACtB,SAAS,YAAY;AACrB,SAAS,OAAyB,kBAAkB;AACpD,SAAS,cAAc;AAEvB,SAAS,iCAAiC;AAMnC,MAAM,4BAA4B;AAQzC,SAAS,mBAAmB,QAAsC;AAChE,MAAI,OAAO,WAAW,YAAa;AACnC,SAAO,cAAc,IAAI,YAAoC,2BAA2B,EAAE,OAAO,CAAC,CAAC;AACrG;AA0BA,SAAS,gBAAgB,QAAmD;AAC1E,SAAO,WAAW;AACpB;AAEA,SAAS,gBAAgB,QAAmD;AAC1E,SAAO,WAAW,YAAY,WAAW,eAAe,WAAW;AACrE;AAEO,SAAS,mBAAmB,OAAgC;AACjE,QAAM,IAAI,KAAK;AACf,QAAM,kBAAkB,MAAM,mBAAmB;AACjD,QAAM,UAAW,MAAM,WAAqD,CAAC;AAC7E,QAAM,WAAW,MAAM,iBAAiB,QAAQ,iBAAiB;AAEjE,QAAM,EAAE,QAAQ,OAAO,IAAI,0BAA0B;AAAA,IACnD;AAAA,IACA,UAAU,MAAM;AAAA,IAChB,UAAU,CAAC,mBAAmB,QAAQ,QAAQ;AAAA,EAChD,CAAC;AACD,QAAM,SAAS,YAAY;AAC3B,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,gBAAgB,QAAQ,iBAAiB;AAC/C,QAAM,SAAS,QAAQ,mBAAmB;AAE1C,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AAEA,MAAI,gBAAgB,MAAM,KAAK,iBAAiB,cAAc,SAAS,GAAG;AACxE,WACE,qBAAC,SAAM,
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Wand2 } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Alert, AlertDescription, AlertTitle } from '../../primitives/alert'\nimport { Button } from '../../primitives/button'\nimport type { AiUiPartProps } from '../ui-part-registry'\nimport { useAiPendingActionPolling } from './useAiPendingActionPolling'\nimport type { AiPendingActionCardAction, AiPendingActionCardStatus } from './types'\n\n/** Custom DOM event the failure card dispatches when the operator clicks\n * \"Fix with AI\". `<AiChat>` listens for this and sends a follow-up user\n * message asking the agent to diagnose and retry the failed call. */\nexport const AI_CHAT_FIX_REQUEST_EVENT = 'om-ai-chat-fix-request'\n\nexport interface AiChatFixRequestDetail {\n message: string\n toolName?: string\n pendingActionId?: string\n}\n\nfunction dispatchFixRequest(detail: AiChatFixRequestDetail): void {\n if (typeof window === 'undefined') return\n window.dispatchEvent(new CustomEvent<AiChatFixRequestDetail>(AI_CHAT_FIX_REQUEST_EVENT, { detail }))\n}\n\n/**\n * Terminal-state card that renders the `executionResult` of a pending\n * action. Success \u2192 `Alert variant=\"success\"` with a record link; partial\n * success (batch `failedRecords[]`) \u2192 `variant=\"warning\"` with the list;\n * failure \u2192 `variant=\"destructive\"` with the error code + message.\n *\n * Reads the pending action via the shared polling hook so page reloads\n * still recover state. The hook short-circuits once the row is terminal\n * (spec's reconnect behavior), which is always the case for this card.\n */\nexport interface MutationResultCardPayload {\n /** Server-serialized pending action snapshot (optional \u2014 the hook refetches). */\n pendingAction?: AiPendingActionCardAction\n /** Optional link target for the success record. */\n recordHref?: string\n}\n\nexport interface MutationResultCardProps extends AiUiPartProps {\n /** Optional injected action for tests \u2014 bypasses the polling fetch. */\n initialAction?: AiPendingActionCardAction\n /** Poll endpoint override for tests. */\n endpoint?: string\n}\n\nfunction isSuccessStatus(status: AiPendingActionCardStatus | null): boolean {\n return status === 'confirmed'\n}\n\nfunction isFailureStatus(status: AiPendingActionCardStatus | null): boolean {\n return status === 'failed' || status === 'cancelled' || status === 'expired'\n}\n\nexport function MutationResultCard(props: MutationResultCardProps) {\n const t = useT()\n const pendingActionId = props.pendingActionId ?? ''\n const payload = (props.payload as MutationResultCardPayload | undefined) ?? {}\n const injected = props.initialAction ?? payload.pendingAction ?? null\n\n const { action: polled } = useAiPendingActionPolling({\n pendingActionId,\n endpoint: props.endpoint,\n disabled: !pendingActionId || Boolean(injected),\n })\n const action = injected ?? polled\n const status = action?.status ?? null\n const failedRecords = action?.failedRecords ?? null\n const result = action?.executionResult ?? null\n\n if (!action) {\n return null\n }\n\n if (isSuccessStatus(status) && failedRecords && failedRecords.length > 0) {\n return (\n <Alert status=\"warning\" data-ai-mutation-result=\"partial\">\n <AlertTitle>\n {t(\n 'ai_assistant.chat.mutation_cards.result.partialTitle',\n 'Action applied with failures',\n )}\n </AlertTitle>\n <div className=\"text-sm leading-relaxed\">\n <p>\n {t(\n 'ai_assistant.chat.mutation_cards.result.partialBody',\n 'Some records could not be updated.',\n )}\n </p>\n <ul\n className=\"mt-2 list-disc space-y-1 pl-5 text-xs\"\n data-ai-mutation-failed-records\n >\n {failedRecords.map((record) => (\n <li key={record.recordId} data-ai-mutation-failed-record={record.recordId}>\n <span className=\"font-mono\">{record.recordId}</span>\n <span className=\"mx-1 text-muted-foreground\">\u2022</span>\n <span className=\"font-mono\">{record.error.code}</span>\n <span className=\"mx-1 text-muted-foreground\">\u2014</span>\n <span>{record.error.message}</span>\n </li>\n ))}\n </ul>\n </div>\n </Alert>\n )\n }\n\n if (isSuccessStatus(status)) {\n const recordId = result?.recordId ?? action.targetRecordId ?? null\n const href = payload.recordHref ?? null\n return (\n <Alert status=\"success\" data-ai-mutation-result=\"success\">\n <AlertTitle>\n {t('ai_assistant.chat.mutation_cards.result.successTitle', 'Action applied')}\n </AlertTitle>\n <div className=\"text-sm leading-relaxed\">\n <p>\n {t(\n 'ai_assistant.chat.mutation_cards.result.successBody',\n 'The requested change was saved successfully.',\n )}\n </p>\n {recordId && href ? (\n <p className=\"mt-1 text-xs\">\n <a\n className=\"text-primary underline\"\n href={href}\n data-ai-mutation-result-link\n >\n {t(\n 'ai_assistant.chat.mutation_cards.result.viewRecord',\n 'View record',\n )}\n </a>\n </p>\n ) : null}\n </div>\n </Alert>\n )\n }\n\n // Render the failure alert when the action is in a failure status OR when\n // the dispatcher already captured an `executionResult.error` (the row may\n // not yet have transitioned out of `executing` in the polling snapshot,\n // but the handler error is authoritative \u2014 surface it immediately so the\n // operator never sees a stuck \"applying\u2026\" state silently masking a real\n // failure).\n if (isFailureStatus(status) || result?.error) {\n const code = result?.error?.code ?? status ?? 'failed'\n const message =\n result?.error?.message ??\n t(\n 'ai_assistant.chat.mutation_cards.result.failureBody',\n 'The mutation could not be applied.',\n )\n const errorObj = result?.error\n const errorDetails = errorObj?.details\n const errorInput = errorObj?.input\n const errorName = errorObj?.name\n const onFixWithAi = () => {\n // Build a structured prompt that gives the agent enough context to\n // diagnose and retry without copy/paste from the operator. Keeping\n // it explicit (\"retry with corrected arguments\") nudges the model\n // away from re-issuing the same args (which would just hit the\n // same error). The repository's idempotency check only dedupes\n // active `pending` rows, so a fresh prepareMutation call after a\n // terminal failure always produces a new pending action \u2014 the\n // retry is never silently collapsed.\n //\n // The prompt now embeds the full structured failure context the\n // server captured (Zod issues / fieldErrors, original arguments,\n // failedRecords for batch tools, error name + cause). Without this\n // the operator routinely saw \"Invalid input\" with no field path \u2014\n // the model literally could not fix what it could not see.\n const promptLines: string[] = [\n `The previous call to tool \"${action.toolName}\" failed.`,\n `Error: ${code} \u2014 ${message}.`,\n ]\n if (errorName && errorName !== 'Error') {\n promptLines.push(`Error class: ${errorName}.`)\n }\n if (action.targetEntityType || action.targetRecordId) {\n promptLines.push(\n `Target: ${action.targetEntityType ?? '?'}${action.targetRecordId ? ' / ' + action.targetRecordId : ''}.`,\n )\n }\n\n // Field-level validation issues (Zod, custom). Render as a bulleted\n // list of `path: message` so the model can locate the offender by\n // schema path instead of guessing from a generic message.\n const issues = errorDetails?.issues ?? []\n if (Array.isArray(issues) && issues.length > 0) {\n promptLines.push('', 'Validation issues:')\n for (const issue of issues) {\n const path = Array.isArray(issue?.path) && issue.path.length > 0\n ? issue.path.join('.')\n : '(root)'\n const msg = issue?.message ?? '(no message)'\n const codeHint = issue?.code ? ` [${issue.code}]` : ''\n const expHint =\n issue?.expected || issue?.received\n ? ` (expected ${issue?.expected ?? '?'}, got ${issue?.received ?? '?'})`\n : ''\n promptLines.push(`- ${path}: ${msg}${codeHint}${expHint}`)\n }\n } else if (errorDetails?.fieldErrors && typeof errorDetails.fieldErrors === 'object') {\n const entries = Object.entries(errorDetails.fieldErrors)\n if (entries.length > 0) {\n promptLines.push('', 'Field errors:')\n for (const [path, msgs] of entries) {\n const list = Array.isArray(msgs) ? msgs.join('; ') : String(msgs)\n promptLines.push(`- ${path}: ${list}`)\n }\n }\n }\n\n // Echo the arguments the handler was invoked with so the model can\n // see exactly what it sent and change at least one parameter on\n // retry. JSON-stringified inline for compactness; non-serializable\n // values are dropped on the server side already.\n if (errorInput !== undefined) {\n try {\n const json = JSON.stringify(errorInput, null, 2)\n if (json && json !== '{}' && json.length <= 4000) {\n promptLines.push('', 'Arguments you sent:', '```json', json, '```')\n } else if (json && json.length > 4000) {\n promptLines.push(\n '',\n 'Arguments you sent (truncated):',\n '```json',\n json.slice(0, 4000) + '\\n\u2026 [truncated]',\n '```',\n )\n }\n } catch {\n // ignore\n }\n }\n\n // Surface root-cause when the handler nested another error inside.\n if (errorDetails?.cause !== undefined) {\n try {\n const causeJson = JSON.stringify(errorDetails.cause, null, 2)\n if (causeJson && causeJson !== '{}' && causeJson.length <= 1500) {\n promptLines.push('', 'Underlying cause:', '```json', causeJson, '```')\n }\n } catch {\n // ignore\n }\n }\n\n // Per-record failures from batch tools (Step 5.14). These usually\n // carry the most actionable information for partial-success cases.\n if (failedRecords && failedRecords.length > 0) {\n promptLines.push('', 'Records that failed:')\n for (const rec of failedRecords) {\n promptLines.push(\n `- ${rec.recordId} \u2192 ${rec.error.code}: ${rec.error.message}`,\n )\n }\n }\n\n promptLines.push(\n '',\n 'Diagnose what went wrong using the validation issues / cause / arguments above, correct the arguments, and call the tool again. If the failure indicates missing prerequisites (e.g. a deal needs a linked person/company before commenting), tell me what to fix on the platform side instead of retrying blindly. Do not repeat the exact same arguments \u2014 you must change at least one parameter or stop and explain.',\n )\n dispatchFixRequest({\n message: promptLines.join('\\n'),\n toolName: action.toolName,\n pendingActionId: action.id,\n })\n }\n const visibleIssues = Array.isArray(errorDetails?.issues)\n ? errorDetails!.issues!.filter((entry) => entry && (entry.message || entry.path))\n : []\n return (\n <Alert status=\"error\" data-ai-mutation-result=\"failure\">\n <AlertTitle>\n {t(\n 'ai_assistant.chat.mutation_cards.result.failureTitle',\n 'Action failed',\n )}\n </AlertTitle>\n <div className=\"text-sm leading-relaxed\">\n <div>\n <span className=\"mr-2 font-mono text-xs\" data-ai-mutation-result-code>\n {code}\n </span>\n <span>{message}</span>\n </div>\n {visibleIssues.length > 0 ? (\n <ul\n className=\"mt-2 list-disc space-y-0.5 pl-5 text-xs\"\n data-ai-mutation-result-issues\n >\n {visibleIssues.map((issue, index) => {\n const path =\n Array.isArray(issue?.path) && issue.path.length > 0\n ? issue.path.join('.')\n : null\n return (\n <li key={index}>\n {path ? (\n <span className=\"font-mono\">{path}</span>\n ) : null}\n {path && issue?.message ? <span className=\"mx-1\">\u2014</span> : null}\n {issue?.message ? <span>{issue.message}</span> : null}\n </li>\n )\n })}\n </ul>\n ) : null}\n <div className=\"mt-2 flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={onFixWithAi}\n data-ai-mutation-result-fix\n >\n <Wand2 className=\"size-4\" aria-hidden />\n <span>\n {t(\n 'ai_assistant.chat.mutation_cards.result.fixWithAi',\n 'Fix with AI',\n )}\n </span>\n </Button>\n </div>\n </div>\n </Alert>\n )\n }\n\n return null\n}\n\nexport default MutationResultCard\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/ai/ui-parts/pending-phase3-placeholder.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Alert, AlertDescription, AlertTitle } from '../../primitives/alert'\nimport type { AiUiPartProps } from '../ui-part-registry'\n\n/**\n * Default placeholder for the four Phase 3 reserved slot ids.\n *\n * Consumers that forget to register the real mutation-approval cards still\n * see a humane \"Phase 3 pending\" state instead of the neutral debug chip the\n * `<AiChat>` fallback uses for genuinely unknown component ids. Uses the\n * shared DS `Alert` primitive with `
|
|
5
|
-
"mappings": ";AA4BM,cAMA,YANA;AAzBN,SAAS,YAAY;AACrB,SAAS,OAAO,kBAAkB,kBAAkB;AAiB7C,SAAS,yBAAyB,EAAE,YAAY,GAAkB;AACvE,QAAM,IAAI,KAAK;AACf,SACE;AAAA,IAAC;AAAA;AAAA,MACC,
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Alert, AlertDescription, AlertTitle } from '../../primitives/alert'\nimport type { AiUiPartProps } from '../ui-part-registry'\n\n/**\n * Default placeholder for the four Phase 3 reserved slot ids.\n *\n * Consumers that forget to register the real mutation-approval cards still\n * see a humane \"Phase 3 pending\" state instead of the neutral debug chip the\n * `<AiChat>` fallback uses for genuinely unknown component ids. Uses the\n * shared DS `Alert` primitive with `status=\"information\"` \u2014 no hardcoded colors.\n *\n * When Step 5.10 lands the real cards, app bootstrappers will call\n * `registerAiUiPart('mutation-preview-card', MutationPreviewCard)` (etc.)\n * which overwrites this placeholder. The unit tests in\n * `__tests__/ui-part-registry.test.ts` pin that replacement behavior so the\n * Phase 3 hand-off stays deterministic.\n */\nexport function PendingPhase3Placeholder({ componentId }: AiUiPartProps) {\n const t = useT()\n return (\n <Alert\n status=\"information\"\n data-ai-ui-part-pending-phase3={componentId}\n >\n <AlertTitle>\n {t(\n 'ai_assistant.chat.pending_phase3.title',\n 'Mutation approval card pending',\n )}\n </AlertTitle>\n <AlertDescription>\n <span>\n {t(\n 'ai_assistant.chat.pending_phase3.body',\n 'This interactive card will land in Phase 3 of the unified AI framework.',\n )}\n </span>\n <span className=\"ml-1 font-mono text-xs\">{componentId}</span>\n </AlertDescription>\n </Alert>\n )\n}\n\nexport default PendingPhase3Placeholder\n"],
|
|
5
|
+
"mappings": ";AA4BM,cAMA,YANA;AAzBN,SAAS,YAAY;AACrB,SAAS,OAAO,kBAAkB,kBAAkB;AAiB7C,SAAS,yBAAyB,EAAE,YAAY,GAAkB;AACvE,QAAM,IAAI,KAAK;AACf,SACE;AAAA,IAAC;AAAA;AAAA,MACC,QAAO;AAAA,MACP,kCAAgC;AAAA,MAEhC;AAAA,4BAAC,cACE;AAAA,UACC;AAAA,UACA;AAAA,QACF,GACF;AAAA,QACA,qBAAC,oBACC;AAAA,8BAAC,UACE;AAAA,YACC;AAAA,YACA;AAAA,UACF,GACF;AAAA,UACA,oBAAC,UAAK,WAAU,0BAA0B,uBAAY;AAAA,WACxD;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,qCAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -423,7 +423,7 @@ function PerspectiveSidebar({
|
|
|
423
423
|
onCancel: resetMode
|
|
424
424
|
}
|
|
425
425
|
) : null,
|
|
426
|
-
apiWarning ? /* @__PURE__ */ jsx(Alert, {
|
|
426
|
+
apiWarning ? /* @__PURE__ */ jsx(Alert, { status: "warning", className: "text-xs", children: apiWarning }) : null,
|
|
427
427
|
error ? /* @__PURE__ */ jsx("div", { className: "text-sm text-status-error-text", children: error }) : null
|
|
428
428
|
] }),
|
|
429
429
|
/* @__PURE__ */ jsx("div", { className: "flex items-center px-4 mt-2 pt-2 pb-2", children: /* @__PURE__ */ jsxs("label", { className: "inline-flex items-center gap-2 text-sm leading-none", children: [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/backend/PerspectiveSidebar.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { ArrowLeft, Plus } from 'lucide-react'\nimport { Alert } from '../primitives/alert'\nimport { Button } from '../primitives/button'\nimport { Checkbox } from '../primitives/checkbox'\nimport { Spinner } from '../primitives/spinner'\nimport { useConfirmDialog } from './confirm-dialog'\nimport { flash } from './FlashMessages'\nimport { surfaceRecordConflict } from './conflicts'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type {\n PerspectiveDto,\n PerspectiveSettings,\n RolePerspectiveDto,\n} from '@open-mercato/shared/modules/perspectives/types'\nimport { ViewChip } from './views/ViewChip'\nimport { NewViewForm } from './views/NewViewForm'\nimport { ShareForm } from './views/ShareForm'\nimport { type SidebarMode } from './views/types'\nimport { ColumnChooserSection, type ColumnChooserField } from './columns/ColumnChooserPanel'\n\nexport type { ColumnChooserField } from './columns/ColumnChooserPanel'\n\nexport type PerspectiveSidebarProps = {\n open: boolean\n onOpenChange: (open: boolean) => void\n loading: boolean\n perspectives: PerspectiveDto[]\n rolePerspectives: RolePerspectiveDto[]\n roles: Array<{ id: string; name: string; hasPerspective: boolean; hasDefault: boolean }>\n activePerspectiveId: string | null\n onActivatePerspective: (perspective: PerspectiveDto | RolePerspectiveDto, source: 'personal' | 'role') => void\n onDeletePerspective: (perspectiveId: string) => Promise<void>\n onClearRole: (perspective: RolePerspectiveDto) => Promise<void>\n onSave: (input: { name: string; isDefault: boolean; applyToRoles: string[]; setRoleDefault: boolean; perspectiveId?: string | null; settings?: PerspectiveSettings }) => Promise<void>\n canApplyToRoles: boolean\n availableColumns: ColumnChooserField[]\n visibleColumnKeys: string[]\n columnOrder: string[]\n onToggleColumn: (key: string) => void\n onReorderColumns: (orderedIds: string[]) => void\n saving: boolean\n deletingIds: string[]\n roleClearingIds: string[]\n apiWarning?: string | null\n}\n\nconst emptyArray: any[] = []\n\nexport function PerspectiveSidebar({\n open,\n onOpenChange,\n loading,\n perspectives,\n rolePerspectives,\n roles,\n activePerspectiveId,\n onActivatePerspective,\n onDeletePerspective,\n onClearRole,\n onSave,\n canApplyToRoles,\n availableColumns,\n visibleColumnKeys,\n columnOrder,\n onToggleColumn,\n onReorderColumns,\n saving,\n deletingIds,\n roleClearingIds,\n apiWarning,\n}: PerspectiveSidebarProps) {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n\n React.useEffect(() => {\n if (!open) return\n if (typeof document === 'undefined') return\n document.body.dataset.columnChooserOpen = 'true'\n return () => {\n delete document.body.dataset.columnChooserOpen\n }\n }, [open])\n\n function perspectiveLabel(p: PerspectiveDto | RolePerspectiveDto) {\n return p.name.trim().length ? p.name : t('ui.perspectives.untitled', 'Untitled view')\n }\n\n const [name, setName] = React.useState('')\n const [isDefault, setIsDefault] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n const [mode, setMode] = React.useState<SidebarMode>({ type: 'idle' })\n const [renamingId, setRenamingId] = React.useState<string | null>(null)\n const [renameValue, setRenameValue] = React.useState('')\n const [shareRoles, setShareRoles] = React.useState<string[]>([])\n const [shareSetDefault, setShareSetDefault] = React.useState(false)\n const [sharedIds, setSharedIds] = React.useState<Set<string>>(new Set())\n const [pendingCloneBaselineIds, setPendingCloneBaselineIds] = React.useState<Set<string> | null>(null)\n\n const autosaveRef = React.useRef<ReturnType<typeof setTimeout> | null>(null)\n const isDefaultUserChangeRef = React.useRef(false)\n const isDefaultRef = React.useRef(isDefault)\n isDefaultRef.current = isDefault\n\n const onSaveRef = React.useRef(onSave)\n onSaveRef.current = onSave\n\n const perspectivesRef = React.useRef(perspectives)\n perspectivesRef.current = perspectives\n\n const flushAutosave = React.useCallback(() => {\n if (autosaveRef.current) {\n clearTimeout(autosaveRef.current)\n autosaveRef.current = null\n }\n }, [])\n\n const scheduleAutosave = React.useCallback(() => {\n if (!activePerspectiveId || mode.type === 'new') return\n const activePersonal = perspectivesRef.current.find((p) => p.id === activePerspectiveId)\n if (!activePersonal) return\n flushAutosave()\n const targetId = activePersonal.id\n const targetName = activePersonal.name\n autosaveRef.current = setTimeout(async () => {\n autosaveRef.current = null\n try {\n await onSaveRef.current({\n name: targetName,\n isDefault: isDefaultRef.current,\n applyToRoles: [],\n setRoleDefault: false,\n perspectiveId: targetId,\n })\n flash(t('ui.perspectives.autosave.success', 'View saved'), 'success')\n } catch (err: unknown) {\n if (surfaceRecordConflict(err, t)) return\n flash(t('ui.perspectives.autosave.error', 'Failed to save view'), 'error')\n }\n }, 400)\n }, [activePerspectiveId, mode.type, flushAutosave, t])\n\n React.useEffect(() => {\n return () => { flushAutosave() }\n }, [flushAutosave])\n\n const resetMode = () => {\n setMode({ type: 'idle' })\n setName('')\n setShareRoles([])\n setShareSetDefault(false)\n }\n\n const startNewMode = () => {\n setMode({ type: 'new' })\n setName('')\n setIsDefault(false)\n setRenamingId(null)\n setShareRoles([])\n setShareSetDefault(false)\n }\n\n const startShareMode = (p: PerspectiveDto) => {\n setMode({ type: 'share', perspectiveId: p.id, perspectiveName: p.name, perspectiveIsDefault: p.isDefault })\n setName('')\n setRenamingId(null)\n setShareRoles([])\n setShareSetDefault(false)\n }\n\n React.useEffect(() => {\n if (!open) {\n flushAutosave()\n setError(null)\n resetMode()\n setRenamingId(null)\n setSharedIds(new Set())\n setPendingCloneBaselineIds(null)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open])\n\n React.useEffect(() => {\n if (!open) return\n const active = perspectives.find((p) => p.id === activePerspectiveId)\n ?? rolePerspectives.find((p) => p.id === activePerspectiveId)\n isDefaultUserChangeRef.current = false\n if (active) {\n setIsDefault(active.isDefault)\n } else {\n setIsDefault(false)\n }\n requestAnimationFrame(() => { isDefaultUserChangeRef.current = true })\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open, activePerspectiveId])\n\n const handleSaveNew = async () => {\n setError(null)\n try {\n await onSave({ name: name.trim(), isDefault, applyToRoles: [], setRoleDefault: false, perspectiveId: null })\n resetMode()\n } catch (err: unknown) {\n if (surfaceRecordConflict(err, t)) return\n setError(err instanceof Error ? err.message : 'Failed to save view')\n }\n }\n\n const handleToggleColumnWithAutosave = React.useCallback((key: string) => {\n onToggleColumn(key)\n scheduleAutosave()\n }, [onToggleColumn, scheduleAutosave])\n\n const handleReorderColumnsWithAutosave = React.useCallback((orderedIds: string[]) => {\n onReorderColumns(orderedIds)\n scheduleAutosave()\n }, [onReorderColumns, scheduleAutosave])\n\n const handleRename = async (p: PerspectiveDto | RolePerspectiveDto) => {\n const trimmed = renameValue.trim()\n if (!trimmed) {\n setRenamingId(null)\n return\n }\n const conflict = perspectivesRef.current.some(\n (item) => item.id !== p.id && item.name.trim() === trimmed,\n )\n if (conflict) {\n flash(t('ui.perspectives.error.nameExists', 'View with this name already exists'), 'error')\n return\n }\n setError(null)\n try {\n await onSave({\n name: trimmed,\n isDefault: p.isDefault,\n applyToRoles: [],\n setRoleDefault: false,\n perspectiveId: p.id,\n })\n setRenamingId(null)\n } catch (err: unknown) {\n if (surfaceRecordConflict(err, t)) return\n setError(err instanceof Error ? err.message : 'Failed to rename view')\n }\n }\n\n const handleClone = async (p: PerspectiveDto | RolePerspectiveDto) => {\n const originalName = perspectiveLabel(p)\n setError(null)\n const baseline = new Set(perspectivesRef.current.map((item) => item.id))\n const originalSettingsKey = JSON.stringify(p.settings ?? {})\n const sharedRoleIds = Array.from(\n new Set(\n rolePerspectives\n .filter((rp) => JSON.stringify(rp.settings ?? {}) === originalSettingsKey)\n .map((rp) => rp.roleId),\n ),\n )\n const buildCloneName = (n: number) =>\n n <= 1 ? `${originalName} (copy)` : `${originalName} (copy ${n})`\n const existingNames = new Set(\n perspectivesRef.current.map((item) => item.name.trim()),\n )\n let counter = 1\n while (existingNames.has(buildCloneName(counter))) {\n counter += 1\n }\n const clonedSettings = structuredClone(p.settings)\n const MAX_ATTEMPTS = 50\n let lastErr: unknown = null\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt += 1) {\n try {\n await onSave({\n name: buildCloneName(counter),\n isDefault: false,\n applyToRoles: sharedRoleIds,\n setRoleDefault: false,\n perspectiveId: null,\n settings: clonedSettings,\n })\n setPendingCloneBaselineIds(baseline)\n return\n } catch (err: unknown) {\n const errMessage = err instanceof Error ? err.message : ''\n const msg = errMessage.toLowerCase()\n const isDuplicate = msg.includes('duplicate key') || msg.includes('unique constraint')\n if (!isDuplicate) {\n if (surfaceRecordConflict(err, t)) return\n setError(errMessage || 'Failed to clone view')\n return\n }\n lastErr = err\n counter += 1\n }\n }\n if (surfaceRecordConflict(lastErr, t)) return\n setError(lastErr instanceof Error ? lastErr.message : 'Failed to clone view')\n }\n\n React.useEffect(() => {\n if (!pendingCloneBaselineIds) return\n const created = perspectives.find((item) => !pendingCloneBaselineIds.has(item.id))\n if (!created) return\n setRenamingId(created.id)\n setRenameValue(created.name)\n setPendingCloneBaselineIds(null)\n }, [perspectives, pendingCloneBaselineIds])\n\n const handleDelete = async (p: PerspectiveDto) => {\n const confirmed = await confirm({\n title: t('ui.perspectives.delete.title', 'Delete \"{name}\"?', { name: perspectiveLabel(p) }),\n text: t('ui.perspectives.delete.text', 'This view will be removed for you and all shared roles. This cannot be undone.'),\n confirmText: t('common.delete', 'Delete'),\n variant: 'destructive',\n })\n if (confirmed) {\n await onDeletePerspective(p.id)\n }\n }\n\n const handleDeleteRole = async (p: RolePerspectiveDto) => {\n const confirmed = await confirm({\n title: t('ui.perspectives.delete.title', 'Delete \"{name}\"?', { name: perspectiveLabel(p) }),\n text: t('ui.perspectives.deleteRole.text', 'This shared view will be removed for the role. This cannot be undone.'),\n confirmText: t('common.delete', 'Delete'),\n variant: 'destructive',\n })\n if (confirmed) {\n await onClearRole(p)\n }\n }\n\n const handleShareApply = async () => {\n if (mode.type !== 'share') return\n setError(null)\n try {\n await onSave({ name: mode.perspectiveName, isDefault: mode.perspectiveIsDefault, applyToRoles: shareRoles, setRoleDefault: shareSetDefault, perspectiveId: mode.perspectiveId })\n setSharedIds((prev) => new Set([...prev, mode.perspectiveId]))\n resetMode()\n } catch (err: unknown) {\n if (surfaceRecordConflict(err, t)) return\n setError(err instanceof Error ? err.message : 'Failed to share view')\n }\n }\n\n const toggleShareRole = (roleId: string) => {\n setShareRoles((prev) => {\n const next = new Set(prev)\n if (next.has(roleId)) next.delete(roleId)\n else next.add(roleId)\n return Array.from(next)\n })\n }\n\n if (!open) return null\n\n const isNew = mode.type === 'new'\n const isShare = mode.type === 'share'\n\n return (\n <div className=\"fixed inset-0 z-modal\">\n <div className=\"absolute inset-0 bg-black/20\" onClick={() => onOpenChange(false)} role=\"presentation\" />\n <div className=\"fixed right-0 top-0 h-full w-full sm:w-80 bg-background shadow-xl border-l flex flex-col\">\n <div className=\"flex items-center p-4 border-b\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => onOpenChange(false)}\n className=\"h-auto -ml-2 gap-2 px-2 text-lg font-semibold hover:bg-transparent\"\n aria-label={t('ui.perspectives.close', 'Close')}\n >\n <ArrowLeft className=\"size-5\" />\n {t('ui.perspectives.title', 'Views')}\n </Button>\n </div>\n <div className=\"flex-1 overflow-auto\">\n {/* Saved views as chips */}\n <section className=\"p-4 space-y-3\">\n <h3 className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">{t('ui.perspectives.savedViews.title', 'Saved views')}</h3>\n {loading ? <Spinner size=\"sm\" /> : null}\n <div className=\"flex flex-wrap gap-1.5\">\n {/* + New chip */}\n <Button\n type=\"button\"\n variant={isNew ? 'secondary' : 'outline'}\n size=\"sm\"\n className={`h-8 px-3 py-1.5 text-sm ${!isNew ? 'border-dashed' : ''}`}\n onClick={startNewMode}\n >\n <Plus className=\"size-3 mr-1\" />\n {t('ui.perspectives.savedViews.new', 'New')}\n </Button>\n {(perspectives ?? emptyArray).map((p) => {\n const isActive = activePerspectiveId === p.id\n const deleting = deletingIds.includes(p.id)\n const isShared = sharedIds.has(p.id)\n return (\n <ViewChip\n key={p.id}\n id={p.id}\n label={perspectiveLabel(p)}\n kind=\"personal\"\n isActive={isActive}\n disabled={deleting}\n isShared={isShared}\n isRenaming={renamingId === p.id}\n renameValue={renameValue}\n canApplyToRoles={canApplyToRoles}\n deleting={deleting}\n onActivate={() => { onActivatePerspective(p, 'personal'); resetMode() }}\n onRenameValueChange={setRenameValue}\n onRenameConfirm={() => void handleRename(p)}\n onRenameCancel={() => setRenamingId(null)}\n onRenameStart={() => { setRenamingId(p.id); setRenameValue(p.name) }}\n onClone={() => void handleClone(p)}\n onShareStart={() => startShareMode(p)}\n onDelete={() => void handleDelete(p)}\n />\n )\n })}\n {rolePerspectives\n .filter((rp) => {\n const rpName = rp.name.trim()\n return !perspectives.some((pp) => pp.name.trim() === rpName)\n })\n .map((p) => {\n const isActive = activePerspectiveId === p.id\n const clearing = roleClearingIds.includes(p.roleId)\n return (\n <ViewChip\n key={p.id}\n id={p.id}\n label={perspectiveLabel(p)}\n kind=\"role\"\n isActive={isActive}\n disabled={clearing}\n isShared={false}\n isRenaming={renamingId === p.id}\n renameValue={renameValue}\n canApplyToRoles={canApplyToRoles}\n deleting={clearing}\n onActivate={() => { onActivatePerspective(p, 'role'); resetMode() }}\n onRenameValueChange={setRenameValue}\n onRenameConfirm={() => void handleRename(p)}\n onRenameCancel={() => setRenamingId(null)}\n onClone={() => void handleClone(p)}\n onDelete={() => void handleDeleteRole(p)}\n />\n )\n })}\n </div>\n\n {/* Inline form slot \u2014 shared between New and Share modes */}\n {isNew ? (\n <NewViewForm\n name={name}\n onNameChange={setName}\n onSubmit={() => void handleSaveNew()}\n onCancel={resetMode}\n saving={saving}\n />\n ) : null}\n\n {isShare ? (\n <ShareForm\n roles={roles}\n shareRoles={shareRoles}\n shareSetDefault={shareSetDefault}\n onToggleRole={toggleShareRole}\n onToggleSetDefault={setShareSetDefault}\n onApply={() => void handleShareApply()}\n onCancel={resetMode}\n />\n ) : null}\n\n {apiWarning ? (\n <Alert variant=\"warning\" className=\"text-xs\">{apiWarning}</Alert>\n ) : null}\n {error ? <div className=\"text-sm text-status-error-text\">{error}</div> : null}\n </section>\n\n {/* Set as default \u2014 separated from chips (border-t) and from columns (ColumnList owns border-t) */}\n <div className=\"flex items-center px-4 mt-2 pt-2 pb-2\">\n <label className=\"inline-flex items-center gap-2 text-sm leading-none\">\n <Checkbox\n checked={isDefault}\n onCheckedChange={(checked) => {\n setIsDefault(checked === true)\n if (isDefaultUserChangeRef.current) {\n scheduleAutosave()\n }\n }}\n />\n {t('ui.perspectives.form.makeDefault', 'Set as default')}\n </label>\n </div>\n\n <ColumnChooserSection\n availableColumns={availableColumns}\n visibleColumnKeys={visibleColumnKeys}\n columnOrder={columnOrder}\n onToggleColumn={handleToggleColumnWithAutosave}\n onReorderColumns={handleReorderColumnsWithAutosave}\n dndContextId=\"perspective-columns\"\n />\n </div>\n </div>\n {ConfirmDialogElement}\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { ArrowLeft, Plus } from 'lucide-react'\nimport { Alert } from '../primitives/alert'\nimport { Button } from '../primitives/button'\nimport { Checkbox } from '../primitives/checkbox'\nimport { Spinner } from '../primitives/spinner'\nimport { useConfirmDialog } from './confirm-dialog'\nimport { flash } from './FlashMessages'\nimport { surfaceRecordConflict } from './conflicts'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type {\n PerspectiveDto,\n PerspectiveSettings,\n RolePerspectiveDto,\n} from '@open-mercato/shared/modules/perspectives/types'\nimport { ViewChip } from './views/ViewChip'\nimport { NewViewForm } from './views/NewViewForm'\nimport { ShareForm } from './views/ShareForm'\nimport { type SidebarMode } from './views/types'\nimport { ColumnChooserSection, type ColumnChooserField } from './columns/ColumnChooserPanel'\n\nexport type { ColumnChooserField } from './columns/ColumnChooserPanel'\n\nexport type PerspectiveSidebarProps = {\n open: boolean\n onOpenChange: (open: boolean) => void\n loading: boolean\n perspectives: PerspectiveDto[]\n rolePerspectives: RolePerspectiveDto[]\n roles: Array<{ id: string; name: string; hasPerspective: boolean; hasDefault: boolean }>\n activePerspectiveId: string | null\n onActivatePerspective: (perspective: PerspectiveDto | RolePerspectiveDto, source: 'personal' | 'role') => void\n onDeletePerspective: (perspectiveId: string) => Promise<void>\n onClearRole: (perspective: RolePerspectiveDto) => Promise<void>\n onSave: (input: { name: string; isDefault: boolean; applyToRoles: string[]; setRoleDefault: boolean; perspectiveId?: string | null; settings?: PerspectiveSettings }) => Promise<void>\n canApplyToRoles: boolean\n availableColumns: ColumnChooserField[]\n visibleColumnKeys: string[]\n columnOrder: string[]\n onToggleColumn: (key: string) => void\n onReorderColumns: (orderedIds: string[]) => void\n saving: boolean\n deletingIds: string[]\n roleClearingIds: string[]\n apiWarning?: string | null\n}\n\nconst emptyArray: any[] = []\n\nexport function PerspectiveSidebar({\n open,\n onOpenChange,\n loading,\n perspectives,\n rolePerspectives,\n roles,\n activePerspectiveId,\n onActivatePerspective,\n onDeletePerspective,\n onClearRole,\n onSave,\n canApplyToRoles,\n availableColumns,\n visibleColumnKeys,\n columnOrder,\n onToggleColumn,\n onReorderColumns,\n saving,\n deletingIds,\n roleClearingIds,\n apiWarning,\n}: PerspectiveSidebarProps) {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n\n React.useEffect(() => {\n if (!open) return\n if (typeof document === 'undefined') return\n document.body.dataset.columnChooserOpen = 'true'\n return () => {\n delete document.body.dataset.columnChooserOpen\n }\n }, [open])\n\n function perspectiveLabel(p: PerspectiveDto | RolePerspectiveDto) {\n return p.name.trim().length ? p.name : t('ui.perspectives.untitled', 'Untitled view')\n }\n\n const [name, setName] = React.useState('')\n const [isDefault, setIsDefault] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n const [mode, setMode] = React.useState<SidebarMode>({ type: 'idle' })\n const [renamingId, setRenamingId] = React.useState<string | null>(null)\n const [renameValue, setRenameValue] = React.useState('')\n const [shareRoles, setShareRoles] = React.useState<string[]>([])\n const [shareSetDefault, setShareSetDefault] = React.useState(false)\n const [sharedIds, setSharedIds] = React.useState<Set<string>>(new Set())\n const [pendingCloneBaselineIds, setPendingCloneBaselineIds] = React.useState<Set<string> | null>(null)\n\n const autosaveRef = React.useRef<ReturnType<typeof setTimeout> | null>(null)\n const isDefaultUserChangeRef = React.useRef(false)\n const isDefaultRef = React.useRef(isDefault)\n isDefaultRef.current = isDefault\n\n const onSaveRef = React.useRef(onSave)\n onSaveRef.current = onSave\n\n const perspectivesRef = React.useRef(perspectives)\n perspectivesRef.current = perspectives\n\n const flushAutosave = React.useCallback(() => {\n if (autosaveRef.current) {\n clearTimeout(autosaveRef.current)\n autosaveRef.current = null\n }\n }, [])\n\n const scheduleAutosave = React.useCallback(() => {\n if (!activePerspectiveId || mode.type === 'new') return\n const activePersonal = perspectivesRef.current.find((p) => p.id === activePerspectiveId)\n if (!activePersonal) return\n flushAutosave()\n const targetId = activePersonal.id\n const targetName = activePersonal.name\n autosaveRef.current = setTimeout(async () => {\n autosaveRef.current = null\n try {\n await onSaveRef.current({\n name: targetName,\n isDefault: isDefaultRef.current,\n applyToRoles: [],\n setRoleDefault: false,\n perspectiveId: targetId,\n })\n flash(t('ui.perspectives.autosave.success', 'View saved'), 'success')\n } catch (err: unknown) {\n if (surfaceRecordConflict(err, t)) return\n flash(t('ui.perspectives.autosave.error', 'Failed to save view'), 'error')\n }\n }, 400)\n }, [activePerspectiveId, mode.type, flushAutosave, t])\n\n React.useEffect(() => {\n return () => { flushAutosave() }\n }, [flushAutosave])\n\n const resetMode = () => {\n setMode({ type: 'idle' })\n setName('')\n setShareRoles([])\n setShareSetDefault(false)\n }\n\n const startNewMode = () => {\n setMode({ type: 'new' })\n setName('')\n setIsDefault(false)\n setRenamingId(null)\n setShareRoles([])\n setShareSetDefault(false)\n }\n\n const startShareMode = (p: PerspectiveDto) => {\n setMode({ type: 'share', perspectiveId: p.id, perspectiveName: p.name, perspectiveIsDefault: p.isDefault })\n setName('')\n setRenamingId(null)\n setShareRoles([])\n setShareSetDefault(false)\n }\n\n React.useEffect(() => {\n if (!open) {\n flushAutosave()\n setError(null)\n resetMode()\n setRenamingId(null)\n setSharedIds(new Set())\n setPendingCloneBaselineIds(null)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open])\n\n React.useEffect(() => {\n if (!open) return\n const active = perspectives.find((p) => p.id === activePerspectiveId)\n ?? rolePerspectives.find((p) => p.id === activePerspectiveId)\n isDefaultUserChangeRef.current = false\n if (active) {\n setIsDefault(active.isDefault)\n } else {\n setIsDefault(false)\n }\n requestAnimationFrame(() => { isDefaultUserChangeRef.current = true })\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open, activePerspectiveId])\n\n const handleSaveNew = async () => {\n setError(null)\n try {\n await onSave({ name: name.trim(), isDefault, applyToRoles: [], setRoleDefault: false, perspectiveId: null })\n resetMode()\n } catch (err: unknown) {\n if (surfaceRecordConflict(err, t)) return\n setError(err instanceof Error ? err.message : 'Failed to save view')\n }\n }\n\n const handleToggleColumnWithAutosave = React.useCallback((key: string) => {\n onToggleColumn(key)\n scheduleAutosave()\n }, [onToggleColumn, scheduleAutosave])\n\n const handleReorderColumnsWithAutosave = React.useCallback((orderedIds: string[]) => {\n onReorderColumns(orderedIds)\n scheduleAutosave()\n }, [onReorderColumns, scheduleAutosave])\n\n const handleRename = async (p: PerspectiveDto | RolePerspectiveDto) => {\n const trimmed = renameValue.trim()\n if (!trimmed) {\n setRenamingId(null)\n return\n }\n const conflict = perspectivesRef.current.some(\n (item) => item.id !== p.id && item.name.trim() === trimmed,\n )\n if (conflict) {\n flash(t('ui.perspectives.error.nameExists', 'View with this name already exists'), 'error')\n return\n }\n setError(null)\n try {\n await onSave({\n name: trimmed,\n isDefault: p.isDefault,\n applyToRoles: [],\n setRoleDefault: false,\n perspectiveId: p.id,\n })\n setRenamingId(null)\n } catch (err: unknown) {\n if (surfaceRecordConflict(err, t)) return\n setError(err instanceof Error ? err.message : 'Failed to rename view')\n }\n }\n\n const handleClone = async (p: PerspectiveDto | RolePerspectiveDto) => {\n const originalName = perspectiveLabel(p)\n setError(null)\n const baseline = new Set(perspectivesRef.current.map((item) => item.id))\n const originalSettingsKey = JSON.stringify(p.settings ?? {})\n const sharedRoleIds = Array.from(\n new Set(\n rolePerspectives\n .filter((rp) => JSON.stringify(rp.settings ?? {}) === originalSettingsKey)\n .map((rp) => rp.roleId),\n ),\n )\n const buildCloneName = (n: number) =>\n n <= 1 ? `${originalName} (copy)` : `${originalName} (copy ${n})`\n const existingNames = new Set(\n perspectivesRef.current.map((item) => item.name.trim()),\n )\n let counter = 1\n while (existingNames.has(buildCloneName(counter))) {\n counter += 1\n }\n const clonedSettings = structuredClone(p.settings)\n const MAX_ATTEMPTS = 50\n let lastErr: unknown = null\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt += 1) {\n try {\n await onSave({\n name: buildCloneName(counter),\n isDefault: false,\n applyToRoles: sharedRoleIds,\n setRoleDefault: false,\n perspectiveId: null,\n settings: clonedSettings,\n })\n setPendingCloneBaselineIds(baseline)\n return\n } catch (err: unknown) {\n const errMessage = err instanceof Error ? err.message : ''\n const msg = errMessage.toLowerCase()\n const isDuplicate = msg.includes('duplicate key') || msg.includes('unique constraint')\n if (!isDuplicate) {\n if (surfaceRecordConflict(err, t)) return\n setError(errMessage || 'Failed to clone view')\n return\n }\n lastErr = err\n counter += 1\n }\n }\n if (surfaceRecordConflict(lastErr, t)) return\n setError(lastErr instanceof Error ? lastErr.message : 'Failed to clone view')\n }\n\n React.useEffect(() => {\n if (!pendingCloneBaselineIds) return\n const created = perspectives.find((item) => !pendingCloneBaselineIds.has(item.id))\n if (!created) return\n setRenamingId(created.id)\n setRenameValue(created.name)\n setPendingCloneBaselineIds(null)\n }, [perspectives, pendingCloneBaselineIds])\n\n const handleDelete = async (p: PerspectiveDto) => {\n const confirmed = await confirm({\n title: t('ui.perspectives.delete.title', 'Delete \"{name}\"?', { name: perspectiveLabel(p) }),\n text: t('ui.perspectives.delete.text', 'This view will be removed for you and all shared roles. This cannot be undone.'),\n confirmText: t('common.delete', 'Delete'),\n variant: 'destructive',\n })\n if (confirmed) {\n await onDeletePerspective(p.id)\n }\n }\n\n const handleDeleteRole = async (p: RolePerspectiveDto) => {\n const confirmed = await confirm({\n title: t('ui.perspectives.delete.title', 'Delete \"{name}\"?', { name: perspectiveLabel(p) }),\n text: t('ui.perspectives.deleteRole.text', 'This shared view will be removed for the role. This cannot be undone.'),\n confirmText: t('common.delete', 'Delete'),\n variant: 'destructive',\n })\n if (confirmed) {\n await onClearRole(p)\n }\n }\n\n const handleShareApply = async () => {\n if (mode.type !== 'share') return\n setError(null)\n try {\n await onSave({ name: mode.perspectiveName, isDefault: mode.perspectiveIsDefault, applyToRoles: shareRoles, setRoleDefault: shareSetDefault, perspectiveId: mode.perspectiveId })\n setSharedIds((prev) => new Set([...prev, mode.perspectiveId]))\n resetMode()\n } catch (err: unknown) {\n if (surfaceRecordConflict(err, t)) return\n setError(err instanceof Error ? err.message : 'Failed to share view')\n }\n }\n\n const toggleShareRole = (roleId: string) => {\n setShareRoles((prev) => {\n const next = new Set(prev)\n if (next.has(roleId)) next.delete(roleId)\n else next.add(roleId)\n return Array.from(next)\n })\n }\n\n if (!open) return null\n\n const isNew = mode.type === 'new'\n const isShare = mode.type === 'share'\n\n return (\n <div className=\"fixed inset-0 z-modal\">\n <div className=\"absolute inset-0 bg-black/20\" onClick={() => onOpenChange(false)} role=\"presentation\" />\n <div className=\"fixed right-0 top-0 h-full w-full sm:w-80 bg-background shadow-xl border-l flex flex-col\">\n <div className=\"flex items-center p-4 border-b\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => onOpenChange(false)}\n className=\"h-auto -ml-2 gap-2 px-2 text-lg font-semibold hover:bg-transparent\"\n aria-label={t('ui.perspectives.close', 'Close')}\n >\n <ArrowLeft className=\"size-5\" />\n {t('ui.perspectives.title', 'Views')}\n </Button>\n </div>\n <div className=\"flex-1 overflow-auto\">\n {/* Saved views as chips */}\n <section className=\"p-4 space-y-3\">\n <h3 className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">{t('ui.perspectives.savedViews.title', 'Saved views')}</h3>\n {loading ? <Spinner size=\"sm\" /> : null}\n <div className=\"flex flex-wrap gap-1.5\">\n {/* + New chip */}\n <Button\n type=\"button\"\n variant={isNew ? 'secondary' : 'outline'}\n size=\"sm\"\n className={`h-8 px-3 py-1.5 text-sm ${!isNew ? 'border-dashed' : ''}`}\n onClick={startNewMode}\n >\n <Plus className=\"size-3 mr-1\" />\n {t('ui.perspectives.savedViews.new', 'New')}\n </Button>\n {(perspectives ?? emptyArray).map((p) => {\n const isActive = activePerspectiveId === p.id\n const deleting = deletingIds.includes(p.id)\n const isShared = sharedIds.has(p.id)\n return (\n <ViewChip\n key={p.id}\n id={p.id}\n label={perspectiveLabel(p)}\n kind=\"personal\"\n isActive={isActive}\n disabled={deleting}\n isShared={isShared}\n isRenaming={renamingId === p.id}\n renameValue={renameValue}\n canApplyToRoles={canApplyToRoles}\n deleting={deleting}\n onActivate={() => { onActivatePerspective(p, 'personal'); resetMode() }}\n onRenameValueChange={setRenameValue}\n onRenameConfirm={() => void handleRename(p)}\n onRenameCancel={() => setRenamingId(null)}\n onRenameStart={() => { setRenamingId(p.id); setRenameValue(p.name) }}\n onClone={() => void handleClone(p)}\n onShareStart={() => startShareMode(p)}\n onDelete={() => void handleDelete(p)}\n />\n )\n })}\n {rolePerspectives\n .filter((rp) => {\n const rpName = rp.name.trim()\n return !perspectives.some((pp) => pp.name.trim() === rpName)\n })\n .map((p) => {\n const isActive = activePerspectiveId === p.id\n const clearing = roleClearingIds.includes(p.roleId)\n return (\n <ViewChip\n key={p.id}\n id={p.id}\n label={perspectiveLabel(p)}\n kind=\"role\"\n isActive={isActive}\n disabled={clearing}\n isShared={false}\n isRenaming={renamingId === p.id}\n renameValue={renameValue}\n canApplyToRoles={canApplyToRoles}\n deleting={clearing}\n onActivate={() => { onActivatePerspective(p, 'role'); resetMode() }}\n onRenameValueChange={setRenameValue}\n onRenameConfirm={() => void handleRename(p)}\n onRenameCancel={() => setRenamingId(null)}\n onClone={() => void handleClone(p)}\n onDelete={() => void handleDeleteRole(p)}\n />\n )\n })}\n </div>\n\n {/* Inline form slot \u2014 shared between New and Share modes */}\n {isNew ? (\n <NewViewForm\n name={name}\n onNameChange={setName}\n onSubmit={() => void handleSaveNew()}\n onCancel={resetMode}\n saving={saving}\n />\n ) : null}\n\n {isShare ? (\n <ShareForm\n roles={roles}\n shareRoles={shareRoles}\n shareSetDefault={shareSetDefault}\n onToggleRole={toggleShareRole}\n onToggleSetDefault={setShareSetDefault}\n onApply={() => void handleShareApply()}\n onCancel={resetMode}\n />\n ) : null}\n\n {apiWarning ? (\n <Alert status=\"warning\" className=\"text-xs\">{apiWarning}</Alert>\n ) : null}\n {error ? <div className=\"text-sm text-status-error-text\">{error}</div> : null}\n </section>\n\n {/* Set as default \u2014 separated from chips (border-t) and from columns (ColumnList owns border-t) */}\n <div className=\"flex items-center px-4 mt-2 pt-2 pb-2\">\n <label className=\"inline-flex items-center gap-2 text-sm leading-none\">\n <Checkbox\n checked={isDefault}\n onCheckedChange={(checked) => {\n setIsDefault(checked === true)\n if (isDefaultUserChangeRef.current) {\n scheduleAutosave()\n }\n }}\n />\n {t('ui.perspectives.form.makeDefault', 'Set as default')}\n </label>\n </div>\n\n <ColumnChooserSection\n availableColumns={availableColumns}\n visibleColumnKeys={visibleColumnKeys}\n columnOrder={columnOrder}\n onToggleColumn={handleToggleColumnWithAutosave}\n onReorderColumns={handleReorderColumnsWithAutosave}\n dndContextId=\"perspective-columns\"\n />\n </div>\n </div>\n {ConfirmDialogElement}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -313,7 +313,7 @@ const FieldDefinitionsManager = React.forwardRef(
|
|
|
313
313
|
}
|
|
314
314
|
);
|
|
315
315
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 sm:gap-4", children: [
|
|
316
|
-
statusError ? /* @__PURE__ */ jsxs(Alert, {
|
|
316
|
+
statusError ? /* @__PURE__ */ jsxs(Alert, { status: "error", children: [
|
|
317
317
|
/* @__PURE__ */ jsx(AlertTitle, { children: t("entities.customFields.errors.title", "Something went wrong") }),
|
|
318
318
|
/* @__PURE__ */ jsx(AlertDescription, { children: statusError })
|
|
319
319
|
] }) : null,
|