@open-mercato/ui 0.6.7 → 0.6.8-develop.6874.1.982d6097d8

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.
Files changed (214) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +4 -2
  3. package/agentic/standalone-guide.md +1 -1
  4. package/build.mjs +2 -54
  5. package/dist/ai/AiChat.js +26 -7
  6. package/dist/ai/AiChat.js.map +2 -2
  7. package/dist/ai/LoopDisabledBanner.js +1 -1
  8. package/dist/ai/LoopDisabledBanner.js.map +2 -2
  9. package/dist/ai/parts/ConfirmationCard.js +4 -4
  10. package/dist/ai/parts/ConfirmationCard.js.map +2 -2
  11. package/dist/ai/parts/FieldDiffCard.js +2 -2
  12. package/dist/ai/parts/FieldDiffCard.js.map +2 -2
  13. package/dist/ai/parts/MutationResultCard.js +3 -3
  14. package/dist/ai/parts/MutationResultCard.js.map +2 -2
  15. package/dist/ai/ui-parts/pending-phase3-placeholder.js +1 -1
  16. package/dist/ai/ui-parts/pending-phase3-placeholder.js.map +2 -2
  17. package/dist/backend/AppShell.js +43 -4
  18. package/dist/backend/AppShell.js.map +2 -2
  19. package/dist/backend/AuthSessionGuard.js +14 -0
  20. package/dist/backend/AuthSessionGuard.js.map +2 -2
  21. package/dist/backend/BackendChromeProvider.js +6 -1
  22. package/dist/backend/BackendChromeProvider.js.map +2 -2
  23. package/dist/backend/CrudForm.js +6 -4
  24. package/dist/backend/CrudForm.js.map +2 -2
  25. package/dist/backend/DataTable.js +20 -18
  26. package/dist/backend/DataTable.js.map +2 -2
  27. package/dist/backend/JsonBuilder.js +3 -3
  28. package/dist/backend/JsonBuilder.js.map +2 -2
  29. package/dist/backend/NextStepCallout.js +4 -4
  30. package/dist/backend/NextStepCallout.js.map +1 -1
  31. package/dist/backend/PerspectiveSidebar.js +1 -1
  32. package/dist/backend/PerspectiveSidebar.js.map +2 -2
  33. package/dist/backend/ProfileDropdown.js +2 -1
  34. package/dist/backend/ProfileDropdown.js.map +2 -2
  35. package/dist/backend/RowActions.js +2 -2
  36. package/dist/backend/RowActions.js.map +2 -2
  37. package/dist/backend/confirm-dialog/ConfirmDialog.js +13 -1
  38. package/dist/backend/confirm-dialog/ConfirmDialog.js.map +2 -2
  39. package/dist/backend/crud/useGroupOrder.js +14 -21
  40. package/dist/backend/crud/useGroupOrder.js.map +2 -2
  41. package/dist/backend/custom-fields/FieldDefinitionsManager.js +1 -1
  42. package/dist/backend/custom-fields/FieldDefinitionsManager.js.map +2 -2
  43. package/dist/backend/dashboard/DashboardScreen.js +3 -3
  44. package/dist/backend/dashboard/DashboardScreen.js.map +2 -2
  45. package/dist/backend/detail/AddressTiles.js +4 -5
  46. package/dist/backend/detail/AddressTiles.js.map +2 -2
  47. package/dist/backend/detail/AttachmentDeleteDialog.js +1 -1
  48. package/dist/backend/detail/AttachmentDeleteDialog.js.map +2 -2
  49. package/dist/backend/detail/AttachmentMetadataDialog.js.map +1 -1
  50. package/dist/backend/detail/AttachmentsSection.js +1 -1
  51. package/dist/backend/detail/AttachmentsSection.js.map +2 -2
  52. package/dist/backend/detail/NotesSection.js +1 -1
  53. package/dist/backend/detail/NotesSection.js.map +2 -2
  54. package/dist/backend/detail/TagsSection.js +1 -1
  55. package/dist/backend/detail/TagsSection.js.map +2 -2
  56. package/dist/backend/filters/AdvancedFilterPanel.js +1 -1
  57. package/dist/backend/filters/AdvancedFilterPanel.js.map +2 -2
  58. package/dist/backend/forms/FormHeader.js +1 -2
  59. package/dist/backend/forms/FormHeader.js.map +2 -2
  60. package/dist/backend/icons/lucideRegistry.generated.js +1 -25
  61. package/dist/backend/icons/lucideRegistry.generated.js.map +2 -2
  62. package/dist/backend/icons/lucideRegistry.js +3 -3
  63. package/dist/backend/icons/lucideRegistry.js.map +2 -2
  64. package/dist/backend/icons/lucideRegistryRuntime.js +28 -0
  65. package/dist/backend/icons/lucideRegistryRuntime.js.map +7 -0
  66. package/dist/backend/injection/spotIds.js +23 -18
  67. package/dist/backend/injection/spotIds.js.map +2 -2
  68. package/dist/backend/inputs/ComboboxInput.js.map +1 -1
  69. package/dist/backend/messages/EmailThreadsPanel.js +1 -1
  70. package/dist/backend/messages/EmailThreadsPanel.js.map +2 -2
  71. package/dist/backend/perspectiveState.js +34 -0
  72. package/dist/backend/perspectiveState.js.map +7 -0
  73. package/dist/backend/schedule/ScheduleAgenda.js +3 -3
  74. package/dist/backend/schedule/ScheduleAgenda.js.map +1 -1
  75. package/dist/backend/schedule/ScheduleGrid.js +3 -3
  76. package/dist/backend/schedule/ScheduleGrid.js.map +1 -1
  77. package/dist/backend/schedule/ScheduleToolbar.js +87 -70
  78. package/dist/backend/schedule/ScheduleToolbar.js.map +2 -2
  79. package/dist/backend/sidebar/SidebarCustomizationEditor.js +30 -2
  80. package/dist/backend/sidebar/SidebarCustomizationEditor.js.map +2 -2
  81. package/dist/backend/sidebar/customization-helpers.js +30 -0
  82. package/dist/backend/sidebar/customization-helpers.js.map +2 -2
  83. package/dist/backend/utils/apiCall.js +21 -1
  84. package/dist/backend/utils/apiCall.js.map +2 -2
  85. package/dist/backend/utils/customFieldDefs.js.map +2 -2
  86. package/dist/backend/utils/customFieldForms.js +10 -9
  87. package/dist/backend/utils/customFieldForms.js.map +2 -2
  88. package/dist/backend/utils/fetchAllPages.js +57 -0
  89. package/dist/backend/utils/fetchAllPages.js.map +7 -0
  90. package/dist/backend/utils/nav.js +5 -2
  91. package/dist/backend/utils/nav.js.map +2 -2
  92. package/dist/backend/version-history/VersionHistoryPanel.js +1 -1
  93. package/dist/backend/version-history/VersionHistoryPanel.js.map +2 -2
  94. package/dist/frontend/LanguageSwitcher.js +2 -1
  95. package/dist/frontend/LanguageSwitcher.js.map +2 -2
  96. package/dist/portal/PortalContext.js +1 -1
  97. package/dist/portal/PortalContext.js.map +2 -2
  98. package/dist/portal/hooks/portalBridgeStatus.js +25 -0
  99. package/dist/portal/hooks/portalBridgeStatus.js.map +7 -0
  100. package/dist/portal/hooks/usePortalEventBridge.js +6 -0
  101. package/dist/portal/hooks/usePortalEventBridge.js.map +2 -2
  102. package/dist/portal/hooks/usePortalNotifications.js +42 -4
  103. package/dist/portal/hooks/usePortalNotifications.js.map +2 -2
  104. package/dist/portal/utils/nav.js +2 -3
  105. package/dist/portal/utils/nav.js.map +2 -2
  106. package/dist/primitives/ErrorNotice.js +1 -1
  107. package/dist/primitives/ErrorNotice.js.map +2 -2
  108. package/dist/primitives/Notice.js +1 -1
  109. package/dist/primitives/Notice.js.map +1 -1
  110. package/dist/primitives/button.js +9 -4
  111. package/dist/primitives/button.js.map +2 -2
  112. package/dist/theme/ThemeProvider.js +1 -1
  113. package/dist/theme/ThemeProvider.js.map +2 -2
  114. package/dist/theme/index.js +3 -0
  115. package/dist/theme/index.js.map +2 -2
  116. package/dist/theme/theme-init-script.js +17 -0
  117. package/dist/theme/theme-init-script.js.map +7 -0
  118. package/figma/alert.figma.tsx +35 -0
  119. package/figma/badge.figma.tsx +33 -0
  120. package/figma/button.figma.tsx +33 -0
  121. package/figma/checkbox.figma.tsx +28 -0
  122. package/figma/drawer.figma.tsx +40 -0
  123. package/figma/input-variants.figma.tsx +58 -0
  124. package/figma/radio.figma.tsx +16 -0
  125. package/figma/select.figma.tsx +24 -0
  126. package/figma/status-badge.figma.tsx +24 -0
  127. package/figma/switch.figma.tsx +28 -0
  128. package/figma/tabs.figma.tsx +22 -0
  129. package/figma/tag.figma.tsx +27 -0
  130. package/figma.config.json +6 -0
  131. package/jest.config.cjs +2 -1
  132. package/package.json +21 -18
  133. package/scripts/lucideRegistrySource.cjs +133 -0
  134. package/src/ai/AiChat.tsx +36 -7
  135. package/src/ai/LoopDisabledBanner.tsx +1 -1
  136. package/src/ai/__tests__/AiChat.test.tsx +31 -0
  137. package/src/ai/parts/ConfirmationCard.tsx +4 -4
  138. package/src/ai/parts/FieldDiffCard.tsx +2 -2
  139. package/src/ai/parts/MutationResultCard.tsx +3 -3
  140. package/src/ai/ui-parts/pending-phase3-placeholder.tsx +2 -2
  141. package/src/backend/AppShell.tsx +55 -4
  142. package/src/backend/AuthSessionGuard.tsx +29 -0
  143. package/src/backend/BackendChromeProvider.tsx +19 -1
  144. package/src/backend/CrudForm.tsx +18 -4
  145. package/src/backend/DataTable.tsx +36 -19
  146. package/src/backend/JsonBuilder.tsx +3 -3
  147. package/src/backend/NextStepCallout.tsx +4 -4
  148. package/src/backend/PerspectiveSidebar.tsx +1 -1
  149. package/src/backend/ProfileDropdown.tsx +1 -0
  150. package/src/backend/RowActions.tsx +2 -2
  151. package/src/backend/__tests__/AppShell.test.tsx +65 -7
  152. package/src/backend/__tests__/AuthSessionGuard.test.tsx +52 -0
  153. package/src/backend/__tests__/BackendChromeProvider.test.tsx +98 -2
  154. package/src/backend/__tests__/CrudForm.fieldsetBinding.test.tsx +158 -0
  155. package/src/backend/__tests__/CrudForm.groupInjectionColumn.test.tsx +98 -0
  156. package/src/backend/__tests__/DataTable.metaHiddenLateColumns.test.tsx +105 -0
  157. package/src/backend/__tests__/DataTable.perspectiveStorage.test.ts +62 -1
  158. package/src/backend/__tests__/nav-utils.test.ts +139 -0
  159. package/src/backend/confirm-dialog/ConfirmDialog.tsx +16 -1
  160. package/src/backend/confirm-dialog/__tests__/ConfirmDialog.test.tsx +68 -1
  161. package/src/backend/crud/__tests__/useGroupOrder.test.ts +39 -1
  162. package/src/backend/crud/useGroupOrder.ts +30 -25
  163. package/src/backend/custom-fields/FieldDefinitionsManager.tsx +1 -1
  164. package/src/backend/dashboard/DashboardScreen.tsx +3 -3
  165. package/src/backend/detail/AddressTiles.tsx +3 -6
  166. package/src/backend/detail/AttachmentDeleteDialog.tsx +1 -1
  167. package/src/backend/detail/AttachmentMetadataDialog.tsx +1 -1
  168. package/src/backend/detail/AttachmentsSection.tsx +1 -1
  169. package/src/backend/detail/NotesSection.tsx +1 -1
  170. package/src/backend/detail/TagsSection.tsx +1 -1
  171. package/src/backend/detail/__tests__/AttachmentAssignmentsEditor.test.tsx +21 -0
  172. package/src/backend/filters/AdvancedFilterPanel.tsx +1 -1
  173. package/src/backend/forms/FormHeader.tsx +1 -2
  174. package/src/backend/icons/__tests__/lucideRegistryGenerator.test.ts +177 -0
  175. package/src/backend/icons/lucideRegistry.generated.tsx +3 -37
  176. package/src/backend/icons/lucideRegistry.ts +2 -2
  177. package/src/backend/icons/lucideRegistryRuntime.tsx +36 -0
  178. package/src/backend/injection/__tests__/spotIds.extension-points.test.ts +38 -0
  179. package/src/backend/injection/spotIds.ts +23 -18
  180. package/src/backend/inputs/ComboboxInput.tsx +1 -1
  181. package/src/backend/messages/EmailThreadsPanel.tsx +1 -1
  182. package/src/backend/perspectiveState.ts +44 -0
  183. package/src/backend/schedule/ScheduleAgenda.tsx +3 -3
  184. package/src/backend/schedule/ScheduleGrid.tsx +3 -3
  185. package/src/backend/schedule/ScheduleToolbar.tsx +89 -68
  186. package/src/backend/sidebar/SidebarCustomizationEditor.tsx +42 -3
  187. package/src/backend/sidebar/__tests__/group-visibility.test.ts +131 -0
  188. package/src/backend/sidebar/customization-helpers.ts +47 -0
  189. package/src/backend/utils/__tests__/apiCall.test.ts +61 -0
  190. package/src/backend/utils/__tests__/customFieldForms.optionsPaging.test.ts +95 -0
  191. package/src/backend/utils/__tests__/fetchAllPages.test.ts +103 -0
  192. package/src/backend/utils/apiCall.ts +29 -1
  193. package/src/backend/utils/customFieldDefs.ts +1 -0
  194. package/src/backend/utils/customFieldForms.ts +15 -11
  195. package/src/backend/utils/fetchAllPages.ts +93 -0
  196. package/src/backend/utils/nav.ts +22 -2
  197. package/src/backend/version-history/VersionHistoryPanel.tsx +1 -1
  198. package/src/frontend/LanguageSwitcher.tsx +1 -0
  199. package/src/portal/PortalContext.tsx +1 -1
  200. package/src/portal/hooks/__tests__/usePortalEventBridge.test.tsx +66 -0
  201. package/src/portal/hooks/__tests__/usePortalNotifications.test.tsx +155 -0
  202. package/src/portal/hooks/portalBridgeStatus.ts +29 -0
  203. package/src/portal/hooks/usePortalEventBridge.ts +6 -0
  204. package/src/portal/hooks/usePortalNotifications.ts +55 -12
  205. package/src/portal/utils/__tests__/nav.test.ts +2 -2
  206. package/src/portal/utils/nav.ts +3 -4
  207. package/src/primitives/ErrorNotice.tsx +1 -1
  208. package/src/primitives/Notice.tsx +5 -5
  209. package/src/primitives/__tests__/button.test.tsx +70 -0
  210. package/src/primitives/button.tsx +10 -4
  211. package/src/theme/ThemeProvider.tsx +1 -2
  212. package/src/theme/__tests__/theme-init-script.test.ts +70 -0
  213. package/src/theme/index.ts +1 -0
  214. package/src/theme/theme-init-script.ts +23 -0
@@ -125,7 +125,7 @@ function ConfirmationErrorAlert({
125
125
  return /* @__PURE__ */ jsxs(
126
126
  Alert,
127
127
  {
128
- variant: "warning",
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
- variant: "warning",
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
- variant: "warning",
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, { variant: "destructive", className: "mt-3", "data-ai-confirmation-error": code, children: [
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, { variant: "info", children: /* @__PURE__ */ jsx(AlertDescription, { children: t(
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, { variant: "info", "data-ai-field-diff-mode": "empty", children: /* @__PURE__ */ jsx(AlertDescription, { children: t(
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 variant=\"info\">\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 variant=\"info\" 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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",
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, { variant: "warning", "data-ai-mutation-result": "partial", children: [
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, { variant: "success", "data-ai-mutation-result": "success", children: [
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, { variant: "destructive", "data-ai-mutation-result": "failure", children: [
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,SAAQ,WAAU,2BAAwB,WAC/C;AAAA,0BAAC,cACE;AAAA,QACC;AAAA,QACA;AAAA,MACF,GACF;AAAA,MACA,qBAAC,SAAI,WAAU,2BACb;AAAA,4BAAC,OACE;AAAA,UACC;AAAA,UACA;AAAA,QACF,GACF;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,mCAA+B;AAAA,YAE9B,wBAAc,IAAI,CAAC,WAClB,qBAAC,QAAyB,kCAAgC,OAAO,UAC/D;AAAA,kCAAC,UAAK,WAAU,aAAa,iBAAO,UAAS;AAAA,cAC7C,oBAAC,UAAK,WAAU,8BAA6B,oBAAC;AAAA,cAC9C,oBAAC,UAAK,WAAU,aAAa,iBAAO,MAAM,MAAK;AAAA,cAC/C,oBAAC,UAAK,WAAU,8BAA6B,oBAAC;AAAA,cAC9C,oBAAC,UAAM,iBAAO,MAAM,SAAQ;AAAA,iBALrB,OAAO,QAMhB,CACD;AAAA;AAAA,QACH;AAAA,SACF;AAAA,OACF;AAAA,EAEJ;AAEA,MAAI,gBAAgB,MAAM,GAAG;AAC3B,UAAM,WAAW,QAAQ,YAAY,OAAO,kBAAkB;AAC9D,UAAM,OAAO,QAAQ,cAAc;AACnC,WACE,qBAAC,SAAM,SAAQ,WAAU,2BAAwB,WAC/C;AAAA,0BAAC,cACE,YAAE,wDAAwD,gBAAgB,GAC7E;AAAA,MACA,qBAAC,SAAI,WAAU,2BACb;AAAA,4BAAC,OACE;AAAA,UACC;AAAA,UACA;AAAA,QACF,GACF;AAAA,QACC,YAAY,OACX,oBAAC,OAAE,WAAU,gBACX;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV;AAAA,YACA,gCAA4B;AAAA,YAE3B;AAAA,cACC;AAAA,cACA;AAAA,YACF;AAAA;AAAA,QACF,GACF,IACE;AAAA,SACN;AAAA,OACF;AAAA,EAEJ;AAQA,MAAI,gBAAgB,MAAM,KAAK,QAAQ,OAAO;AAC5C,UAAM,OAAO,QAAQ,OAAO,QAAQ,UAAU;AAC9C,UAAM,UACJ,QAAQ,OAAO,WACf;AAAA,MACE;AAAA,MACA;AAAA,IACF;AACF,UAAM,WAAW,QAAQ;AACzB,UAAM,eAAe,UAAU;AAC/B,UAAM,aAAa,UAAU;AAC7B,UAAM,YAAY,UAAU;AAC5B,UAAM,cAAc,MAAM;AAexB,YAAM,cAAwB;AAAA,QAC5B,8BAA8B,OAAO,QAAQ;AAAA,QAC7C,UAAU,IAAI,WAAM,OAAO;AAAA,MAC7B;AACA,UAAI,aAAa,cAAc,SAAS;AACtC,oBAAY,KAAK,gBAAgB,SAAS,GAAG;AAAA,MAC/C;AACA,UAAI,OAAO,oBAAoB,OAAO,gBAAgB;AACpD,oBAAY;AAAA,UACV,WAAW,OAAO,oBAAoB,GAAG,GAAG,OAAO,iBAAiB,QAAQ,OAAO,iBAAiB,EAAE;AAAA,QACxG;AAAA,MACF;AAKA,YAAM,SAAS,cAAc,UAAU,CAAC;AACxC,UAAI,MAAM,QAAQ,MAAM,KAAK,OAAO,SAAS,GAAG;AAC9C,oBAAY,KAAK,IAAI,oBAAoB;AACzC,mBAAW,SAAS,QAAQ;AAC1B,gBAAM,OAAO,MAAM,QAAQ,OAAO,IAAI,KAAK,MAAM,KAAK,SAAS,IAC3D,MAAM,KAAK,KAAK,GAAG,IACnB;AACJ,gBAAM,MAAM,OAAO,WAAW;AAC9B,gBAAM,WAAW,OAAO,OAAO,KAAK,MAAM,IAAI,MAAM;AACpD,gBAAM,UACJ,OAAO,YAAY,OAAO,WACtB,cAAc,OAAO,YAAY,GAAG,SAAS,OAAO,YAAY,GAAG,MACnE;AACN,sBAAY,KAAK,KAAK,IAAI,KAAK,GAAG,GAAG,QAAQ,GAAG,OAAO,EAAE;AAAA,QAC3D;AAAA,MACF,WAAW,cAAc,eAAe,OAAO,aAAa,gBAAgB,UAAU;AACpF,cAAM,UAAU,OAAO,QAAQ,aAAa,WAAW;AACvD,YAAI,QAAQ,SAAS,GAAG;AACtB,sBAAY,KAAK,IAAI,eAAe;AACpC,qBAAW,CAAC,MAAM,IAAI,KAAK,SAAS;AAClC,kBAAM,OAAO,MAAM,QAAQ,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,IAAI;AAChE,wBAAY,KAAK,KAAK,IAAI,KAAK,IAAI,EAAE;AAAA,UACvC;AAAA,QACF;AAAA,MACF;AAMA,UAAI,eAAe,QAAW;AAC5B,YAAI;AACF,gBAAM,OAAO,KAAK,UAAU,YAAY,MAAM,CAAC;AAC/C,cAAI,QAAQ,SAAS,QAAQ,KAAK,UAAU,KAAM;AAChD,wBAAY,KAAK,IAAI,uBAAuB,WAAW,MAAM,KAAK;AAAA,UACpE,WAAW,QAAQ,KAAK,SAAS,KAAM;AACrC,wBAAY;AAAA,cACV;AAAA,cACA;AAAA,cACA;AAAA,cACA,KAAK,MAAM,GAAG,GAAI,IAAI;AAAA,cACtB;AAAA,YACF;AAAA,UACF;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AAGA,UAAI,cAAc,UAAU,QAAW;AACrC,YAAI;AACF,gBAAM,YAAY,KAAK,UAAU,aAAa,OAAO,MAAM,CAAC;AAC5D,cAAI,aAAa,cAAc,QAAQ,UAAU,UAAU,MAAM;AAC/D,wBAAY,KAAK,IAAI,qBAAqB,WAAW,WAAW,KAAK;AAAA,UACvE;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AAIA,UAAI,iBAAiB,cAAc,SAAS,GAAG;AAC7C,oBAAY,KAAK,IAAI,sBAAsB;AAC3C,mBAAW,OAAO,eAAe;AAC/B,sBAAY;AAAA,YACV,KAAK,IAAI,QAAQ,WAAM,IAAI,MAAM,IAAI,KAAK,IAAI,MAAM,OAAO;AAAA,UAC7D;AAAA,QACF;AAAA,MACF;AAEA,kBAAY;AAAA,QACV;AAAA,QACA;AAAA,MACF;AACA,yBAAmB;AAAA,QACjB,SAAS,YAAY,KAAK,IAAI;AAAA,QAC9B,UAAU,OAAO;AAAA,QACjB,iBAAiB,OAAO;AAAA,MAC1B,CAAC;AAAA,IACH;AACA,UAAM,gBAAgB,MAAM,QAAQ,cAAc,MAAM,IACpD,aAAc,OAAQ,OAAO,CAAC,UAAU,UAAU,MAAM,WAAW,MAAM,KAAK,IAC9E,CAAC;AACL,WACE,qBAAC,SAAM,SAAQ,eAAc,2BAAwB,WACnD;AAAA,0BAAC,cACE;AAAA,QACC;AAAA,QACA;AAAA,MACF,GACF;AAAA,MACA,qBAAC,SAAI,WAAU,2BACb;AAAA,6BAAC,SACC;AAAA,8BAAC,UAAK,WAAU,0BAAyB,gCAA4B,MAClE,gBACH;AAAA,UACA,oBAAC,UAAM,mBAAQ;AAAA,WACjB;AAAA,QACC,cAAc,SAAS,IACtB;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,kCAA8B;AAAA,YAE7B,wBAAc,IAAI,CAAC,OAAO,UAAU;AACnC,oBAAM,OACJ,MAAM,QAAQ,OAAO,IAAI,KAAK,MAAM,KAAK,SAAS,IAC9C,MAAM,KAAK,KAAK,GAAG,IACnB;AACN,qBACE,qBAAC,QACE;AAAA,uBACC,oBAAC,UAAK,WAAU,aAAa,gBAAK,IAChC;AAAA,gBACH,QAAQ,OAAO,UAAU,oBAAC,UAAK,WAAU,QAAO,oBAAC,IAAU;AAAA,gBAC3D,OAAO,UAAU,oBAAC,UAAM,gBAAM,SAAQ,IAAU;AAAA,mBAL1C,KAMT;AAAA,YAEJ,CAAC;AAAA;AAAA,QACH,IACE;AAAA,QACJ,oBAAC,SAAI,WAAU,gCACb;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAQ;AAAA,YACR,MAAK;AAAA,YACL,SAAS;AAAA,YACT,+BAA2B;AAAA,YAE3B;AAAA,kCAAC,SAAM,WAAU,UAAS,eAAW,MAAC;AAAA,cACtC,oBAAC,UACE;AAAA,gBACC;AAAA,gBACA;AAAA,cACF,GACF;AAAA;AAAA;AAAA,QACF,GACF;AAAA,SACF;AAAA,OACF;AAAA,EAEJ;AAEA,SAAO;AACT;AAEA,IAAO,6BAAQ;",
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
  }
@@ -7,7 +7,7 @@ function PendingPhase3Placeholder({ componentId }) {
7
7
  return /* @__PURE__ */ jsxs(
8
8
  Alert,
9
9
  {
10
- variant: "info",
10
+ status: "information",
11
11
  "data-ai-ui-part-pending-phase3": componentId,
12
12
  children: [
13
13
  /* @__PURE__ */ jsx(AlertTitle, { children: t(
@@ -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 `variant=\"info\"` \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 variant=\"info\"\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,SAAQ;AAAA,MACR,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;",
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
  }
@@ -87,6 +87,45 @@ function shouldBypassLogoOptimization(src) {
87
87
  const value = src ?? "";
88
88
  return /^https?:\/\//.test(value) || /^\/api\/attachments\/(?:image|file)\//.test(value);
89
89
  }
90
+ function ShellBrandLogo({
91
+ logo,
92
+ brandName,
93
+ unoptimized,
94
+ compact = false,
95
+ mobile = false
96
+ }) {
97
+ const src = logo?.src ?? "/open-mercato.svg";
98
+ const alt = logo?.alt ?? brandName;
99
+ const isCustomLogo = Boolean(logo?.src);
100
+ const preserveAspectRatio = Boolean(logo?.preserveAspectRatio);
101
+ if (!isCustomLogo || !preserveAspectRatio) {
102
+ return /* @__PURE__ */ jsx(
103
+ Image,
104
+ {
105
+ src,
106
+ alt,
107
+ width: mobile ? 28 : 40,
108
+ height: mobile ? 28 : 40,
109
+ className: `${mobile ? "rounded" : "rounded-full"} shrink-0 object-cover`,
110
+ unoptimized: unoptimized ? true : void 0
111
+ }
112
+ );
113
+ }
114
+ const width = compact ? 40 : mobile ? 96 : 120;
115
+ const height = mobile ? 28 : 40;
116
+ const className = compact ? "h-10 max-w-10 w-auto shrink-0 object-contain" : mobile ? "h-7 max-w-24 w-auto shrink-0 object-contain" : "h-10 max-w-[120px] w-auto shrink-0 object-contain";
117
+ return /* @__PURE__ */ jsx(
118
+ Image,
119
+ {
120
+ src,
121
+ alt,
122
+ width,
123
+ height,
124
+ className,
125
+ unoptimized: unoptimized ? true : void 0
126
+ }
127
+ );
128
+ }
90
129
  function mergeSidebarItemsWithInjected(items, injectedItems, t) {
91
130
  if (injectedItems.length === 0) return items;
92
131
  const builtInById = /* @__PURE__ */ new Map();
@@ -514,7 +553,7 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
514
553
  className: `flex items-center gap-3 rounded-xl transition-colors hover:bg-muted ${compact ? "p-2 justify-center" : "p-3"}`,
515
554
  "aria-label": t("appShell.goToDashboard"),
516
555
  children: [
517
- /* @__PURE__ */ jsx(Image, { src: resolvedLogo?.src ?? "/open-mercato.svg", alt: resolvedLogo?.alt ?? resolvedBrandName, width: 40, height: 40, className: "rounded-full shrink-0", unoptimized: resolvedLogoBypassesOptimization ? true : void 0 }),
556
+ /* @__PURE__ */ jsx(ShellBrandLogo, { logo: resolvedLogo, brandName: resolvedBrandName, compact, unoptimized: resolvedLogoBypassesOptimization }),
518
557
  !compact && /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium text-foreground", children: resolvedBrandName })
519
558
  ]
520
559
  }
@@ -617,7 +656,7 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
617
656
  className: `flex items-center gap-3 rounded-xl transition-colors hover:bg-muted ${compact ? "p-2 justify-center" : "p-3"}`,
618
657
  "aria-label": t("appShell.goToDashboard"),
619
658
  children: [
620
- /* @__PURE__ */ jsx(Image, { src: resolvedLogo?.src ?? "/open-mercato.svg", alt: resolvedLogo?.alt ?? resolvedBrandName, width: 40, height: 40, className: "rounded-full shrink-0", unoptimized: resolvedLogoBypassesOptimization ? true : void 0 }),
659
+ /* @__PURE__ */ jsx(ShellBrandLogo, { logo: resolvedLogo, brandName: resolvedBrandName, compact, unoptimized: resolvedLogoBypassesOptimization }),
621
660
  !compact && /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium text-foreground", children: resolvedBrandName })
622
661
  ]
623
662
  }
@@ -677,7 +716,7 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
677
716
  className: `flex items-center gap-3 rounded-xl transition-colors hover:bg-muted ${compact ? "p-2 justify-center" : "p-3"}`,
678
717
  "aria-label": t("appShell.goToDashboard"),
679
718
  children: [
680
- /* @__PURE__ */ jsx(Image, { src: resolvedLogo?.src ?? "/open-mercato.svg", alt: resolvedLogo?.alt ?? resolvedBrandName, width: 40, height: 40, className: "rounded-full shrink-0", unoptimized: resolvedLogoBypassesOptimization ? true : void 0 }),
719
+ /* @__PURE__ */ jsx(ShellBrandLogo, { logo: resolvedLogo, brandName: resolvedBrandName, compact, unoptimized: resolvedLogoBypassesOptimization }),
681
720
  !compact && /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium text-foreground", children: resolvedBrandName })
682
721
  ]
683
722
  }
@@ -1080,7 +1119,7 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
1080
1119
  /* @__PURE__ */ jsxs("aside", { className: "absolute left-0 top-0 flex h-full w-[280px] max-w-[85vw] flex-col bg-background border-r shadow-lg overflow-hidden", children: [
1081
1120
  /* @__PURE__ */ jsxs("div", { className: "shrink-0 flex items-center justify-between gap-2 border-b px-4 py-3", children: [
1082
1121
  /* @__PURE__ */ jsxs(Link, { href: "/backend", className: "flex items-center gap-2 min-w-0 text-sm font-semibold", onClick: () => setMobileOpen(false), "aria-label": t("appShell.goToDashboard"), children: [
1083
- /* @__PURE__ */ jsx(Image, { src: resolvedLogo?.src ?? "/open-mercato.svg", alt: resolvedLogo?.alt ?? resolvedBrandName, width: 28, height: 28, className: "rounded shrink-0", unoptimized: resolvedLogoBypassesOptimization ? true : void 0 }),
1122
+ /* @__PURE__ */ jsx(ShellBrandLogo, { logo: resolvedLogo, brandName: resolvedBrandName, mobile: true, unoptimized: resolvedLogoBypassesOptimization }),
1084
1123
  /* @__PURE__ */ jsx("span", { className: "truncate", children: resolvedBrandName })
1085
1124
  ] }),
1086
1125
  /* @__PURE__ */ jsx(IconButton, { variant: "ghost", size: "sm", onClick: () => setMobileOpen(false), "aria-label": t("appShell.closeMenu"), children: /* @__PURE__ */ jsx(X, { className: "size-4" }) })