@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
@@ -82,6 +82,18 @@ function ConfirmDialog({
82
82
  dialog.addEventListener("cancel", handleCancel2);
83
83
  return () => dialog.removeEventListener("cancel", handleCancel2);
84
84
  }, [loading, requestClose]);
85
+ React.useEffect(() => {
86
+ if (!open) return;
87
+ const handleEscape = (event) => {
88
+ if (event.key !== "Escape") return;
89
+ if (!(event.target instanceof Node) || !dialogRef.current?.contains(event.target)) return;
90
+ event.preventDefault();
91
+ event.stopPropagation();
92
+ if (!loading) requestClose();
93
+ };
94
+ window.addEventListener("keydown", handleEscape, true);
95
+ return () => window.removeEventListener("keydown", handleEscape, true);
96
+ }, [loading, open, requestClose]);
85
97
  const handleBackdropClick = (e) => {
86
98
  if (e.target === dialogRef.current && !loading) {
87
99
  requestClose();
@@ -196,7 +208,7 @@ function ConfirmDialog({
196
208
  Button,
197
209
  {
198
210
  ref: confirmButtonRef,
199
- variant: variant === "destructive" ? "destructive" : "default",
211
+ variant: variant === "destructive" ? "destructive-solid" : "default",
200
212
  onClick: handleConfirm,
201
213
  disabled: loading,
202
214
  className: "w-full sm:w-auto",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/confirm-dialog/ConfirmDialog.tsx"],
4
- "sourcesContent": ["\"use client\";\nimport * as React from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { useT } from \"@open-mercato/shared/lib/i18n/context\";\nimport { Button } from \"@open-mercato/ui/primitives/button\";\nimport { IconButton } from \"../../primitives/icon-button\";\nimport { cn } from \"@open-mercato/shared/lib/utils\";\nimport { Loader2, X } from \"lucide-react\";\n\nexport type ConfirmDialogProps = {\n /** Whether the dialog is open (controlled mode \u2014 used by useConfirmDialog) */\n open?: boolean;\n /** Callback when open state changes (controlled mode) */\n onOpenChange?: (open: boolean) => void;\n /** Callback when user confirms */\n onConfirm: () => void | Promise<void>;\n /** Callback when user cancels (optional, defaults to closing) */\n onCancel?: () => void;\n /** Dialog title. Defaults to i18n key \"ui.dialogs.confirm.defaultTitle\" (\"Are you sure?\") */\n title?: string;\n /** Dialog body text / description */\n text?: string;\n /** Confirm button label. Defaults to i18n \"ui.dialogs.confirm.confirmText\" (\"Confirm\").\n * Pass `false` to hide the confirm button entirely. */\n confirmText?: string | false;\n /** Cancel button label. Defaults to i18n \"ui.dialogs.confirm.cancelText\" (\"Cancel\").\n * Pass `false` to hide the cancel button entirely. */\n cancelText?: string | false;\n /** Visual variant \u2014 \"destructive\" renders the confirm button in red */\n variant?: \"default\" | \"destructive\";\n /** Whether the confirm button shows a loading spinner.\n * Useful for async onConfirm handlers (e.g., waiting for API response before closing). */\n loading?: boolean;\n /** Trigger element \u2014 when provided, component manages its own open state (declarative mode).\n * Clicking the trigger opens the dialog. */\n trigger?: React.ReactNode;\n};\n\nexport function ConfirmDialog({\n open: controlledOpen,\n onOpenChange,\n onConfirm,\n onCancel,\n title,\n text,\n confirmText,\n cancelText,\n variant = \"default\",\n loading = false,\n trigger,\n}: ConfirmDialogProps) {\n const t = useT();\n const dialogRef = React.useRef<HTMLDialogElement>(null);\n const [internalOpen, setInternalOpen] = React.useState(false);\n const cancelButtonRef = React.useRef<HTMLButtonElement>(null);\n const confirmButtonRef = React.useRef<HTMLButtonElement>(null);\n const [portalTarget, setPortalTarget] = React.useState<HTMLElement | null>(null);\n // Unique IDs so multiple ConfirmDialog instances on the same page don't\n // collide and make `aria-labelledby` resolve to the wrong dialog's title.\n const reactId = React.useId();\n const titleId = `confirm-dialog-title-${reactId}`;\n const descriptionId = `confirm-dialog-description-${reactId}`;\n\n // Determine if we're in controlled mode (open prop provided) or declarative mode (trigger provided)\n const isControlled = controlledOpen !== undefined;\n const open = isControlled ? controlledOpen : internalOpen;\n const setOpen = isControlled\n ? onOpenChange || (() => {})\n : setInternalOpen;\n const handleCancelCallback = React.useCallback(() => {\n if (!isControlled) {\n onCancel?.();\n }\n }, [isControlled, onCancel]);\n\n // Default text values from i18n\n const resolvedTitle =\n title ?? t(\"ui.dialogs.confirm.defaultTitle\", \"Are you sure?\");\n const resolvedConfirmText =\n confirmText === false\n ? false\n : confirmText ?? t(\"ui.dialogs.confirm.confirmText\", \"Confirm\");\n const resolvedCancelText =\n cancelText === false\n ? false\n : cancelText ?? t(\"ui.dialogs.confirm.cancelText\", \"Cancel\");\n const closeAriaLabel = t(\"ui.dialog.close.ariaLabel\", \"Close\");\n const requestClose = React.useCallback(() => {\n setOpen(false);\n handleCancelCallback();\n }, [setOpen, handleCancelCallback]);\n\n React.useLayoutEffect(() => {\n setPortalTarget(document.body);\n }, []);\n\n // Handle dialog open/close with native showModal/close\n React.useLayoutEffect(() => {\n const dialog = dialogRef.current;\n if (!dialog) return;\n\n if (open) {\n if (!dialog.open) {\n dialog.showModal();\n // Focus cancel button (safe default) or confirm if no cancel\n setTimeout(() => {\n if (resolvedCancelText !== false && cancelButtonRef.current) {\n cancelButtonRef.current.focus();\n } else if (confirmButtonRef.current) {\n confirmButtonRef.current.focus();\n }\n }, 0);\n }\n } else {\n if (dialog.open) {\n dialog.close();\n }\n }\n }, [open, resolvedCancelText, portalTarget]);\n\n // Handle native cancel event (Escape key)\n React.useEffect(() => {\n const dialog = dialogRef.current;\n if (!dialog) return;\n\n const handleCancel = (e: Event) => {\n // Prevent close if loading\n if (loading) {\n e.preventDefault();\n return;\n }\n requestClose();\n };\n\n dialog.addEventListener(\"cancel\", handleCancel);\n return () => dialog.removeEventListener(\"cancel\", handleCancel);\n }, [loading, requestClose]);\n\n // Handle backdrop click\n const handleBackdropClick = (e: React.MouseEvent<HTMLDialogElement>) => {\n // Only close if clicking directly on the dialog (backdrop), not its children\n if (e.target === dialogRef.current && !loading) {\n requestClose();\n }\n };\n\n // Handle keyboard shortcuts\n React.useEffect(() => {\n if (!open) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n // Cmd/Ctrl+Enter confirms\n if ((e.metaKey || e.ctrlKey) && e.key === \"Enter\" && !loading) {\n e.preventDefault();\n handleConfirm();\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, [open, loading]);\n\n const handleConfirm = async () => {\n await onConfirm();\n // Don't auto-close if loading \u2014 let the parent control when to close\n if (!loading) {\n setOpen(false);\n }\n };\n\n const handleCancel = () => {\n requestClose();\n };\n\n const handleTriggerClick = () => {\n if (trigger) {\n setOpen(true);\n }\n };\n\n const dialogElement = (\n <dialog\n ref={dialogRef}\n role=\"alertdialog\"\n aria-labelledby={titleId}\n aria-describedby={text ? descriptionId : undefined}\n onClick={handleBackdropClick}\n className={cn(\n // Reset dialog defaults\n \"m-0 p-0 max-w-none bg-transparent border-none pointer-events-auto\",\n // Backdrop styling\n \"backdrop:bg-black/50 backdrop:backdrop-blur-sm backdrop:transition-opacity\",\n // Mobile: bottom sheet\n \"fixed inset-x-0 bottom-0 top-auto w-full\",\n // Desktop: centered\n \"sm:inset-auto sm:mx-auto sm:my-auto sm:max-w-md sm:top-1/2 sm:left-1/2 sm:-translate-x-1/2 sm:-translate-y-1/2\",\n // Animation with reduced motion support\n \"motion-safe:open:animate-in motion-safe:open:fade-in-0 motion-safe:open:slide-in-from-bottom-4\",\n \"sm:motion-safe:open:slide-in-from-bottom-0 sm:motion-safe:open:zoom-in-95\",\n // Duration\n \"motion-safe:open:duration-300\"\n )}\n >\n <div\n role=\"document\"\n className={cn(\n // Panel container\n \"flex flex-col gap-4 rounded-t-2xl border-t bg-card p-6 text-foreground shadow-lg\",\n \"sm:rounded-xl sm:border\",\n // Relative positioning for close button\n \"relative\"\n )}\n >\n {/* Close button */}\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={handleCancel}\n disabled={loading}\n aria-label={closeAriaLabel}\n className=\"absolute right-4 top-4 opacity-70 hover:opacity-100\"\n >\n <X className=\"h-4 w-4\" />\n </IconButton>\n\n {/* Title */}\n <h2\n id={titleId}\n className={cn(\n \"text-sm font-medium leading-snug tracking-tight pr-6\",\n // Mobile: centered, Desktop: left-aligned\n \"text-center sm:text-left\"\n )}\n >\n {resolvedTitle}\n </h2>\n\n {/* Description (optional) */}\n {text && (\n <p\n id={descriptionId}\n className=\"text-sm font-medium leading-snug text-muted-foreground\"\n >\n {text}\n </p>\n )}\n\n {/* Actions */}\n <div className=\"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\">\n {resolvedCancelText !== false && (\n <Button\n ref={cancelButtonRef}\n variant=\"outline\"\n onClick={handleCancel}\n disabled={loading}\n className=\"w-full sm:w-auto\"\n >\n {resolvedCancelText}\n </Button>\n )}\n {resolvedConfirmText !== false && (\n <Button\n ref={confirmButtonRef}\n variant={variant === \"destructive\" ? \"destructive\" : \"default\"}\n onClick={handleConfirm}\n disabled={loading}\n className=\"w-full sm:w-auto\"\n >\n {loading && <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />}\n {resolvedConfirmText}\n </Button>\n )}\n </div>\n </div>\n </dialog>\n );\n\n return (\n <>\n {trigger && (\n <div onClick={handleTriggerClick} className=\"inline-block\" role=\"presentation\">\n {trigger}\n </div>\n )}\n\n {portalTarget ? createPortal(dialogElement, portalTarget) : null}\n </>\n );\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\";\nimport * as React from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { useT } from \"@open-mercato/shared/lib/i18n/context\";\nimport { Button } from \"@open-mercato/ui/primitives/button\";\nimport { IconButton } from \"../../primitives/icon-button\";\nimport { cn } from \"@open-mercato/shared/lib/utils\";\nimport { Loader2, X } from \"lucide-react\";\n\nexport type ConfirmDialogProps = {\n /** Whether the dialog is open (controlled mode \u2014 used by useConfirmDialog) */\n open?: boolean;\n /** Callback when open state changes (controlled mode) */\n onOpenChange?: (open: boolean) => void;\n /** Callback when user confirms */\n onConfirm: () => void | Promise<void>;\n /** Callback when user cancels (optional, defaults to closing) */\n onCancel?: () => void;\n /** Dialog title. Defaults to i18n key \"ui.dialogs.confirm.defaultTitle\" (\"Are you sure?\") */\n title?: string;\n /** Dialog body text / description */\n text?: string;\n /** Confirm button label. Defaults to i18n \"ui.dialogs.confirm.confirmText\" (\"Confirm\").\n * Pass `false` to hide the confirm button entirely. */\n confirmText?: string | false;\n /** Cancel button label. Defaults to i18n \"ui.dialogs.confirm.cancelText\" (\"Cancel\").\n * Pass `false` to hide the cancel button entirely. */\n cancelText?: string | false;\n /** Visual variant \u2014 \"destructive\" renders the confirm button in red */\n variant?: \"default\" | \"destructive\";\n /** Whether the confirm button shows a loading spinner.\n * Useful for async onConfirm handlers (e.g., waiting for API response before closing). */\n loading?: boolean;\n /** Trigger element \u2014 when provided, component manages its own open state (declarative mode).\n * Clicking the trigger opens the dialog. */\n trigger?: React.ReactNode;\n};\n\nexport function ConfirmDialog({\n open: controlledOpen,\n onOpenChange,\n onConfirm,\n onCancel,\n title,\n text,\n confirmText,\n cancelText,\n variant = \"default\",\n loading = false,\n trigger,\n}: ConfirmDialogProps) {\n const t = useT();\n const dialogRef = React.useRef<HTMLDialogElement>(null);\n const [internalOpen, setInternalOpen] = React.useState(false);\n const cancelButtonRef = React.useRef<HTMLButtonElement>(null);\n const confirmButtonRef = React.useRef<HTMLButtonElement>(null);\n const [portalTarget, setPortalTarget] = React.useState<HTMLElement | null>(null);\n // Unique IDs so multiple ConfirmDialog instances on the same page don't\n // collide and make `aria-labelledby` resolve to the wrong dialog's title.\n const reactId = React.useId();\n const titleId = `confirm-dialog-title-${reactId}`;\n const descriptionId = `confirm-dialog-description-${reactId}`;\n\n // Determine if we're in controlled mode (open prop provided) or declarative mode (trigger provided)\n const isControlled = controlledOpen !== undefined;\n const open = isControlled ? controlledOpen : internalOpen;\n const setOpen = isControlled\n ? onOpenChange || (() => {})\n : setInternalOpen;\n const handleCancelCallback = React.useCallback(() => {\n if (!isControlled) {\n onCancel?.();\n }\n }, [isControlled, onCancel]);\n\n // Default text values from i18n\n const resolvedTitle =\n title ?? t(\"ui.dialogs.confirm.defaultTitle\", \"Are you sure?\");\n const resolvedConfirmText =\n confirmText === false\n ? false\n : confirmText ?? t(\"ui.dialogs.confirm.confirmText\", \"Confirm\");\n const resolvedCancelText =\n cancelText === false\n ? false\n : cancelText ?? t(\"ui.dialogs.confirm.cancelText\", \"Cancel\");\n const closeAriaLabel = t(\"ui.dialog.close.ariaLabel\", \"Close\");\n const requestClose = React.useCallback(() => {\n setOpen(false);\n handleCancelCallback();\n }, [setOpen, handleCancelCallback]);\n\n React.useLayoutEffect(() => {\n setPortalTarget(document.body);\n }, []);\n\n // Handle dialog open/close with native showModal/close\n React.useLayoutEffect(() => {\n const dialog = dialogRef.current;\n if (!dialog) return;\n\n if (open) {\n if (!dialog.open) {\n dialog.showModal();\n // Focus cancel button (safe default) or confirm if no cancel\n setTimeout(() => {\n if (resolvedCancelText !== false && cancelButtonRef.current) {\n cancelButtonRef.current.focus();\n } else if (confirmButtonRef.current) {\n confirmButtonRef.current.focus();\n }\n }, 0);\n }\n } else {\n if (dialog.open) {\n dialog.close();\n }\n }\n }, [open, resolvedCancelText, portalTarget]);\n\n // Handle native cancel event (Escape key)\n React.useEffect(() => {\n const dialog = dialogRef.current;\n if (!dialog) return;\n\n const handleCancel = (e: Event) => {\n // Prevent close if loading\n if (loading) {\n e.preventDefault();\n return;\n }\n requestClose();\n };\n\n dialog.addEventListener(\"cancel\", handleCancel);\n return () => dialog.removeEventListener(\"cancel\", handleCancel);\n }, [loading, requestClose]);\n\n React.useEffect(() => {\n if (!open) return;\n\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key !== \"Escape\") return;\n if (!(event.target instanceof Node) || !dialogRef.current?.contains(event.target)) return;\n event.preventDefault();\n event.stopPropagation();\n if (!loading) requestClose();\n };\n\n window.addEventListener(\"keydown\", handleEscape, true);\n return () => window.removeEventListener(\"keydown\", handleEscape, true);\n }, [loading, open, requestClose]);\n\n // Handle backdrop click\n const handleBackdropClick = (e: React.MouseEvent<HTMLDialogElement>) => {\n // Only close if clicking directly on the dialog (backdrop), not its children\n if (e.target === dialogRef.current && !loading) {\n requestClose();\n }\n };\n\n // Handle keyboard shortcuts\n React.useEffect(() => {\n if (!open) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n // Cmd/Ctrl+Enter confirms\n if ((e.metaKey || e.ctrlKey) && e.key === \"Enter\" && !loading) {\n e.preventDefault();\n handleConfirm();\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, [open, loading]);\n\n const handleConfirm = async () => {\n await onConfirm();\n // Don't auto-close if loading \u2014 let the parent control when to close\n if (!loading) {\n setOpen(false);\n }\n };\n\n const handleCancel = () => {\n requestClose();\n };\n\n const handleTriggerClick = () => {\n if (trigger) {\n setOpen(true);\n }\n };\n\n const dialogElement = (\n <dialog\n ref={dialogRef}\n role=\"alertdialog\"\n aria-labelledby={titleId}\n aria-describedby={text ? descriptionId : undefined}\n onClick={handleBackdropClick}\n className={cn(\n // Reset dialog defaults\n \"m-0 p-0 max-w-none bg-transparent border-none pointer-events-auto\",\n // Backdrop styling\n \"backdrop:bg-black/50 backdrop:backdrop-blur-sm backdrop:transition-opacity\",\n // Mobile: bottom sheet\n \"fixed inset-x-0 bottom-0 top-auto w-full\",\n // Desktop: centered\n \"sm:inset-auto sm:mx-auto sm:my-auto sm:max-w-md sm:top-1/2 sm:left-1/2 sm:-translate-x-1/2 sm:-translate-y-1/2\",\n // Animation with reduced motion support\n \"motion-safe:open:animate-in motion-safe:open:fade-in-0 motion-safe:open:slide-in-from-bottom-4\",\n \"sm:motion-safe:open:slide-in-from-bottom-0 sm:motion-safe:open:zoom-in-95\",\n // Duration\n \"motion-safe:open:duration-300\"\n )}\n >\n <div\n role=\"document\"\n className={cn(\n // Panel container\n \"flex flex-col gap-4 rounded-t-2xl border-t bg-card p-6 text-foreground shadow-lg\",\n \"sm:rounded-xl sm:border\",\n // Relative positioning for close button\n \"relative\"\n )}\n >\n {/* Close button */}\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={handleCancel}\n disabled={loading}\n aria-label={closeAriaLabel}\n className=\"absolute right-4 top-4 opacity-70 hover:opacity-100\"\n >\n <X className=\"h-4 w-4\" />\n </IconButton>\n\n {/* Title */}\n <h2\n id={titleId}\n className={cn(\n \"text-sm font-medium leading-snug tracking-tight pr-6\",\n // Mobile: centered, Desktop: left-aligned\n \"text-center sm:text-left\"\n )}\n >\n {resolvedTitle}\n </h2>\n\n {/* Description (optional) */}\n {text && (\n <p\n id={descriptionId}\n className=\"text-sm font-medium leading-snug text-muted-foreground\"\n >\n {text}\n </p>\n )}\n\n {/* Actions */}\n <div className=\"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\">\n {resolvedCancelText !== false && (\n <Button\n ref={cancelButtonRef}\n variant=\"outline\"\n onClick={handleCancel}\n disabled={loading}\n className=\"w-full sm:w-auto\"\n >\n {resolvedCancelText}\n </Button>\n )}\n {resolvedConfirmText !== false && (\n <Button\n ref={confirmButtonRef}\n variant={variant === \"destructive\" ? \"destructive-solid\" : \"default\"}\n onClick={handleConfirm}\n disabled={loading}\n className=\"w-full sm:w-auto\"\n >\n {loading && <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />}\n {resolvedConfirmText}\n </Button>\n )}\n </div>\n </div>\n </dialog>\n );\n\n return (\n <>\n {trigger && (\n <div onClick={handleTriggerClick} className=\"inline-block\" role=\"presentation\">\n {trigger}\n </div>\n )}\n\n {portalTarget ? createPortal(dialogElement, portalTarget) : null}\n </>\n );\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["handleCancel"]
7
7
  }
@@ -24,32 +24,25 @@ function arraysEqual(a, b) {
24
24
  return true;
25
25
  }
26
26
  function useGroupOrder(pageType, defaultGroupIds) {
27
- const [orderedIds, setOrderedIds] = React.useState(defaultGroupIds);
28
- const mounted = React.useRef(false);
27
+ const [savedOrder, setSavedOrder] = React.useState(null);
29
28
  React.useEffect(() => {
30
- mounted.current = true;
31
29
  const saved = readJsonFromLocalStorage(getStorageKey(pageType), null);
32
- if (Array.isArray(saved)) {
33
- setOrderedIds(mergeOrder(saved, defaultGroupIds));
34
- }
30
+ setSavedOrder(Array.isArray(saved) ? saved : null);
35
31
  }, [pageType]);
36
- React.useEffect(() => {
37
- setOrderedIds((prev) => {
38
- const merged = mergeOrder(prev, defaultGroupIds);
39
- return arraysEqual(prev, merged) ? prev : merged;
40
- });
41
- }, [defaultGroupIds]);
32
+ const mergedIds = savedOrder ? mergeOrder(savedOrder, defaultGroupIds) : defaultGroupIds;
33
+ const stableIdsRef = React.useRef(mergedIds);
34
+ if (!arraysEqual(stableIdsRef.current, mergedIds)) {
35
+ stableIdsRef.current = mergedIds;
36
+ }
37
+ const orderedIds = stableIdsRef.current;
42
38
  const reorder = React.useCallback(
43
39
  (fromIndex, toIndex) => {
44
- setOrderedIds((prev) => {
45
- const next = [...prev];
46
- const [moved] = next.splice(fromIndex, 1);
47
- next.splice(toIndex, 0, moved);
48
- if (mounted.current) {
49
- writeJsonToLocalStorage(getStorageKey(pageType), next);
50
- }
51
- return next;
52
- });
40
+ const next = [...stableIdsRef.current];
41
+ const [moved] = next.splice(fromIndex, 1);
42
+ next.splice(toIndex, 0, moved);
43
+ stableIdsRef.current = next;
44
+ writeJsonToLocalStorage(getStorageKey(pageType), next);
45
+ setSavedOrder(next);
53
46
  },
54
47
  [pageType]
55
48
  );
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/crud/useGroupOrder.ts"],
4
- "sourcesContent": ["'use client'\nimport * as React from 'react'\nimport {\n readJsonFromLocalStorage,\n writeJsonToLocalStorage,\n} from '@open-mercato/shared/lib/browser/safeLocalStorage'\n\nconst STORAGE_PREFIX = 'om:group-order:'\n\nfunction getStorageKey(pageType: string) {\n return `${STORAGE_PREFIX}${pageType}`\n}\n\nfunction mergeOrder(saved: string[], defaults: string[]): string[] {\n const known = new Set(defaults)\n const result = saved.filter((id) => known.has(id))\n for (const id of defaults) {\n if (!result.includes(id)) result.push(id)\n }\n return result\n}\n\nfunction arraysEqual(a: string[], b: string[]): boolean {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i += 1) {\n if (a[i] !== b[i]) return false\n }\n return true\n}\n\n/**\n * Returns the group IDs in the user's preferred order.\n * Falls back to the default order when no preference is stored.\n */\nexport function useGroupOrder(pageType: string, defaultGroupIds: string[]) {\n const [orderedIds, setOrderedIds] = React.useState<string[]>(defaultGroupIds)\n const mounted = React.useRef(false)\n\n React.useEffect(() => {\n mounted.current = true\n const saved = readJsonFromLocalStorage<string[] | null>(getStorageKey(pageType), null)\n if (Array.isArray(saved)) {\n setOrderedIds(mergeOrder(saved, defaultGroupIds))\n }\n // Intentionally only runs on mount (per pageType); defaultGroupIds changes are\n // handled by the sync effect below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [pageType])\n\n // Sync when defaultGroupIds changes (e.g. new groups added dynamically)\n React.useEffect(() => {\n setOrderedIds((prev) => {\n const merged = mergeOrder(prev, defaultGroupIds)\n return arraysEqual(prev, merged) ? prev : merged\n })\n }, [defaultGroupIds])\n\n const reorder = React.useCallback(\n (fromIndex: number, toIndex: number) => {\n setOrderedIds((prev) => {\n const next = [...prev]\n const [moved] = next.splice(fromIndex, 1)\n next.splice(toIndex, 0, moved)\n if (mounted.current) {\n writeJsonToLocalStorage(getStorageKey(pageType), next)\n }\n return next\n })\n },\n [pageType],\n )\n\n return { orderedIds, reorder }\n}\n"],
5
- "mappings": ";AACA,YAAY,WAAW;AACvB;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP,MAAM,iBAAiB;AAEvB,SAAS,cAAc,UAAkB;AACvC,SAAO,GAAG,cAAc,GAAG,QAAQ;AACrC;AAEA,SAAS,WAAW,OAAiB,UAA8B;AACjE,QAAM,QAAQ,IAAI,IAAI,QAAQ;AAC9B,QAAM,SAAS,MAAM,OAAO,CAAC,OAAO,MAAM,IAAI,EAAE,CAAC;AACjD,aAAW,MAAM,UAAU;AACzB,QAAI,CAAC,OAAO,SAAS,EAAE,EAAG,QAAO,KAAK,EAAE;AAAA,EAC1C;AACA,SAAO;AACT;AAEA,SAAS,YAAY,GAAa,GAAsB;AACtD,MAAI,EAAE,WAAW,EAAE,OAAQ,QAAO;AAClC,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK,GAAG;AACpC,QAAI,EAAE,CAAC,MAAM,EAAE,CAAC,EAAG,QAAO;AAAA,EAC5B;AACA,SAAO;AACT;AAMO,SAAS,cAAc,UAAkB,iBAA2B;AACzE,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAmB,eAAe;AAC5E,QAAM,UAAU,MAAM,OAAO,KAAK;AAElC,QAAM,UAAU,MAAM;AACpB,YAAQ,UAAU;AAClB,UAAM,QAAQ,yBAA0C,cAAc,QAAQ,GAAG,IAAI;AACrF,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,oBAAc,WAAW,OAAO,eAAe,CAAC;AAAA,IAClD;AAAA,EAIF,GAAG,CAAC,QAAQ,CAAC;AAGb,QAAM,UAAU,MAAM;AACpB,kBAAc,CAAC,SAAS;AACtB,YAAM,SAAS,WAAW,MAAM,eAAe;AAC/C,aAAO,YAAY,MAAM,MAAM,IAAI,OAAO;AAAA,IAC5C,CAAC;AAAA,EACH,GAAG,CAAC,eAAe,CAAC;AAEpB,QAAM,UAAU,MAAM;AAAA,IACpB,CAAC,WAAmB,YAAoB;AACtC,oBAAc,CAAC,SAAS;AACtB,cAAM,OAAO,CAAC,GAAG,IAAI;AACrB,cAAM,CAAC,KAAK,IAAI,KAAK,OAAO,WAAW,CAAC;AACxC,aAAK,OAAO,SAAS,GAAG,KAAK;AAC7B,YAAI,QAAQ,SAAS;AACnB,kCAAwB,cAAc,QAAQ,GAAG,IAAI;AAAA,QACvD;AACA,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AAEA,SAAO,EAAE,YAAY,QAAQ;AAC/B;",
4
+ "sourcesContent": ["'use client'\nimport * as React from 'react'\nimport {\n readJsonFromLocalStorage,\n writeJsonToLocalStorage,\n} from '@open-mercato/shared/lib/browser/safeLocalStorage'\n\nconst STORAGE_PREFIX = 'om:group-order:'\n\nfunction getStorageKey(pageType: string) {\n return `${STORAGE_PREFIX}${pageType}`\n}\n\nfunction mergeOrder(saved: string[], defaults: string[]): string[] {\n const known = new Set(defaults)\n const result = saved.filter((id) => known.has(id))\n for (const id of defaults) {\n if (!result.includes(id)) result.push(id)\n }\n return result\n}\n\nfunction arraysEqual(a: string[], b: string[]): boolean {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i += 1) {\n if (a[i] !== b[i]) return false\n }\n return true\n}\n\n/**\n * Returns the group IDs in the user's preferred order.\n * Falls back to the default order when no preference is stored.\n *\n * State holds only the saved preference; the visible order is derived during\n * render. Syncing derived order back into state via an effect looped forever\n * when a host recreated `defaultGroupIds` with different content on every\n * render (#4386), so no effect writes state from `defaultGroupIds` here.\n *\n * `stableIdsRef` is mutated during render, which React's render-purity rule\n * normally forbids. It is safe here because the guard swaps the reference only\n * when the *content* differs: a concurrent render that React discards can leave\n * behind a different array identity, but never a content-stale value, and the\n * render that commits always leaves `stableIdsRef.current` content-equal to its\n * own `mergedIds`. `reorder` advances the same ref from its event handler so\n * that several reorders within one commit compose instead of overwriting each\n * other; a discarded state update leaves the ref ahead of `savedOrder` only\n * until the next render's content guard pulls it back (#4691).\n */\nexport function useGroupOrder(pageType: string, defaultGroupIds: string[]) {\n const [savedOrder, setSavedOrder] = React.useState<string[] | null>(null)\n\n React.useEffect(() => {\n const saved = readJsonFromLocalStorage<string[] | null>(getStorageKey(pageType), null)\n setSavedOrder(Array.isArray(saved) ? saved : null)\n }, [pageType])\n\n const mergedIds = savedOrder ? mergeOrder(savedOrder, defaultGroupIds) : defaultGroupIds\n\n const stableIdsRef = React.useRef(mergedIds)\n if (!arraysEqual(stableIdsRef.current, mergedIds)) {\n stableIdsRef.current = mergedIds\n }\n const orderedIds = stableIdsRef.current\n\n const reorder = React.useCallback(\n (fromIndex: number, toIndex: number) => {\n const next = [...stableIdsRef.current]\n const [moved] = next.splice(fromIndex, 1)\n next.splice(toIndex, 0, moved)\n stableIdsRef.current = next\n writeJsonToLocalStorage(getStorageKey(pageType), next)\n setSavedOrder(next)\n },\n [pageType],\n )\n\n return { orderedIds, reorder }\n}\n"],
5
+ "mappings": ";AACA,YAAY,WAAW;AACvB;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP,MAAM,iBAAiB;AAEvB,SAAS,cAAc,UAAkB;AACvC,SAAO,GAAG,cAAc,GAAG,QAAQ;AACrC;AAEA,SAAS,WAAW,OAAiB,UAA8B;AACjE,QAAM,QAAQ,IAAI,IAAI,QAAQ;AAC9B,QAAM,SAAS,MAAM,OAAO,CAAC,OAAO,MAAM,IAAI,EAAE,CAAC;AACjD,aAAW,MAAM,UAAU;AACzB,QAAI,CAAC,OAAO,SAAS,EAAE,EAAG,QAAO,KAAK,EAAE;AAAA,EAC1C;AACA,SAAO;AACT;AAEA,SAAS,YAAY,GAAa,GAAsB;AACtD,MAAI,EAAE,WAAW,EAAE,OAAQ,QAAO;AAClC,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK,GAAG;AACpC,QAAI,EAAE,CAAC,MAAM,EAAE,CAAC,EAAG,QAAO;AAAA,EAC5B;AACA,SAAO;AACT;AAqBO,SAAS,cAAc,UAAkB,iBAA2B;AACzE,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAA0B,IAAI;AAExE,QAAM,UAAU,MAAM;AACpB,UAAM,QAAQ,yBAA0C,cAAc,QAAQ,GAAG,IAAI;AACrF,kBAAc,MAAM,QAAQ,KAAK,IAAI,QAAQ,IAAI;AAAA,EACnD,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,YAAY,aAAa,WAAW,YAAY,eAAe,IAAI;AAEzE,QAAM,eAAe,MAAM,OAAO,SAAS;AAC3C,MAAI,CAAC,YAAY,aAAa,SAAS,SAAS,GAAG;AACjD,iBAAa,UAAU;AAAA,EACzB;AACA,QAAM,aAAa,aAAa;AAEhC,QAAM,UAAU,MAAM;AAAA,IACpB,CAAC,WAAmB,YAAoB;AACtC,YAAM,OAAO,CAAC,GAAG,aAAa,OAAO;AACrC,YAAM,CAAC,KAAK,IAAI,KAAK,OAAO,WAAW,CAAC;AACxC,WAAK,OAAO,SAAS,GAAG,KAAK;AAC7B,mBAAa,UAAU;AACvB,8BAAwB,cAAc,QAAQ,GAAG,IAAI;AACrD,oBAAc,IAAI;AAAA,IACpB;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AAEA,SAAO,EAAE,YAAY,QAAQ;AAC/B;",
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, { variant: "destructive", children: [
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,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/custom-fields/FieldDefinitionsManager.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { Button } from '../../primitives/button'\nimport { Spinner } from '../../primitives/spinner'\nimport { Alert, AlertDescription, AlertTitle } from '../../primitives/alert'\nimport { flash } from '../FlashMessages'\nimport { apiCall, readApiResultOrThrow } from '../utils/apiCall'\nimport { raiseCrudError } from '../utils/serverErrors'\nimport { invalidateCustomFieldDefs } from '../utils/customFieldDefs'\nimport { FieldDefinitionsEditor, type FieldDefinition, type FieldDefinitionError } from './FieldDefinitionsEditor'\nimport { upsertCustomFieldDefSchema } from '@open-mercato/shared/modules/entities/validators'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype FieldsetGroup = { code: string; title?: string; hint?: string }\ntype FieldsetDefinition = { code: string; label: string; icon?: string; description?: string; groups?: FieldsetGroup[] }\ntype DefinitionsManageResponse = {\n items?: any[]\n deletedKeys?: string[]\n fieldsets?: FieldsetDefinition[]\n settings?: { singleFieldsetPerRecord?: boolean }\n}\n\ntype FieldDefinitionsManagerProps = {\n entityId: string\n initialFieldset?: string | null\n onClose: () => void\n onSaved?: () => void | Promise<void>\n fullEditorHref?: string | null\n}\n\nexport type FieldDefinitionsManagerHandle = {\n submit: () => Promise<void>\n}\n\nfunction normalizeGroupPayload(value: unknown): FieldsetGroup | null {\n if (!value) return null\n if (typeof value === 'string') {\n const code = value.trim()\n return code ? { code } : null\n }\n if (typeof value !== 'object') return null\n const entry = value as Record<string, unknown>\n const code = typeof entry.code === 'string' ? entry.code.trim() : ''\n if (!code) return null\n const group: FieldsetGroup = { code }\n if (typeof entry.title === 'string' && entry.title.trim()) group.title = entry.title.trim()\n if (typeof entry.hint === 'string' && entry.hint.trim()) group.hint = entry.hint.trim()\n return group\n}\n\nexport const FieldDefinitionsManager = React.forwardRef<FieldDefinitionsManagerHandle, FieldDefinitionsManagerProps>(\n function FieldDefinitionsManager({ entityId, initialFieldset = null, onClose, onSaved, fullEditorHref }, ref) {\n const t = useT()\n const queryClient = useQueryClient()\n const [loading, setLoading] = React.useState(true)\n const [saving, setSaving] = React.useState(false)\n const [statusError, setStatusError] = React.useState<string | null>(null)\n const [defs, setDefs] = React.useState<FieldDefinition[]>([])\n const [deletedKeys, setDeletedKeys] = React.useState<string[]>([])\n const [defErrors, setDefErrors] = React.useState<Record<number, FieldDefinitionError>>({})\n const [fieldsets, setFieldsets] = React.useState<FieldsetDefinition[]>([])\n const [activeFieldset, setActiveFieldset] = React.useState<string | null>(initialFieldset)\n const [singleFieldsetPerRecord, setSingleFieldsetPerRecord] = React.useState(true)\n const [isDirty, setIsDirty] = React.useState(false)\n const [orderDirty, setOrderDirty] = React.useState(false)\n\n const loadDefinitions = React.useCallback(async () => {\n if (!entityId) return\n setLoading(true)\n setStatusError(null)\n try {\n const json = await readApiResultOrThrow<DefinitionsManageResponse>(\n `/api/entities/definitions.manage?entityId=${encodeURIComponent(entityId)}`,\n undefined,\n { errorMessage: t('entities.customFields.errors.loadFailed', 'Failed to load field definitions'), fallback: { items: [], deletedKeys: [] } },\n )\n const loaded: FieldDefinition[] = (json.items || []).map((d: any) => ({\n key: d.key,\n kind: d.kind,\n configJson: d.configJson || {},\n isActive: d.isActive !== false,\n }))\n loaded.sort(\n (a, b) => Number(a.configJson?.priority ?? 0) - Number(b.configJson?.priority ?? 0)\n )\n setDefs(loaded)\n setDeletedKeys(Array.isArray(json.deletedKeys) ? json.deletedKeys : [])\n const loadedFieldsets = Array.isArray(json.fieldsets) ? json.fieldsets : []\n setFieldsets(loadedFieldsets)\n setActiveFieldset((current) => {\n if (initialFieldset && loadedFieldsets.some((fs) => fs.code === initialFieldset)) {\n return initialFieldset\n }\n if (current && loadedFieldsets.some((fs) => fs.code === current)) return current\n return loadedFieldsets[0]?.code ?? null\n })\n setSingleFieldsetPerRecord(json.settings?.singleFieldsetPerRecord !== false)\n setDefErrors({})\n setIsDirty(false)\n setOrderDirty(false)\n } catch (err) {\n const message = err instanceof Error ? err.message : t('entities.customFields.errors.loadFailed', 'Failed to load field definitions')\n setStatusError(message)\n } finally {\n setLoading(false)\n }\n }, [entityId, initialFieldset, t])\n\n React.useEffect(() => {\n loadDefinitions().catch(() => {})\n }, [loadDefinitions])\n\n const buildFieldsetPayload = React.useCallback(() => {\n const groupMap = new Map<string, FieldsetGroup[]>()\n defs.forEach((definition) => {\n const code = typeof definition.configJson?.fieldset === 'string' ? definition.configJson.fieldset : null\n if (!code) return\n const normalized = normalizeGroupPayload(definition.configJson?.group)\n if (!normalized) return\n const list = groupMap.get(code) ?? []\n if (!list.some((entry) => entry.code === normalized.code)) {\n list.push(normalized)\n groupMap.set(code, list)\n }\n })\n return fieldsets.map((fs) => ({\n ...fs,\n groups: groupMap.get(fs.code) ?? [],\n }))\n }, [defs, fieldsets])\n\n const validateDef = React.useCallback(\n (definition: FieldDefinition): FieldDefinitionError => {\n const parsed = upsertCustomFieldDefSchema.safeParse({\n entityId,\n key: definition.key,\n kind: definition.kind,\n configJson: definition.configJson,\n isActive: definition.isActive,\n })\n if (parsed.success) return {}\n const errs: FieldDefinitionError = {}\n for (const issue of parsed.error.issues) {\n if (issue.path.includes('key')) errs.key = issue.message\n if (issue.path.includes('kind')) errs.kind = issue.message\n }\n return errs\n },\n [entityId],\n )\n\n const validateAll = React.useCallback(() => {\n const nextErrors: Record<number, FieldDefinitionError> = {}\n defs.forEach((definition, index) => {\n nextErrors[index] = validateDef(definition)\n })\n setDefErrors(nextErrors)\n return Object.values(nextErrors).every((entry) => !entry.key && !entry.kind)\n }, [defs, validateDef])\n\n const handleDefinitionChange = React.useCallback((index: number, nextDef: FieldDefinition) => {\n setDefs((arr) => arr.map((entry, idx) => (idx === index ? nextDef : entry)))\n setDefErrors((prev) => ({ ...prev, [index]: validateDef(nextDef) }))\n setIsDirty(true)\n }, [validateDef])\n\n const handleAddField = React.useCallback(() => {\n setDefs((arr) => [\n ...arr,\n {\n key: '',\n kind: 'text',\n configJson: activeFieldset ? { fieldset: activeFieldset } : {},\n isActive: true,\n },\n ])\n setIsDirty(true)\n }, [activeFieldset])\n\n const handleRemoveField = React.useCallback(async (index: number) => {\n const def = defs[index]\n if (!def) return\n if (def.key) {\n try {\n const call = await apiCall('/api/entities/definitions', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ entityId, key: def.key }),\n })\n if (!call.ok) {\n await raiseCrudError(call.response, t('entities.customFields.errors.deleteFailed', 'Failed to delete field'))\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : t('entities.customFields.errors.deleteFailed', 'Failed to delete field')\n flash(message, 'error')\n return\n }\n }\n setDefs((arr) => arr.filter((_, idx) => idx !== index))\n setIsDirty(true)\n }, [defs, entityId, t])\n\n const handleRestoreField = React.useCallback(async (key: string) => {\n try {\n const call = await apiCall('/api/entities/definitions.restore', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ entityId, key }),\n })\n if (!call.ok) {\n await raiseCrudError(call.response, t('entities.customFields.errors.restoreFailed', 'Failed to restore field'))\n }\n flash(t('entities.customFields.flash.restored', 'Field restored'), 'success')\n await loadDefinitions()\n } catch (error) {\n const message = error instanceof Error ? error.message : t('entities.customFields.errors.restoreFailed', 'Failed to restore field')\n flash(message, 'error')\n }\n }, [entityId, loadDefinitions, t])\n\n const handleReorder = React.useCallback((from: number, to: number) => {\n setDefs((arr) => {\n const next = [...arr]\n const [moved] = next.splice(from, 1)\n next.splice(to, 0, moved)\n return next\n })\n setIsDirty(true)\n setOrderDirty(true)\n }, [])\n\n const handleFieldsetsChange = React.useCallback((next: FieldsetDefinition[]) => {\n setFieldsets(next)\n if (!next.some((fs) => fs.code === activeFieldset)) {\n setActiveFieldset(next[0]?.code ?? null)\n }\n setIsDirty(true)\n }, [activeFieldset])\n\n const handleFieldsetCodeChange = React.useCallback((previousCode: string, nextCode: string) => {\n if (!previousCode || !nextCode || previousCode === nextCode) return\n setDefs((arr) =>\n arr.map((entry) => {\n const current = typeof entry.configJson?.fieldset === 'string' ? entry.configJson.fieldset : undefined\n if (current !== previousCode) return entry\n const nextConfig = { ...(entry.configJson || {}) }\n nextConfig.fieldset = nextCode\n return { ...entry, configJson: nextConfig }\n }),\n )\n setActiveFieldset((current) => (current === previousCode ? nextCode : current))\n setIsDirty(true)\n }, [])\n\n const handleFieldsetRemoved = React.useCallback((code: string) => {\n if (!code) return\n setDefs((arr) =>\n arr.map((entry) => {\n const current = typeof entry.configJson?.fieldset === 'string' ? entry.configJson.fieldset : undefined\n if (current !== code) return entry\n const nextConfig = { ...(entry.configJson || {}) }\n delete nextConfig.fieldset\n delete nextConfig.group\n return { ...entry, configJson: nextConfig }\n }),\n )\n setIsDirty(true)\n }, [])\n\n const submit = React.useCallback(async () => {\n if (!entityId) return\n setSaving(true)\n setStatusError(null)\n try {\n if (!validateAll()) {\n flash(t('entities.customFields.errors.validation', 'Please fix validation errors'), 'error')\n throw new Error(t('entities.customFields.errors.validation', 'Validation failed'))\n }\n const payload = {\n entityId,\n definitions: defs.filter((d) => !!d.key).map((d) => ({\n key: d.key,\n kind: d.kind,\n configJson: d.configJson,\n isActive: d.isActive !== false,\n })),\n fieldsets: buildFieldsetPayload(),\n singleFieldsetPerRecord,\n }\n const call = await apiCall('/api/entities/definitions.batch', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!call.ok) {\n await raiseCrudError(call.response, t('entities.customFields.errors.saveFailed', 'Failed to save definitions'))\n }\n await invalidateCustomFieldDefs(queryClient, entityId)\n flash(t('entities.customFields.flash.saved', 'Definitions saved'), 'success')\n setIsDirty(false)\n setOrderDirty(false)\n if (onSaved) await onSaved()\n onClose()\n } catch (err) {\n const message = err instanceof Error ? err.message : t('entities.customFields.errors.saveFailed', 'Failed to save definitions')\n setStatusError(message)\n } finally {\n setSaving(false)\n }\n }, [buildFieldsetPayload, defs, entityId, onClose, onSaved, queryClient, singleFieldsetPerRecord, t, validateAll])\n\n React.useImperativeHandle(ref, () => ({ submit }), [submit])\n\n const cancelLabel = t('ui.forms.actions.cancel', 'Cancel')\n const saveLabel = saving ? t('ui.forms.status.saving', 'Saving...') : t('ui.forms.actions.save', 'Save')\n const openFullLabel = t('entities.customFields.openInNewTab', 'Open full editor')\n\n const handleOpenFullEditor = React.useCallback(() => {\n if (!fullEditorHref || typeof window === 'undefined') return\n window.open(fullEditorHref, '_blank', 'noopener,noreferrer')\n }, [fullEditorHref])\n\n const content = loading ? (\n <div className=\"flex h-64 items-center justify-center\">\n <Spinner size=\"lg\" />\n </div>\n ) : (\n <FieldDefinitionsEditor\n definitions={defs}\n errors={defErrors}\n deletedKeys={deletedKeys}\n fieldsets={fieldsets}\n activeFieldset={activeFieldset}\n onActiveFieldsetChange={setActiveFieldset}\n onFieldsetsChange={handleFieldsetsChange}\n onFieldsetCodeChange={handleFieldsetCodeChange}\n onFieldsetRemoved={handleFieldsetRemoved}\n singleFieldsetPerRecord={singleFieldsetPerRecord}\n onSingleFieldsetPerRecordChange={(value) => {\n setSingleFieldsetPerRecord(value)\n setIsDirty(true)\n }}\n onAddField={handleAddField}\n onRemoveField={(index) => { void handleRemoveField(index) }}\n onDefinitionChange={handleDefinitionChange}\n onRestoreField={(key) => { void handleRestoreField(key) }}\n onReorder={handleReorder}\n orderNotice={orderDirty ? { dirty: true, message: t('entities.customFields.orderNotice', 'Reordered \u2014 remember to save') } : undefined}\n translate={t}\n />\n )\n\n return (\n <div className=\"flex flex-col gap-3 sm:gap-4\">\n {statusError ? (\n <Alert variant=\"destructive\">\n <AlertTitle>{t('entities.customFields.errors.title', 'Something went wrong')}</AlertTitle>\n <AlertDescription>{statusError}</AlertDescription>\n </Alert>\n ) : null}\n <div className=\"rounded-lg border bg-card p-3 sm:p-4 max-h-[70vh] overflow-y-auto\">\n {content}\n </div>\n <div className=\"flex justify-end gap-2\">\n {fullEditorHref ? (\n <Button type=\"button\" variant=\"ghost\" onClick={handleOpenFullEditor}>\n {openFullLabel}\n </Button>\n ) : null}\n <Button type=\"button\" variant=\"outline\" onClick={onClose} disabled={saving}>\n {cancelLabel}\n </Button>\n <Button type=\"button\" onClick={() => { void submit() }} disabled={saving || !isDirty}>\n {saveLabel}\n </Button>\n </div>\n </div>\n )\n },\n)\n\nFieldDefinitionsManager.displayName = 'FieldDefinitionsManager'\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { Button } from '../../primitives/button'\nimport { Spinner } from '../../primitives/spinner'\nimport { Alert, AlertDescription, AlertTitle } from '../../primitives/alert'\nimport { flash } from '../FlashMessages'\nimport { apiCall, readApiResultOrThrow } from '../utils/apiCall'\nimport { raiseCrudError } from '../utils/serverErrors'\nimport { invalidateCustomFieldDefs } from '../utils/customFieldDefs'\nimport { FieldDefinitionsEditor, type FieldDefinition, type FieldDefinitionError } from './FieldDefinitionsEditor'\nimport { upsertCustomFieldDefSchema } from '@open-mercato/shared/modules/entities/validators'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype FieldsetGroup = { code: string; title?: string; hint?: string }\ntype FieldsetDefinition = { code: string; label: string; icon?: string; description?: string; groups?: FieldsetGroup[] }\ntype DefinitionsManageResponse = {\n items?: any[]\n deletedKeys?: string[]\n fieldsets?: FieldsetDefinition[]\n settings?: { singleFieldsetPerRecord?: boolean }\n}\n\ntype FieldDefinitionsManagerProps = {\n entityId: string\n initialFieldset?: string | null\n onClose: () => void\n onSaved?: () => void | Promise<void>\n fullEditorHref?: string | null\n}\n\nexport type FieldDefinitionsManagerHandle = {\n submit: () => Promise<void>\n}\n\nfunction normalizeGroupPayload(value: unknown): FieldsetGroup | null {\n if (!value) return null\n if (typeof value === 'string') {\n const code = value.trim()\n return code ? { code } : null\n }\n if (typeof value !== 'object') return null\n const entry = value as Record<string, unknown>\n const code = typeof entry.code === 'string' ? entry.code.trim() : ''\n if (!code) return null\n const group: FieldsetGroup = { code }\n if (typeof entry.title === 'string' && entry.title.trim()) group.title = entry.title.trim()\n if (typeof entry.hint === 'string' && entry.hint.trim()) group.hint = entry.hint.trim()\n return group\n}\n\nexport const FieldDefinitionsManager = React.forwardRef<FieldDefinitionsManagerHandle, FieldDefinitionsManagerProps>(\n function FieldDefinitionsManager({ entityId, initialFieldset = null, onClose, onSaved, fullEditorHref }, ref) {\n const t = useT()\n const queryClient = useQueryClient()\n const [loading, setLoading] = React.useState(true)\n const [saving, setSaving] = React.useState(false)\n const [statusError, setStatusError] = React.useState<string | null>(null)\n const [defs, setDefs] = React.useState<FieldDefinition[]>([])\n const [deletedKeys, setDeletedKeys] = React.useState<string[]>([])\n const [defErrors, setDefErrors] = React.useState<Record<number, FieldDefinitionError>>({})\n const [fieldsets, setFieldsets] = React.useState<FieldsetDefinition[]>([])\n const [activeFieldset, setActiveFieldset] = React.useState<string | null>(initialFieldset)\n const [singleFieldsetPerRecord, setSingleFieldsetPerRecord] = React.useState(true)\n const [isDirty, setIsDirty] = React.useState(false)\n const [orderDirty, setOrderDirty] = React.useState(false)\n\n const loadDefinitions = React.useCallback(async () => {\n if (!entityId) return\n setLoading(true)\n setStatusError(null)\n try {\n const json = await readApiResultOrThrow<DefinitionsManageResponse>(\n `/api/entities/definitions.manage?entityId=${encodeURIComponent(entityId)}`,\n undefined,\n { errorMessage: t('entities.customFields.errors.loadFailed', 'Failed to load field definitions'), fallback: { items: [], deletedKeys: [] } },\n )\n const loaded: FieldDefinition[] = (json.items || []).map((d: any) => ({\n key: d.key,\n kind: d.kind,\n configJson: d.configJson || {},\n isActive: d.isActive !== false,\n }))\n loaded.sort(\n (a, b) => Number(a.configJson?.priority ?? 0) - Number(b.configJson?.priority ?? 0)\n )\n setDefs(loaded)\n setDeletedKeys(Array.isArray(json.deletedKeys) ? json.deletedKeys : [])\n const loadedFieldsets = Array.isArray(json.fieldsets) ? json.fieldsets : []\n setFieldsets(loadedFieldsets)\n setActiveFieldset((current) => {\n if (initialFieldset && loadedFieldsets.some((fs) => fs.code === initialFieldset)) {\n return initialFieldset\n }\n if (current && loadedFieldsets.some((fs) => fs.code === current)) return current\n return loadedFieldsets[0]?.code ?? null\n })\n setSingleFieldsetPerRecord(json.settings?.singleFieldsetPerRecord !== false)\n setDefErrors({})\n setIsDirty(false)\n setOrderDirty(false)\n } catch (err) {\n const message = err instanceof Error ? err.message : t('entities.customFields.errors.loadFailed', 'Failed to load field definitions')\n setStatusError(message)\n } finally {\n setLoading(false)\n }\n }, [entityId, initialFieldset, t])\n\n React.useEffect(() => {\n loadDefinitions().catch(() => {})\n }, [loadDefinitions])\n\n const buildFieldsetPayload = React.useCallback(() => {\n const groupMap = new Map<string, FieldsetGroup[]>()\n defs.forEach((definition) => {\n const code = typeof definition.configJson?.fieldset === 'string' ? definition.configJson.fieldset : null\n if (!code) return\n const normalized = normalizeGroupPayload(definition.configJson?.group)\n if (!normalized) return\n const list = groupMap.get(code) ?? []\n if (!list.some((entry) => entry.code === normalized.code)) {\n list.push(normalized)\n groupMap.set(code, list)\n }\n })\n return fieldsets.map((fs) => ({\n ...fs,\n groups: groupMap.get(fs.code) ?? [],\n }))\n }, [defs, fieldsets])\n\n const validateDef = React.useCallback(\n (definition: FieldDefinition): FieldDefinitionError => {\n const parsed = upsertCustomFieldDefSchema.safeParse({\n entityId,\n key: definition.key,\n kind: definition.kind,\n configJson: definition.configJson,\n isActive: definition.isActive,\n })\n if (parsed.success) return {}\n const errs: FieldDefinitionError = {}\n for (const issue of parsed.error.issues) {\n if (issue.path.includes('key')) errs.key = issue.message\n if (issue.path.includes('kind')) errs.kind = issue.message\n }\n return errs\n },\n [entityId],\n )\n\n const validateAll = React.useCallback(() => {\n const nextErrors: Record<number, FieldDefinitionError> = {}\n defs.forEach((definition, index) => {\n nextErrors[index] = validateDef(definition)\n })\n setDefErrors(nextErrors)\n return Object.values(nextErrors).every((entry) => !entry.key && !entry.kind)\n }, [defs, validateDef])\n\n const handleDefinitionChange = React.useCallback((index: number, nextDef: FieldDefinition) => {\n setDefs((arr) => arr.map((entry, idx) => (idx === index ? nextDef : entry)))\n setDefErrors((prev) => ({ ...prev, [index]: validateDef(nextDef) }))\n setIsDirty(true)\n }, [validateDef])\n\n const handleAddField = React.useCallback(() => {\n setDefs((arr) => [\n ...arr,\n {\n key: '',\n kind: 'text',\n configJson: activeFieldset ? { fieldset: activeFieldset } : {},\n isActive: true,\n },\n ])\n setIsDirty(true)\n }, [activeFieldset])\n\n const handleRemoveField = React.useCallback(async (index: number) => {\n const def = defs[index]\n if (!def) return\n if (def.key) {\n try {\n const call = await apiCall('/api/entities/definitions', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ entityId, key: def.key }),\n })\n if (!call.ok) {\n await raiseCrudError(call.response, t('entities.customFields.errors.deleteFailed', 'Failed to delete field'))\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : t('entities.customFields.errors.deleteFailed', 'Failed to delete field')\n flash(message, 'error')\n return\n }\n }\n setDefs((arr) => arr.filter((_, idx) => idx !== index))\n setIsDirty(true)\n }, [defs, entityId, t])\n\n const handleRestoreField = React.useCallback(async (key: string) => {\n try {\n const call = await apiCall('/api/entities/definitions.restore', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ entityId, key }),\n })\n if (!call.ok) {\n await raiseCrudError(call.response, t('entities.customFields.errors.restoreFailed', 'Failed to restore field'))\n }\n flash(t('entities.customFields.flash.restored', 'Field restored'), 'success')\n await loadDefinitions()\n } catch (error) {\n const message = error instanceof Error ? error.message : t('entities.customFields.errors.restoreFailed', 'Failed to restore field')\n flash(message, 'error')\n }\n }, [entityId, loadDefinitions, t])\n\n const handleReorder = React.useCallback((from: number, to: number) => {\n setDefs((arr) => {\n const next = [...arr]\n const [moved] = next.splice(from, 1)\n next.splice(to, 0, moved)\n return next\n })\n setIsDirty(true)\n setOrderDirty(true)\n }, [])\n\n const handleFieldsetsChange = React.useCallback((next: FieldsetDefinition[]) => {\n setFieldsets(next)\n if (!next.some((fs) => fs.code === activeFieldset)) {\n setActiveFieldset(next[0]?.code ?? null)\n }\n setIsDirty(true)\n }, [activeFieldset])\n\n const handleFieldsetCodeChange = React.useCallback((previousCode: string, nextCode: string) => {\n if (!previousCode || !nextCode || previousCode === nextCode) return\n setDefs((arr) =>\n arr.map((entry) => {\n const current = typeof entry.configJson?.fieldset === 'string' ? entry.configJson.fieldset : undefined\n if (current !== previousCode) return entry\n const nextConfig = { ...(entry.configJson || {}) }\n nextConfig.fieldset = nextCode\n return { ...entry, configJson: nextConfig }\n }),\n )\n setActiveFieldset((current) => (current === previousCode ? nextCode : current))\n setIsDirty(true)\n }, [])\n\n const handleFieldsetRemoved = React.useCallback((code: string) => {\n if (!code) return\n setDefs((arr) =>\n arr.map((entry) => {\n const current = typeof entry.configJson?.fieldset === 'string' ? entry.configJson.fieldset : undefined\n if (current !== code) return entry\n const nextConfig = { ...(entry.configJson || {}) }\n delete nextConfig.fieldset\n delete nextConfig.group\n return { ...entry, configJson: nextConfig }\n }),\n )\n setIsDirty(true)\n }, [])\n\n const submit = React.useCallback(async () => {\n if (!entityId) return\n setSaving(true)\n setStatusError(null)\n try {\n if (!validateAll()) {\n flash(t('entities.customFields.errors.validation', 'Please fix validation errors'), 'error')\n throw new Error(t('entities.customFields.errors.validation', 'Validation failed'))\n }\n const payload = {\n entityId,\n definitions: defs.filter((d) => !!d.key).map((d) => ({\n key: d.key,\n kind: d.kind,\n configJson: d.configJson,\n isActive: d.isActive !== false,\n })),\n fieldsets: buildFieldsetPayload(),\n singleFieldsetPerRecord,\n }\n const call = await apiCall('/api/entities/definitions.batch', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!call.ok) {\n await raiseCrudError(call.response, t('entities.customFields.errors.saveFailed', 'Failed to save definitions'))\n }\n await invalidateCustomFieldDefs(queryClient, entityId)\n flash(t('entities.customFields.flash.saved', 'Definitions saved'), 'success')\n setIsDirty(false)\n setOrderDirty(false)\n if (onSaved) await onSaved()\n onClose()\n } catch (err) {\n const message = err instanceof Error ? err.message : t('entities.customFields.errors.saveFailed', 'Failed to save definitions')\n setStatusError(message)\n } finally {\n setSaving(false)\n }\n }, [buildFieldsetPayload, defs, entityId, onClose, onSaved, queryClient, singleFieldsetPerRecord, t, validateAll])\n\n React.useImperativeHandle(ref, () => ({ submit }), [submit])\n\n const cancelLabel = t('ui.forms.actions.cancel', 'Cancel')\n const saveLabel = saving ? t('ui.forms.status.saving', 'Saving...') : t('ui.forms.actions.save', 'Save')\n const openFullLabel = t('entities.customFields.openInNewTab', 'Open full editor')\n\n const handleOpenFullEditor = React.useCallback(() => {\n if (!fullEditorHref || typeof window === 'undefined') return\n window.open(fullEditorHref, '_blank', 'noopener,noreferrer')\n }, [fullEditorHref])\n\n const content = loading ? (\n <div className=\"flex h-64 items-center justify-center\">\n <Spinner size=\"lg\" />\n </div>\n ) : (\n <FieldDefinitionsEditor\n definitions={defs}\n errors={defErrors}\n deletedKeys={deletedKeys}\n fieldsets={fieldsets}\n activeFieldset={activeFieldset}\n onActiveFieldsetChange={setActiveFieldset}\n onFieldsetsChange={handleFieldsetsChange}\n onFieldsetCodeChange={handleFieldsetCodeChange}\n onFieldsetRemoved={handleFieldsetRemoved}\n singleFieldsetPerRecord={singleFieldsetPerRecord}\n onSingleFieldsetPerRecordChange={(value) => {\n setSingleFieldsetPerRecord(value)\n setIsDirty(true)\n }}\n onAddField={handleAddField}\n onRemoveField={(index) => { void handleRemoveField(index) }}\n onDefinitionChange={handleDefinitionChange}\n onRestoreField={(key) => { void handleRestoreField(key) }}\n onReorder={handleReorder}\n orderNotice={orderDirty ? { dirty: true, message: t('entities.customFields.orderNotice', 'Reordered \u2014 remember to save') } : undefined}\n translate={t}\n />\n )\n\n return (\n <div className=\"flex flex-col gap-3 sm:gap-4\">\n {statusError ? (\n <Alert status=\"error\">\n <AlertTitle>{t('entities.customFields.errors.title', 'Something went wrong')}</AlertTitle>\n <AlertDescription>{statusError}</AlertDescription>\n </Alert>\n ) : null}\n <div className=\"rounded-lg border bg-card p-3 sm:p-4 max-h-[70vh] overflow-y-auto\">\n {content}\n </div>\n <div className=\"flex justify-end gap-2\">\n {fullEditorHref ? (\n <Button type=\"button\" variant=\"ghost\" onClick={handleOpenFullEditor}>\n {openFullLabel}\n </Button>\n ) : null}\n <Button type=\"button\" variant=\"outline\" onClick={onClose} disabled={saving}>\n {cancelLabel}\n </Button>\n <Button type=\"button\" onClick={() => { void submit() }} disabled={saving || !isDirty}>\n {saveLabel}\n </Button>\n </div>\n </div>\n )\n },\n)\n\nFieldDefinitionsManager.displayName = 'FieldDefinitionsManager'\n"],
5
+ "mappings": ";AAsUQ,cA+BE,YA/BF;AApUR,YAAY,WAAW;AACvB,SAAS,sBAAsB;AAC/B,SAAS,cAAc;AACvB,SAAS,eAAe;AACxB,SAAS,OAAO,kBAAkB,kBAAkB;AACpD,SAAS,aAAa;AACtB,SAAS,SAAS,4BAA4B;AAC9C,SAAS,sBAAsB;AAC/B,SAAS,iCAAiC;AAC1C,SAAS,8BAA+E;AACxF,SAAS,kCAAkC;AAC3C,SAAS,YAAY;AAuBrB,SAAS,sBAAsB,OAAsC;AACnE,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAMA,QAAO,MAAM,KAAK;AACxB,WAAOA,QAAO,EAAE,MAAAA,MAAK,IAAI;AAAA,EAC3B;AACA,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,QAAQ;AACd,QAAM,OAAO,OAAO,MAAM,SAAS,WAAW,MAAM,KAAK,KAAK,IAAI;AAClE,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,QAAuB,EAAE,KAAK;AACpC,MAAI,OAAO,MAAM,UAAU,YAAY,MAAM,MAAM,KAAK,EAAG,OAAM,QAAQ,MAAM,MAAM,KAAK;AAC1F,MAAI,OAAO,MAAM,SAAS,YAAY,MAAM,KAAK,KAAK,EAAG,OAAM,OAAO,MAAM,KAAK,KAAK;AACtF,SAAO;AACT;AAEO,MAAM,0BAA0B,MAAM;AAAA,EAC3C,SAASC,yBAAwB,EAAE,UAAU,kBAAkB,MAAM,SAAS,SAAS,eAAe,GAAG,KAAK;AAC5G,UAAM,IAAI,KAAK;AACf,UAAM,cAAc,eAAe;AACnC,UAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,IAAI;AACjD,UAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAS,KAAK;AAChD,UAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAwB,IAAI;AACxE,UAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAA4B,CAAC,CAAC;AAC5D,UAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAmB,CAAC,CAAC;AACjE,UAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAA+C,CAAC,CAAC;AACzF,UAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAA+B,CAAC,CAAC;AACzE,UAAM,CAAC,gBAAgB,iBAAiB,IAAI,MAAM,SAAwB,eAAe;AACzF,UAAM,CAAC,yBAAyB,0BAA0B,IAAI,MAAM,SAAS,IAAI;AACjF,UAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAClD,UAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,KAAK;AAExD,UAAM,kBAAkB,MAAM,YAAY,YAAY;AACpD,UAAI,CAAC,SAAU;AACf,iBAAW,IAAI;AACf,qBAAe,IAAI;AACnB,UAAI;AACF,cAAM,OAAO,MAAM;AAAA,UACjB,6CAA6C,mBAAmB,QAAQ,CAAC;AAAA,UACzE;AAAA,UACA,EAAE,cAAc,EAAE,2CAA2C,kCAAkC,GAAG,UAAU,EAAE,OAAO,CAAC,GAAG,aAAa,CAAC,EAAE,EAAE;AAAA,QAC7I;AACA,cAAM,UAA6B,KAAK,SAAS,CAAC,GAAG,IAAI,CAAC,OAAY;AAAA,UACpE,KAAK,EAAE;AAAA,UACP,MAAM,EAAE;AAAA,UACR,YAAY,EAAE,cAAc,CAAC;AAAA,UAC7B,UAAU,EAAE,aAAa;AAAA,QAC3B,EAAE;AACF,eAAO;AAAA,UACL,CAAC,GAAG,MAAM,OAAO,EAAE,YAAY,YAAY,CAAC,IAAI,OAAO,EAAE,YAAY,YAAY,CAAC;AAAA,QACpF;AACA,gBAAQ,MAAM;AACd,uBAAe,MAAM,QAAQ,KAAK,WAAW,IAAI,KAAK,cAAc,CAAC,CAAC;AACtE,cAAM,kBAAkB,MAAM,QAAQ,KAAK,SAAS,IAAI,KAAK,YAAY,CAAC;AAC1E,qBAAa,eAAe;AAC5B,0BAAkB,CAAC,YAAY;AAC7B,cAAI,mBAAmB,gBAAgB,KAAK,CAAC,OAAO,GAAG,SAAS,eAAe,GAAG;AAChF,mBAAO;AAAA,UACT;AACA,cAAI,WAAW,gBAAgB,KAAK,CAAC,OAAO,GAAG,SAAS,OAAO,EAAG,QAAO;AACzE,iBAAO,gBAAgB,CAAC,GAAG,QAAQ;AAAA,QACrC,CAAC;AACD,mCAA2B,KAAK,UAAU,4BAA4B,KAAK;AAC3E,qBAAa,CAAC,CAAC;AACf,mBAAW,KAAK;AAChB,sBAAc,KAAK;AAAA,MACrB,SAAS,KAAK;AACZ,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,EAAE,2CAA2C,kCAAkC;AACpI,uBAAe,OAAO;AAAA,MACxB,UAAE;AACA,mBAAW,KAAK;AAAA,MAClB;AAAA,IACF,GAAG,CAAC,UAAU,iBAAiB,CAAC,CAAC;AAEjC,UAAM,UAAU,MAAM;AACpB,sBAAgB,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IAClC,GAAG,CAAC,eAAe,CAAC;AAEpB,UAAM,uBAAuB,MAAM,YAAY,MAAM;AACnD,YAAM,WAAW,oBAAI,IAA6B;AAClD,WAAK,QAAQ,CAAC,eAAe;AAC3B,cAAM,OAAO,OAAO,WAAW,YAAY,aAAa,WAAW,WAAW,WAAW,WAAW;AACpG,YAAI,CAAC,KAAM;AACX,cAAM,aAAa,sBAAsB,WAAW,YAAY,KAAK;AACrE,YAAI,CAAC,WAAY;AACjB,cAAM,OAAO,SAAS,IAAI,IAAI,KAAK,CAAC;AACpC,YAAI,CAAC,KAAK,KAAK,CAAC,UAAU,MAAM,SAAS,WAAW,IAAI,GAAG;AACzD,eAAK,KAAK,UAAU;AACpB,mBAAS,IAAI,MAAM,IAAI;AAAA,QACzB;AAAA,MACF,CAAC;AACD,aAAO,UAAU,IAAI,CAAC,QAAQ;AAAA,QAC5B,GAAG;AAAA,QACH,QAAQ,SAAS,IAAI,GAAG,IAAI,KAAK,CAAC;AAAA,MACpC,EAAE;AAAA,IACJ,GAAG,CAAC,MAAM,SAAS,CAAC;AAEpB,UAAM,cAAc,MAAM;AAAA,MACxB,CAAC,eAAsD;AACrD,cAAM,SAAS,2BAA2B,UAAU;AAAA,UAClD;AAAA,UACA,KAAK,WAAW;AAAA,UAChB,MAAM,WAAW;AAAA,UACjB,YAAY,WAAW;AAAA,UACvB,UAAU,WAAW;AAAA,QACvB,CAAC;AACD,YAAI,OAAO,QAAS,QAAO,CAAC;AAC5B,cAAM,OAA6B,CAAC;AACpC,mBAAW,SAAS,OAAO,MAAM,QAAQ;AACvC,cAAI,MAAM,KAAK,SAAS,KAAK,EAAG,MAAK,MAAM,MAAM;AACjD,cAAI,MAAM,KAAK,SAAS,MAAM,EAAG,MAAK,OAAO,MAAM;AAAA,QACrD;AACA,eAAO;AAAA,MACT;AAAA,MACA,CAAC,QAAQ;AAAA,IACX;AAEA,UAAM,cAAc,MAAM,YAAY,MAAM;AAC1C,YAAM,aAAmD,CAAC;AAC1D,WAAK,QAAQ,CAAC,YAAY,UAAU;AAClC,mBAAW,KAAK,IAAI,YAAY,UAAU;AAAA,MAC5C,CAAC;AACD,mBAAa,UAAU;AACvB,aAAO,OAAO,OAAO,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,MAAM,OAAO,CAAC,MAAM,IAAI;AAAA,IAC7E,GAAG,CAAC,MAAM,WAAW,CAAC;AAEtB,UAAM,yBAAyB,MAAM,YAAY,CAAC,OAAe,YAA6B;AAC5F,cAAQ,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,QAAS,QAAQ,QAAQ,UAAU,KAAM,CAAC;AAC3E,mBAAa,CAAC,UAAU,EAAE,GAAG,MAAM,CAAC,KAAK,GAAG,YAAY,OAAO,EAAE,EAAE;AACnE,iBAAW,IAAI;AAAA,IACjB,GAAG,CAAC,WAAW,CAAC;AAEhB,UAAM,iBAAiB,MAAM,YAAY,MAAM;AAC7C,cAAQ,CAAC,QAAQ;AAAA,QACf,GAAG;AAAA,QACH;AAAA,UACE,KAAK;AAAA,UACL,MAAM;AAAA,UACN,YAAY,iBAAiB,EAAE,UAAU,eAAe,IAAI,CAAC;AAAA,UAC7D,UAAU;AAAA,QACZ;AAAA,MACF,CAAC;AACD,iBAAW,IAAI;AAAA,IACjB,GAAG,CAAC,cAAc,CAAC;AAEnB,UAAM,oBAAoB,MAAM,YAAY,OAAO,UAAkB;AACnE,YAAM,MAAM,KAAK,KAAK;AACtB,UAAI,CAAC,IAAK;AACV,UAAI,IAAI,KAAK;AACX,YAAI;AACF,gBAAM,OAAO,MAAM,QAAQ,6BAA6B;AAAA,YACtD,QAAQ;AAAA,YACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,YAC9C,MAAM,KAAK,UAAU,EAAE,UAAU,KAAK,IAAI,IAAI,CAAC;AAAA,UACjD,CAAC;AACD,cAAI,CAAC,KAAK,IAAI;AACZ,kBAAM,eAAe,KAAK,UAAU,EAAE,6CAA6C,wBAAwB,CAAC;AAAA,UAC9G;AAAA,QACF,SAAS,OAAO;AACd,gBAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,EAAE,6CAA6C,wBAAwB;AAChI,gBAAM,SAAS,OAAO;AACtB;AAAA,QACF;AAAA,MACF;AACA,cAAQ,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,QAAQ,QAAQ,KAAK,CAAC;AACtD,iBAAW,IAAI;AAAA,IACjB,GAAG,CAAC,MAAM,UAAU,CAAC,CAAC;AAEtB,UAAM,qBAAqB,MAAM,YAAY,OAAO,QAAgB;AAClE,UAAI;AACF,cAAM,OAAO,MAAM,QAAQ,qCAAqC;AAAA,UAC9D,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,EAAE,UAAU,IAAI,CAAC;AAAA,QACxC,CAAC;AACD,YAAI,CAAC,KAAK,IAAI;AACZ,gBAAM,eAAe,KAAK,UAAU,EAAE,8CAA8C,yBAAyB,CAAC;AAAA,QAChH;AACA,cAAM,EAAE,wCAAwC,gBAAgB,GAAG,SAAS;AAC5E,cAAM,gBAAgB;AAAA,MACxB,SAAS,OAAO;AACd,cAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,EAAE,8CAA8C,yBAAyB;AAClI,cAAM,SAAS,OAAO;AAAA,MACxB;AAAA,IACF,GAAG,CAAC,UAAU,iBAAiB,CAAC,CAAC;AAEjC,UAAM,gBAAgB,MAAM,YAAY,CAAC,MAAc,OAAe;AACpE,cAAQ,CAAC,QAAQ;AACf,cAAM,OAAO,CAAC,GAAG,GAAG;AACpB,cAAM,CAAC,KAAK,IAAI,KAAK,OAAO,MAAM,CAAC;AACnC,aAAK,OAAO,IAAI,GAAG,KAAK;AACxB,eAAO;AAAA,MACT,CAAC;AACD,iBAAW,IAAI;AACf,oBAAc,IAAI;AAAA,IACpB,GAAG,CAAC,CAAC;AAEL,UAAM,wBAAwB,MAAM,YAAY,CAAC,SAA+B;AAC9E,mBAAa,IAAI;AACjB,UAAI,CAAC,KAAK,KAAK,CAAC,OAAO,GAAG,SAAS,cAAc,GAAG;AAClD,0BAAkB,KAAK,CAAC,GAAG,QAAQ,IAAI;AAAA,MACzC;AACA,iBAAW,IAAI;AAAA,IACjB,GAAG,CAAC,cAAc,CAAC;AAEnB,UAAM,2BAA2B,MAAM,YAAY,CAAC,cAAsB,aAAqB;AAC7F,UAAI,CAAC,gBAAgB,CAAC,YAAY,iBAAiB,SAAU;AAC7D;AAAA,QAAQ,CAAC,QACP,IAAI,IAAI,CAAC,UAAU;AACjB,gBAAM,UAAU,OAAO,MAAM,YAAY,aAAa,WAAW,MAAM,WAAW,WAAW;AAC7F,cAAI,YAAY,aAAc,QAAO;AACrC,gBAAM,aAAa,EAAE,GAAI,MAAM,cAAc,CAAC,EAAG;AACjD,qBAAW,WAAW;AACtB,iBAAO,EAAE,GAAG,OAAO,YAAY,WAAW;AAAA,QAC5C,CAAC;AAAA,MACH;AACA,wBAAkB,CAAC,YAAa,YAAY,eAAe,WAAW,OAAQ;AAC9E,iBAAW,IAAI;AAAA,IACjB,GAAG,CAAC,CAAC;AAEL,UAAM,wBAAwB,MAAM,YAAY,CAAC,SAAiB;AAChE,UAAI,CAAC,KAAM;AACX;AAAA,QAAQ,CAAC,QACP,IAAI,IAAI,CAAC,UAAU;AACjB,gBAAM,UAAU,OAAO,MAAM,YAAY,aAAa,WAAW,MAAM,WAAW,WAAW;AAC7F,cAAI,YAAY,KAAM,QAAO;AAC7B,gBAAM,aAAa,EAAE,GAAI,MAAM,cAAc,CAAC,EAAG;AACjD,iBAAO,WAAW;AAClB,iBAAO,WAAW;AAClB,iBAAO,EAAE,GAAG,OAAO,YAAY,WAAW;AAAA,QAC5C,CAAC;AAAA,MACH;AACA,iBAAW,IAAI;AAAA,IACjB,GAAG,CAAC,CAAC;AAEL,UAAM,SAAS,MAAM,YAAY,YAAY;AAC3C,UAAI,CAAC,SAAU;AACf,gBAAU,IAAI;AACd,qBAAe,IAAI;AACnB,UAAI;AACF,YAAI,CAAC,YAAY,GAAG;AAClB,gBAAM,EAAE,2CAA2C,8BAA8B,GAAG,OAAO;AAC3F,gBAAM,IAAI,MAAM,EAAE,2CAA2C,mBAAmB,CAAC;AAAA,QACnF;AACA,cAAM,UAAU;AAAA,UACd;AAAA,UACA,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,OAAO;AAAA,YACnD,KAAK,EAAE;AAAA,YACP,MAAM,EAAE;AAAA,YACR,YAAY,EAAE;AAAA,YACd,UAAU,EAAE,aAAa;AAAA,UAC3B,EAAE;AAAA,UACF,WAAW,qBAAqB;AAAA,UAChC;AAAA,QACF;AACA,cAAM,OAAO,MAAM,QAAQ,mCAAmC;AAAA,UAC5D,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,QAC9B,CAAC;AACD,YAAI,CAAC,KAAK,IAAI;AACZ,gBAAM,eAAe,KAAK,UAAU,EAAE,2CAA2C,4BAA4B,CAAC;AAAA,QAChH;AACA,cAAM,0BAA0B,aAAa,QAAQ;AACrD,cAAM,EAAE,qCAAqC,mBAAmB,GAAG,SAAS;AAC5E,mBAAW,KAAK;AAChB,sBAAc,KAAK;AACnB,YAAI,QAAS,OAAM,QAAQ;AAC3B,gBAAQ;AAAA,MACV,SAAS,KAAK;AACZ,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,EAAE,2CAA2C,4BAA4B;AAC9H,uBAAe,OAAO;AAAA,MACxB,UAAE;AACA,kBAAU,KAAK;AAAA,MACjB;AAAA,IACF,GAAG,CAAC,sBAAsB,MAAM,UAAU,SAAS,SAAS,aAAa,yBAAyB,GAAG,WAAW,CAAC;AAEjH,UAAM,oBAAoB,KAAK,OAAO,EAAE,OAAO,IAAI,CAAC,MAAM,CAAC;AAE3D,UAAM,cAAc,EAAE,2BAA2B,QAAQ;AACzD,UAAM,YAAY,SAAS,EAAE,0BAA0B,WAAW,IAAI,EAAE,yBAAyB,MAAM;AACvG,UAAM,gBAAgB,EAAE,sCAAsC,kBAAkB;AAEhF,UAAM,uBAAuB,MAAM,YAAY,MAAM;AACnD,UAAI,CAAC,kBAAkB,OAAO,WAAW,YAAa;AACtD,aAAO,KAAK,gBAAgB,UAAU,qBAAqB;AAAA,IAC7D,GAAG,CAAC,cAAc,CAAC;AAEnB,UAAM,UAAU,UACd,oBAAC,SAAI,WAAU,yCACb,8BAAC,WAAQ,MAAK,MAAK,GACrB,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,aAAa;AAAA,QACb,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,QACA,wBAAwB;AAAA,QACxB,mBAAmB;AAAA,QACnB,sBAAsB;AAAA,QACtB,mBAAmB;AAAA,QACnB;AAAA,QACA,iCAAiC,CAAC,UAAU;AAC1C,qCAA2B,KAAK;AAChC,qBAAW,IAAI;AAAA,QACjB;AAAA,QACA,YAAY;AAAA,QACZ,eAAe,CAAC,UAAU;AAAE,eAAK,kBAAkB,KAAK;AAAA,QAAE;AAAA,QAC1D,oBAAoB;AAAA,QACpB,gBAAgB,CAAC,QAAQ;AAAE,eAAK,mBAAmB,GAAG;AAAA,QAAE;AAAA,QACxD,WAAW;AAAA,QACX,aAAa,aAAa,EAAE,OAAO,MAAM,SAAS,EAAE,qCAAqC,mCAA8B,EAAE,IAAI;AAAA,QAC7H,WAAW;AAAA;AAAA,IACb;AAGF,WACE,qBAAC,SAAI,WAAU,gCACZ;AAAA,oBACC,qBAAC,SAAM,QAAO,SACZ;AAAA,4BAAC,cAAY,YAAE,sCAAsC,sBAAsB,GAAE;AAAA,QAC7E,oBAAC,oBAAkB,uBAAY;AAAA,SACjC,IACE;AAAA,MACJ,oBAAC,SAAI,WAAU,qEACZ,mBACH;AAAA,MACA,qBAAC,SAAI,WAAU,0BACZ;AAAA,yBACC,oBAAC,UAAO,MAAK,UAAS,SAAQ,SAAQ,SAAS,sBAC5C,yBACH,IACE;AAAA,QACJ,oBAAC,UAAO,MAAK,UAAS,SAAQ,WAAU,SAAS,SAAS,UAAU,QACjE,uBACH;AAAA,QACA,oBAAC,UAAO,MAAK,UAAS,SAAS,MAAM;AAAE,eAAK,OAAO;AAAA,QAAE,GAAG,UAAU,UAAU,CAAC,SAC1E,qBACH;AAAA,SACF;AAAA,OACF;AAAA,EAEJ;AACF;AAEA,wBAAwB,cAAc;",
6
6
  "names": ["code", "FieldDefinitionsManager"]
7
7
  }
@@ -280,14 +280,14 @@ function DashboardScreen() {
280
280
  return /* @__PURE__ */ jsx("div", { className: "flex min-h-[320px] items-center justify-center", children: /* @__PURE__ */ jsx(Spinner, { size: "lg" }) });
281
281
  }
282
282
  if (error && layout.length === 0) {
283
- return /* @__PURE__ */ jsxs(Alert, { variant: "destructive", children: [
283
+ return /* @__PURE__ */ jsxs(Alert, { status: "error", children: [
284
284
  /* @__PURE__ */ jsx(AlertTitle, { children: t("dashboard.unavailable") }),
285
285
  /* @__PURE__ */ jsx(AlertDescription, { children: error }),
286
286
  /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: handleRefresh, children: t("dashboard.retry") }) })
287
287
  ] });
288
288
  }
289
289
  if (!hasRegisteredWidgets && layout.length === 0) {
290
- return /* @__PURE__ */ jsxs(Alert, { variant: "info", children: [
290
+ return /* @__PURE__ */ jsxs(Alert, { status: "information", children: [
291
291
  /* @__PURE__ */ jsx(AlertTitle, { children: t("dashboard.empty.noWidgets.title", "No dashboard widgets yet") }),
292
292
  /* @__PURE__ */ jsx(AlertDescription, { children: t(
293
293
  "dashboard.empty.noWidgets.description",
@@ -312,7 +312,7 @@ function DashboardScreen() {
312
312
  ] })
313
313
  ] })
314
314
  ] }),
315
- error && layout.length > 0 && /* @__PURE__ */ jsxs(Alert, { variant: "destructive", children: [
315
+ error && layout.length > 0 && /* @__PURE__ */ jsxs(Alert, { status: "error", children: [
316
316
  /* @__PURE__ */ jsx(AlertTitle, { children: t("dashboard.error.partial") }),
317
317
  /* @__PURE__ */ jsx(AlertDescription, { children: error }),
318
318
  /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: handleRefresh, children: t("dashboard.error.reload") }) })
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/dashboard/DashboardScreen.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { getDashboardWidgets, loadDashboardWidgetModule } from './widgetRegistry'\nimport type { DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { GripVertical, Plus, RefreshCw, Settings2, Trash2, X, Loader2 } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { InjectionSpot } from '../injection/InjectionSpot'\nimport { WidgetDataBatchProvider } from './widgetData'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('ui').child({ component: 'DashboardScreen' })\n\ntype DashboardWidgetSize = 'sm' | 'md' | 'lg'\n\ntype LayoutItem = {\n id: string\n widgetId: string\n order: number\n priority?: number\n size?: DashboardWidgetSize\n settings?: unknown\n}\n\ntype WidgetMeta = {\n id: string\n title: string\n description: string | null\n defaultSize: DashboardWidgetSize\n defaultEnabled: boolean\n defaultSettings: unknown\n features: string[]\n moduleId: string\n icon: string | null\n loaderKey: string\n supportsRefresh: boolean\n}\n\ntype LayoutContext = {\n userId: string\n tenantId: string | null\n organizationId: string | null\n userName: string | null\n userEmail: string | null\n userLabel: string | null\n}\n\ntype LayoutResponse = {\n layout: { items: LayoutItem[] }\n widgets: WidgetMeta[]\n allowedWidgetIds: string[]\n canConfigure: boolean\n context: LayoutContext\n}\n\ntype WidgetModule = DashboardWidgetModule<any>\n\nfunction sizeClass(size: DashboardWidgetSize | undefined) {\n switch (size) {\n case 'lg':\n return 'md:col-span-2'\n case 'md':\n return 'md:col-span-1'\n case 'sm':\n default:\n return 'md:col-span-1'\n }\n}\n\nfunction sortLayout(items: LayoutItem[]): LayoutItem[] {\n return [...items]\n .sort((a, b) => {\n const aOrder = a.order ?? a.priority ?? 0\n const bOrder = b.order ?? b.priority ?? 0\n return aOrder - bOrder\n })\n .map((item, index) => ({ ...item, order: index, priority: index }))\n}\n\nconst DEFAULT_SIZE: DashboardWidgetSize = 'md'\n\nfunction generateId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n // Fallback: timestamp + random for better uniqueness\n return Date.now().toString(36) + Math.random().toString(36).slice(2)\n}\n\nexport function DashboardScreen() {\n const t = useT()\n const organizationScopeVersion = useOrganizationScopeVersion()\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [hasRegisteredWidgets, setHasRegisteredWidgets] = React.useState(true)\n const [saving, setSaving] = React.useState(false)\n const [layout, setLayout] = React.useState<LayoutItem[]>([])\n const [widgetCatalog, setWidgetCatalog] = React.useState<WidgetMeta[]>([])\n const [allowedWidgetIds, setAllowedWidgetIds] = React.useState<string[]>([])\n const [canConfigure, setCanConfigure] = React.useState(false)\n const [context, setContext] = React.useState<LayoutContext | null>(null)\n const [editing, setEditing] = React.useState(false)\n const [settingsId, setSettingsId] = React.useState<string | null>(null)\n const pendingOpsRef = React.useRef(0)\n const saveQueueRef = React.useRef(Promise.resolve())\n const draggingIdRef = React.useRef<string | null>(null)\n\n const adjustSaving = React.useCallback((delta: number) => {\n pendingOpsRef.current = Math.max(0, pendingOpsRef.current + delta)\n setSaving(pendingOpsRef.current > 0)\n }, [])\n\n const load = React.useCallback(async () => {\n setLoading(true)\n setError(null)\n try {\n const call = await apiCall<LayoutResponse>('/api/dashboards/layout')\n if (!call.ok || !call.result) {\n throw new Error(`Failed with status ${call.status}`)\n }\n const data = call.result\n const registeredWidgetCount = getDashboardWidgets().length\n const normalizedLayout = sortLayout(data.layout?.items ?? [])\n setLayout(normalizedLayout)\n setWidgetCatalog(data.widgets ?? [])\n setHasRegisteredWidgets(registeredWidgetCount > 0 || (data.widgets ?? []).length > 0)\n setAllowedWidgetIds(data.allowedWidgetIds ?? [])\n setCanConfigure(!!data.canConfigure)\n if (data.context) {\n setContext({\n userId: data.context.userId,\n tenantId: data.context.tenantId ?? null,\n organizationId: data.context.organizationId ?? null,\n userName: data.context.userName ?? null,\n userEmail: data.context.userEmail ?? null,\n userLabel: data.context.userLabel ?? null,\n })\n } else {\n setContext(null)\n }\n if (!data.canConfigure) {\n setEditing(false)\n setSettingsId(null)\n }\n } catch (err) {\n logger.error('Failed to load dashboard layout', { err })\n if (getDashboardWidgets().length === 0) {\n setHasRegisteredWidgets(false)\n setLayout([])\n setWidgetCatalog([])\n setAllowedWidgetIds([])\n setCanConfigure(false)\n setContext(null)\n setEditing(false)\n setSettingsId(null)\n return\n }\n setError(t('dashboard.loadError'))\n } finally {\n setLoading(false)\n }\n }, [t])\n\n React.useEffect(() => {\n load()\n }, [load, organizationScopeVersion])\n\n const metaById = React.useMemo(() => {\n const map = new Map<string, WidgetMeta>()\n for (const meta of widgetCatalog) map.set(meta.id, meta)\n return map\n }, [widgetCatalog])\n\n const availableWidgets = React.useMemo(() => {\n const currentIds = new Set(layout.map((item) => item.widgetId))\n return widgetCatalog.filter((meta) => !currentIds.has(meta.id))\n }, [layout, widgetCatalog])\n\n const resolveWidgetTitle = React.useCallback((meta: WidgetMeta): string => {\n const keys = [\n `${meta.id}.title`,\n `dashboard.widgets.${meta.id}.title`,\n ]\n if (meta.id.includes('.')) {\n const parts = meta.id.split('.')\n const lastPart = parts.pop()\n keys.unshift(`${parts.join('.')}.widgets.${lastPart}.title`)\n }\n for (const key of keys) {\n const translated = t(key)\n if (translated !== key) return translated\n }\n return meta.title\n }, [t])\n\n const resolveWidgetDescription = React.useCallback((meta: WidgetMeta): string | null => {\n if (!meta.description) return null\n const keys = [\n `${meta.id}.description`,\n `dashboard.widgets.${meta.id}.description`,\n ]\n if (meta.id.includes('.')) {\n const parts = meta.id.split('.')\n const lastPart = parts.pop()\n keys.unshift(`${parts.join('.')}.widgets.${lastPart}.description`)\n }\n for (const key of keys) {\n const translated = t(key)\n if (translated !== key) return translated\n }\n return meta.description\n }, [t])\n\n const queueLayoutSave = React.useCallback((items: LayoutItem[]) => {\n saveQueueRef.current = saveQueueRef.current.then(async () => {\n adjustSaving(1)\n try {\n const payload = {\n items: items.map((item, index) => ({\n id: item.id,\n widgetId: item.widgetId,\n order: index,\n priority: index,\n size: item.size ?? DEFAULT_SIZE,\n settings: item.settings ?? null,\n })),\n }\n const call = await apiCall('/api/dashboards/layout', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!call.ok) throw new Error(`Failed with status ${call.status}`)\n setError(null)\n } catch (err) {\n logger.error('Failed to save layout', { err })\n setError(t('dashboard.saveError'))\n } finally {\n adjustSaving(-1)\n }\n })\n }, [adjustSaving, t])\n\n const patchWidgetSettings = React.useCallback(async (itemId: string, nextSettings: unknown) => {\n adjustSaving(1)\n try {\n const call = await apiCall(`/api/dashboards/layout/${encodeURIComponent(itemId)}`, {\n method: 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ settings: nextSettings }),\n })\n if (!call.ok) throw new Error(`Failed with status ${call.status}`)\n setError(null)\n } catch (err) {\n logger.error('Failed to update widget settings', { err })\n setError(t('dashboard.saveError'))\n } finally {\n adjustSaving(-1)\n }\n }, [adjustSaving, t])\n\n const handleAddWidget = React.useCallback((widgetId: string) => {\n const meta = metaById.get(widgetId)\n if (!meta) return\n setLayout((prev) => {\n const next: LayoutItem[] = sortLayout([\n ...prev,\n {\n id: generateId(),\n widgetId: meta.id,\n order: prev.length,\n priority: prev.length,\n size: meta.defaultSize ?? DEFAULT_SIZE,\n settings: meta.defaultSettings ?? null,\n },\n ])\n queueLayoutSave(next)\n return next\n })\n setSettingsId(null)\n }, [metaById, queueLayoutSave])\n\n const handleRemoveWidget = React.useCallback((itemId: string) => {\n setLayout((prev) => {\n const next = sortLayout(prev.filter((item) => item.id !== itemId))\n queueLayoutSave(next)\n return next\n })\n if (settingsId === itemId) setSettingsId(null)\n }, [queueLayoutSave, settingsId])\n\n const handleReorder = React.useCallback((dragId: string | null, targetId: string) => {\n if (!dragId || dragId === targetId) return\n setLayout((prev) => {\n const items = [...prev]\n const from = items.findIndex((item) => item.id === dragId)\n const to = items.findIndex((item) => item.id === targetId)\n if (from === -1 || to === -1) return prev\n const [moved] = items.splice(from, 1)\n items.splice(to, 0, moved)\n const next = items.map((item, index) => ({\n ...item,\n order: index,\n priority: index,\n }))\n queueLayoutSave(next)\n return next\n })\n }, [queueLayoutSave])\n\n const handleSettingsChange = React.useCallback((itemId: string, nextSettings: unknown) => {\n setLayout((prev) => prev.map((item) => (item.id === itemId ? { ...item, settings: nextSettings } : item)))\n void patchWidgetSettings(itemId, nextSettings)\n }, [patchWidgetSettings])\n\n const toggleEditing = React.useCallback(() => {\n if (!canConfigure) return\n setEditing((prev) => {\n const next = !prev\n if (!next) setSettingsId(null)\n return next\n })\n }, [canConfigure])\n\n const handleRefresh = React.useCallback(() => {\n load()\n }, [load])\n\n const injectionContext = React.useMemo(\n () => ({\n layout,\n widgetCatalog,\n allowedWidgetIds,\n canConfigure,\n editing,\n userContext: context,\n }),\n [allowedWidgetIds, canConfigure, context, editing, layout, widgetCatalog],\n )\n const dashboardBeforeSpotId = 'dashboard:before'\n const dashboardAfterSpotId = 'dashboard:after'\n\n if (loading) {\n return (\n <div className=\"flex min-h-[320px] items-center justify-center\">\n <Spinner size=\"lg\" />\n </div>\n )\n }\n\n if (error && layout.length === 0) {\n return (\n <Alert variant=\"destructive\">\n <AlertTitle>{t('dashboard.unavailable')}</AlertTitle>\n <AlertDescription>{error}</AlertDescription>\n <div className=\"mt-2\"><Button variant=\"outline\" onClick={handleRefresh}>{t('dashboard.retry')}</Button></div>\n </Alert>\n )\n }\n\n if (!hasRegisteredWidgets && layout.length === 0) {\n return (\n <Alert variant=\"info\">\n <AlertTitle>{t('dashboard.empty.noWidgets.title', 'No dashboard widgets yet')}</AlertTitle>\n <AlertDescription>\n {t(\n 'dashboard.empty.noWidgets.description',\n 'After you add the first module that exposes dashboard widgets, they will appear here.',\n )}\n </AlertDescription>\n </Alert>\n )\n }\n\n return (\n <div className=\"space-y-6\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div>\n <h1 className=\"text-2xl font-semibold tracking-tight\">{t('dashboard.title')}</h1>\n <p className=\"text-sm text-muted-foreground\">{t('dashboard.subtitle')}</p>\n </div>\n <div className=\"flex items-center gap-2\">\n {saving && (\n <div className=\"flex items-center gap-1 text-sm text-muted-foreground\">\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n <span>{t('dashboard.saving')}</span>\n </div>\n )}\n {canConfigure && (\n <Button variant={editing ? 'secondary' : 'outline'} size=\"sm\" onClick={toggleEditing}>\n <Settings2 className=\"h-4 w-4\" />\n <span>{editing ? t('dashboard.action.done') : t('dashboard.action.customize')}</span>\n </Button>\n )}\n </div>\n </div>\n\n {error && layout.length > 0 && (\n <Alert variant=\"destructive\">\n <AlertTitle>{t('dashboard.error.partial')}</AlertTitle>\n <AlertDescription>{error}</AlertDescription>\n <div className=\"mt-2\"><Button variant=\"ghost\" onClick={handleRefresh}>{t('dashboard.error.reload')}</Button></div>\n </Alert>\n )}\n\n <InjectionSpot spotId={dashboardBeforeSpotId} context={injectionContext} />\n\n {editing && availableWidgets.length > 0 && (\n <div className=\"rounded-lg border border-dashed bg-muted/50 p-4\">\n <div className=\"mb-2 text-sm font-medium text-muted-foreground\">{t('dashboard.addWidget')}</div>\n <div className=\"flex flex-wrap gap-2\">\n {availableWidgets.map((meta) => (\n <Button\n key={meta.id}\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleAddWidget(meta.id)}\n >\n <Plus className=\"h-4 w-4\" />\n {resolveWidgetTitle(meta)}\n </Button>\n ))}\n </div>\n </div>\n )}\n\n <WidgetDataBatchProvider>\n <div className={cn(\n 'grid gap-3 sm:gap-4 md:gap-6',\n 'grid-cols-1',\n 'md:grid-cols-2',\n 'xl:grid-cols-3'\n )}\n onDragOver={(event) => {\n if (!editing || !canConfigure) return\n event.preventDefault()\n event.dataTransfer.dropEffect = 'move'\n }}\n onDrop={(event) => {\n if (!editing || !canConfigure) return\n event.preventDefault()\n const dragId = event.dataTransfer.getData('text/plain') || draggingIdRef.current\n if (!dragId) return\n setLayout((prev) => {\n const items = [...prev]\n const from = items.findIndex((entry) => entry.id === dragId)\n if (from === -1) return prev\n const [moved] = items.splice(from, 1)\n items.push(moved)\n const next = items.map((item, index) => ({\n ...item,\n order: index,\n priority: index,\n }))\n queueLayoutSave(next)\n return next\n })\n draggingIdRef.current = null\n }}>\n {layout.map((item) => {\n const meta = metaById.get(item.widgetId)\n if (!meta) return null\n const title = resolveWidgetTitle(meta)\n const description = resolveWidgetDescription(meta)\n return (\n <DashboardWidgetCard\n key={item.id}\n item={item}\n meta={meta}\n title={title}\n description={description}\n context={context}\n editing={editing && canConfigure}\n activeSettings={settingsId === item.id}\n onToggleSettings={() => setSettingsId((current) => (current === item.id ? null : item.id))}\n onRemove={() => handleRemoveWidget(item.id)}\n onSettingsChange={(settings) => handleSettingsChange(item.id, settings)}\n onDragStart={() => { draggingIdRef.current = item.id }}\n onDragEnd={() => { draggingIdRef.current = null }}\n onDrop={(event) => {\n const dragId = event.dataTransfer.getData('text/plain') || draggingIdRef.current\n handleReorder(dragId, item.id)\n draggingIdRef.current = null\n }}\n onDragEnter={() => {}}\n onDragLeave={() => {}}\n sizeClass={sizeClass(item.size)}\n />\n )\n })}\n </div>\n </WidgetDataBatchProvider>\n\n {layout.length === 0 && (\n <div className=\"rounded-lg border border-dashed bg-muted/30 p-10 text-center text-sm text-muted-foreground\">\n {!hasRegisteredWidgets\n ? t(\n 'dashboard.empty.noWidgets.description',\n 'After you add the first module that exposes dashboard widgets, they will appear here.',\n )\n : canConfigure ? t('dashboard.empty.configurable') : t('dashboard.empty.readonly')}\n </div>\n )}\n\n <InjectionSpot spotId={dashboardAfterSpotId} context={injectionContext} />\n </div>\n )\n}\n\ntype DashboardWidgetCardProps = {\n item: LayoutItem\n meta: WidgetMeta\n title: string\n description: string | null\n context: LayoutContext | null\n editing: boolean\n activeSettings: boolean\n onToggleSettings: () => void\n onRemove: () => void\n onSettingsChange: (next: unknown) => void\n onDragStart: () => void\n onDragEnd: () => void\n onDrop: (event: React.DragEvent<HTMLDivElement>) => void\n onDragEnter: () => void\n onDragLeave: () => void\n sizeClass: string\n}\n\nfunction DashboardWidgetCard({\n item,\n meta,\n title,\n description,\n context,\n editing,\n activeSettings,\n onToggleSettings,\n onRemove,\n onSettingsChange,\n onDragStart,\n onDragEnd,\n onDrop,\n onDragEnter,\n onDragLeave,\n sizeClass,\n}: DashboardWidgetCardProps) {\n const t = useT()\n const [module, setModule] = React.useState<WidgetModule | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [loadError, setLoadError] = React.useState<string | null>(null)\n const [isDragOver, setIsDragOver] = React.useState(false)\n const [refreshToken, setRefreshToken] = React.useState(0)\n const [refreshing, setRefreshing] = React.useState(false)\n\n React.useEffect(() => {\n let cancelled = false\n setLoading(true)\n setLoadError(null)\n loadDashboardWidgetModule(meta.loaderKey)\n .then((loaded) => {\n if (cancelled) return\n if (!loaded) {\n setModule(null)\n setLoadError(t('dashboard.widget.loadError'))\n setLoading(false)\n return\n }\n setModule(loaded)\n setLoading(false)\n })\n .catch((err) => {\n if (cancelled) return\n logger.error('Failed to load widget module', { err })\n setLoadError(t('dashboard.widget.loadError'))\n setLoading(false)\n })\n return () => { cancelled = true }\n }, [meta.loaderKey, t])\n\n React.useEffect(() => {\n if (!meta.supportsRefresh) {\n setRefreshing(false)\n }\n }, [meta.supportsRefresh])\n\n React.useEffect(() => {\n if (activeSettings) {\n setRefreshing(false)\n }\n }, [activeSettings])\n\n React.useEffect(() => {\n if (loadError) {\n setRefreshing(false)\n }\n }, [loadError])\n\n const handleRefreshStateChange = React.useCallback((next: boolean) => {\n setRefreshing(next)\n }, [])\n\n const triggerRefresh = React.useCallback(() => {\n if (loading || !!loadError) return\n setRefreshing(true)\n setRefreshToken((current) => current + 1)\n }, [loadError, loading])\n\n const hydratedSettings = React.useMemo(() => {\n const raw = item.settings ?? meta.defaultSettings ?? null\n if (module?.hydrateSettings) {\n try {\n return module.hydrateSettings(raw)\n } catch (err) {\n logger.warn('Failed to hydrate widget settings', { err })\n return raw\n }\n }\n return raw\n }, [item.settings, meta.defaultSettings, module])\n\n const handleSettingsChange = React.useCallback((next: unknown) => {\n let raw = next\n if (module?.dehydrateSettings) {\n try {\n raw = module.dehydrateSettings(next as never)\n } catch (err) {\n logger.warn('Failed to dehydrate widget settings', { err })\n }\n }\n onSettingsChange(raw)\n }, [module, onSettingsChange])\n\n const WidgetComponent = module?.Widget\n const mode = activeSettings ? 'settings' : 'view'\n\n return (\n <div\n className={cn(\n 'group relative flex h-full flex-col rounded-lg border bg-background shadow-sm transition',\n isDragOver ? 'border-primary ring-2 ring-primary/20' : 'hover:border-primary/40',\n editing ? 'cursor-grab' : 'cursor-default',\n sizeClass\n )}\n draggable={editing}\n onDragStart={(event) => {\n if (!editing) return\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', item.id)\n onDragStart()\n }}\n onDragEnd={() => {\n if (!editing) return\n onDragEnd()\n }}\n onDragOver={(event) => {\n if (!editing) return\n event.preventDefault()\n event.stopPropagation()\n event.dataTransfer.dropEffect = 'move'\n if (!isDragOver) {\n setIsDragOver(true)\n onDragEnter()\n }\n }}\n onDrop={(event) => {\n if (!editing) return\n event.preventDefault()\n event.stopPropagation()\n onDrop(event)\n setIsDragOver(false)\n onDragLeave()\n }}\n onDragLeave={(event) => {\n if (!editing) return\n event.stopPropagation()\n if (event.currentTarget.contains(event.relatedTarget as Node)) return\n setIsDragOver(false)\n onDragLeave()\n }}\n >\n <div className=\"flex items-center justify-between gap-2 border-b px-3 py-2\">\n <div className=\"flex items-center gap-2\">\n {editing && <GripVertical className=\"h-4 w-4 text-muted-foreground\" />}\n <div>\n <div className=\"text-sm font-medium leading-none\">{title}</div>\n {description ? <div className=\"mt-1 text-xs text-muted-foreground\">{description}</div> : null}\n </div>\n </div>\n <div className=\"flex items-center gap-1\">\n {!editing && meta.supportsRefresh && (\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n disabled={refreshing || loading || !!loadError}\n onClick={triggerRefresh}\n aria-label={t('dashboard.widget.refresh')}\n >\n {refreshing ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCw className=\"h-4 w-4\" />}\n </IconButton>\n )}\n {editing && (\n <>\n <IconButton\n variant={activeSettings ? 'outline' : 'ghost'}\n size=\"sm\"\n onClick={onToggleSettings}\n aria-label={activeSettings ? t('dashboard.widget.closeSettings') : t('dashboard.widget.editSettings')}\n >\n {activeSettings ? <X className=\"h-4 w-4\" /> : <Settings2 className=\"h-4 w-4\" />}\n </IconButton>\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n onClick={onRemove}\n aria-label={t('dashboard.widget.remove')}\n >\n <Trash2 className=\"h-4 w-4\" />\n </IconButton>\n </>\n )}\n </div>\n </div>\n <div className=\"flex-1 p-4\">\n {loading && (\n <div className=\"flex h-full min-h-[120px] items-center justify-center\">\n <Spinner />\n </div>\n )}\n {loadError && !loading && (\n <div className=\"text-sm text-muted-foreground\">{loadError}</div>\n )}\n {!loading && !loadError && WidgetComponent && (\n <WidgetComponent\n mode={mode as 'view' | 'settings'}\n layout={item}\n context={context ?? { userId: '', tenantId: null, organizationId: null, userName: null, userEmail: null, userLabel: null }}\n settings={hydratedSettings}\n onSettingsChange={handleSettingsChange}\n refreshToken={refreshToken}\n onRefreshStateChange={handleRefreshStateChange}\n />\n )}\n </div>\n </div>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { getDashboardWidgets, loadDashboardWidgetModule } from './widgetRegistry'\nimport type { DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { GripVertical, Plus, RefreshCw, Settings2, Trash2, X, Loader2 } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { InjectionSpot } from '../injection/InjectionSpot'\nimport { WidgetDataBatchProvider } from './widgetData'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('ui').child({ component: 'DashboardScreen' })\n\ntype DashboardWidgetSize = 'sm' | 'md' | 'lg'\n\ntype LayoutItem = {\n id: string\n widgetId: string\n order: number\n priority?: number\n size?: DashboardWidgetSize\n settings?: unknown\n}\n\ntype WidgetMeta = {\n id: string\n title: string\n description: string | null\n defaultSize: DashboardWidgetSize\n defaultEnabled: boolean\n defaultSettings: unknown\n features: string[]\n moduleId: string\n icon: string | null\n loaderKey: string\n supportsRefresh: boolean\n}\n\ntype LayoutContext = {\n userId: string\n tenantId: string | null\n organizationId: string | null\n userName: string | null\n userEmail: string | null\n userLabel: string | null\n}\n\ntype LayoutResponse = {\n layout: { items: LayoutItem[] }\n widgets: WidgetMeta[]\n allowedWidgetIds: string[]\n canConfigure: boolean\n context: LayoutContext\n}\n\ntype WidgetModule = DashboardWidgetModule<any>\n\nfunction sizeClass(size: DashboardWidgetSize | undefined) {\n switch (size) {\n case 'lg':\n return 'md:col-span-2'\n case 'md':\n return 'md:col-span-1'\n case 'sm':\n default:\n return 'md:col-span-1'\n }\n}\n\nfunction sortLayout(items: LayoutItem[]): LayoutItem[] {\n return [...items]\n .sort((a, b) => {\n const aOrder = a.order ?? a.priority ?? 0\n const bOrder = b.order ?? b.priority ?? 0\n return aOrder - bOrder\n })\n .map((item, index) => ({ ...item, order: index, priority: index }))\n}\n\nconst DEFAULT_SIZE: DashboardWidgetSize = 'md'\n\nfunction generateId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n // Fallback: timestamp + random for better uniqueness\n return Date.now().toString(36) + Math.random().toString(36).slice(2)\n}\n\nexport function DashboardScreen() {\n const t = useT()\n const organizationScopeVersion = useOrganizationScopeVersion()\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [hasRegisteredWidgets, setHasRegisteredWidgets] = React.useState(true)\n const [saving, setSaving] = React.useState(false)\n const [layout, setLayout] = React.useState<LayoutItem[]>([])\n const [widgetCatalog, setWidgetCatalog] = React.useState<WidgetMeta[]>([])\n const [allowedWidgetIds, setAllowedWidgetIds] = React.useState<string[]>([])\n const [canConfigure, setCanConfigure] = React.useState(false)\n const [context, setContext] = React.useState<LayoutContext | null>(null)\n const [editing, setEditing] = React.useState(false)\n const [settingsId, setSettingsId] = React.useState<string | null>(null)\n const pendingOpsRef = React.useRef(0)\n const saveQueueRef = React.useRef(Promise.resolve())\n const draggingIdRef = React.useRef<string | null>(null)\n\n const adjustSaving = React.useCallback((delta: number) => {\n pendingOpsRef.current = Math.max(0, pendingOpsRef.current + delta)\n setSaving(pendingOpsRef.current > 0)\n }, [])\n\n const load = React.useCallback(async () => {\n setLoading(true)\n setError(null)\n try {\n const call = await apiCall<LayoutResponse>('/api/dashboards/layout')\n if (!call.ok || !call.result) {\n throw new Error(`Failed with status ${call.status}`)\n }\n const data = call.result\n const registeredWidgetCount = getDashboardWidgets().length\n const normalizedLayout = sortLayout(data.layout?.items ?? [])\n setLayout(normalizedLayout)\n setWidgetCatalog(data.widgets ?? [])\n setHasRegisteredWidgets(registeredWidgetCount > 0 || (data.widgets ?? []).length > 0)\n setAllowedWidgetIds(data.allowedWidgetIds ?? [])\n setCanConfigure(!!data.canConfigure)\n if (data.context) {\n setContext({\n userId: data.context.userId,\n tenantId: data.context.tenantId ?? null,\n organizationId: data.context.organizationId ?? null,\n userName: data.context.userName ?? null,\n userEmail: data.context.userEmail ?? null,\n userLabel: data.context.userLabel ?? null,\n })\n } else {\n setContext(null)\n }\n if (!data.canConfigure) {\n setEditing(false)\n setSettingsId(null)\n }\n } catch (err) {\n logger.error('Failed to load dashboard layout', { err })\n if (getDashboardWidgets().length === 0) {\n setHasRegisteredWidgets(false)\n setLayout([])\n setWidgetCatalog([])\n setAllowedWidgetIds([])\n setCanConfigure(false)\n setContext(null)\n setEditing(false)\n setSettingsId(null)\n return\n }\n setError(t('dashboard.loadError'))\n } finally {\n setLoading(false)\n }\n }, [t])\n\n React.useEffect(() => {\n load()\n }, [load, organizationScopeVersion])\n\n const metaById = React.useMemo(() => {\n const map = new Map<string, WidgetMeta>()\n for (const meta of widgetCatalog) map.set(meta.id, meta)\n return map\n }, [widgetCatalog])\n\n const availableWidgets = React.useMemo(() => {\n const currentIds = new Set(layout.map((item) => item.widgetId))\n return widgetCatalog.filter((meta) => !currentIds.has(meta.id))\n }, [layout, widgetCatalog])\n\n const resolveWidgetTitle = React.useCallback((meta: WidgetMeta): string => {\n const keys = [\n `${meta.id}.title`,\n `dashboard.widgets.${meta.id}.title`,\n ]\n if (meta.id.includes('.')) {\n const parts = meta.id.split('.')\n const lastPart = parts.pop()\n keys.unshift(`${parts.join('.')}.widgets.${lastPart}.title`)\n }\n for (const key of keys) {\n const translated = t(key)\n if (translated !== key) return translated\n }\n return meta.title\n }, [t])\n\n const resolveWidgetDescription = React.useCallback((meta: WidgetMeta): string | null => {\n if (!meta.description) return null\n const keys = [\n `${meta.id}.description`,\n `dashboard.widgets.${meta.id}.description`,\n ]\n if (meta.id.includes('.')) {\n const parts = meta.id.split('.')\n const lastPart = parts.pop()\n keys.unshift(`${parts.join('.')}.widgets.${lastPart}.description`)\n }\n for (const key of keys) {\n const translated = t(key)\n if (translated !== key) return translated\n }\n return meta.description\n }, [t])\n\n const queueLayoutSave = React.useCallback((items: LayoutItem[]) => {\n saveQueueRef.current = saveQueueRef.current.then(async () => {\n adjustSaving(1)\n try {\n const payload = {\n items: items.map((item, index) => ({\n id: item.id,\n widgetId: item.widgetId,\n order: index,\n priority: index,\n size: item.size ?? DEFAULT_SIZE,\n settings: item.settings ?? null,\n })),\n }\n const call = await apiCall('/api/dashboards/layout', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!call.ok) throw new Error(`Failed with status ${call.status}`)\n setError(null)\n } catch (err) {\n logger.error('Failed to save layout', { err })\n setError(t('dashboard.saveError'))\n } finally {\n adjustSaving(-1)\n }\n })\n }, [adjustSaving, t])\n\n const patchWidgetSettings = React.useCallback(async (itemId: string, nextSettings: unknown) => {\n adjustSaving(1)\n try {\n const call = await apiCall(`/api/dashboards/layout/${encodeURIComponent(itemId)}`, {\n method: 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ settings: nextSettings }),\n })\n if (!call.ok) throw new Error(`Failed with status ${call.status}`)\n setError(null)\n } catch (err) {\n logger.error('Failed to update widget settings', { err })\n setError(t('dashboard.saveError'))\n } finally {\n adjustSaving(-1)\n }\n }, [adjustSaving, t])\n\n const handleAddWidget = React.useCallback((widgetId: string) => {\n const meta = metaById.get(widgetId)\n if (!meta) return\n setLayout((prev) => {\n const next: LayoutItem[] = sortLayout([\n ...prev,\n {\n id: generateId(),\n widgetId: meta.id,\n order: prev.length,\n priority: prev.length,\n size: meta.defaultSize ?? DEFAULT_SIZE,\n settings: meta.defaultSettings ?? null,\n },\n ])\n queueLayoutSave(next)\n return next\n })\n setSettingsId(null)\n }, [metaById, queueLayoutSave])\n\n const handleRemoveWidget = React.useCallback((itemId: string) => {\n setLayout((prev) => {\n const next = sortLayout(prev.filter((item) => item.id !== itemId))\n queueLayoutSave(next)\n return next\n })\n if (settingsId === itemId) setSettingsId(null)\n }, [queueLayoutSave, settingsId])\n\n const handleReorder = React.useCallback((dragId: string | null, targetId: string) => {\n if (!dragId || dragId === targetId) return\n setLayout((prev) => {\n const items = [...prev]\n const from = items.findIndex((item) => item.id === dragId)\n const to = items.findIndex((item) => item.id === targetId)\n if (from === -1 || to === -1) return prev\n const [moved] = items.splice(from, 1)\n items.splice(to, 0, moved)\n const next = items.map((item, index) => ({\n ...item,\n order: index,\n priority: index,\n }))\n queueLayoutSave(next)\n return next\n })\n }, [queueLayoutSave])\n\n const handleSettingsChange = React.useCallback((itemId: string, nextSettings: unknown) => {\n setLayout((prev) => prev.map((item) => (item.id === itemId ? { ...item, settings: nextSettings } : item)))\n void patchWidgetSettings(itemId, nextSettings)\n }, [patchWidgetSettings])\n\n const toggleEditing = React.useCallback(() => {\n if (!canConfigure) return\n setEditing((prev) => {\n const next = !prev\n if (!next) setSettingsId(null)\n return next\n })\n }, [canConfigure])\n\n const handleRefresh = React.useCallback(() => {\n load()\n }, [load])\n\n const injectionContext = React.useMemo(\n () => ({\n layout,\n widgetCatalog,\n allowedWidgetIds,\n canConfigure,\n editing,\n userContext: context,\n }),\n [allowedWidgetIds, canConfigure, context, editing, layout, widgetCatalog],\n )\n const dashboardBeforeSpotId = 'dashboard:before'\n const dashboardAfterSpotId = 'dashboard:after'\n\n if (loading) {\n return (\n <div className=\"flex min-h-[320px] items-center justify-center\">\n <Spinner size=\"lg\" />\n </div>\n )\n }\n\n if (error && layout.length === 0) {\n return (\n <Alert status=\"error\">\n <AlertTitle>{t('dashboard.unavailable')}</AlertTitle>\n <AlertDescription>{error}</AlertDescription>\n <div className=\"mt-2\"><Button variant=\"outline\" onClick={handleRefresh}>{t('dashboard.retry')}</Button></div>\n </Alert>\n )\n }\n\n if (!hasRegisteredWidgets && layout.length === 0) {\n return (\n <Alert status=\"information\">\n <AlertTitle>{t('dashboard.empty.noWidgets.title', 'No dashboard widgets yet')}</AlertTitle>\n <AlertDescription>\n {t(\n 'dashboard.empty.noWidgets.description',\n 'After you add the first module that exposes dashboard widgets, they will appear here.',\n )}\n </AlertDescription>\n </Alert>\n )\n }\n\n return (\n <div className=\"space-y-6\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div>\n <h1 className=\"text-2xl font-semibold tracking-tight\">{t('dashboard.title')}</h1>\n <p className=\"text-sm text-muted-foreground\">{t('dashboard.subtitle')}</p>\n </div>\n <div className=\"flex items-center gap-2\">\n {saving && (\n <div className=\"flex items-center gap-1 text-sm text-muted-foreground\">\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n <span>{t('dashboard.saving')}</span>\n </div>\n )}\n {canConfigure && (\n <Button variant={editing ? 'secondary' : 'outline'} size=\"sm\" onClick={toggleEditing}>\n <Settings2 className=\"h-4 w-4\" />\n <span>{editing ? t('dashboard.action.done') : t('dashboard.action.customize')}</span>\n </Button>\n )}\n </div>\n </div>\n\n {error && layout.length > 0 && (\n <Alert status=\"error\">\n <AlertTitle>{t('dashboard.error.partial')}</AlertTitle>\n <AlertDescription>{error}</AlertDescription>\n <div className=\"mt-2\"><Button variant=\"ghost\" onClick={handleRefresh}>{t('dashboard.error.reload')}</Button></div>\n </Alert>\n )}\n\n <InjectionSpot spotId={dashboardBeforeSpotId} context={injectionContext} />\n\n {editing && availableWidgets.length > 0 && (\n <div className=\"rounded-lg border border-dashed bg-muted/50 p-4\">\n <div className=\"mb-2 text-sm font-medium text-muted-foreground\">{t('dashboard.addWidget')}</div>\n <div className=\"flex flex-wrap gap-2\">\n {availableWidgets.map((meta) => (\n <Button\n key={meta.id}\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleAddWidget(meta.id)}\n >\n <Plus className=\"h-4 w-4\" />\n {resolveWidgetTitle(meta)}\n </Button>\n ))}\n </div>\n </div>\n )}\n\n <WidgetDataBatchProvider>\n <div className={cn(\n 'grid gap-3 sm:gap-4 md:gap-6',\n 'grid-cols-1',\n 'md:grid-cols-2',\n 'xl:grid-cols-3'\n )}\n onDragOver={(event) => {\n if (!editing || !canConfigure) return\n event.preventDefault()\n event.dataTransfer.dropEffect = 'move'\n }}\n onDrop={(event) => {\n if (!editing || !canConfigure) return\n event.preventDefault()\n const dragId = event.dataTransfer.getData('text/plain') || draggingIdRef.current\n if (!dragId) return\n setLayout((prev) => {\n const items = [...prev]\n const from = items.findIndex((entry) => entry.id === dragId)\n if (from === -1) return prev\n const [moved] = items.splice(from, 1)\n items.push(moved)\n const next = items.map((item, index) => ({\n ...item,\n order: index,\n priority: index,\n }))\n queueLayoutSave(next)\n return next\n })\n draggingIdRef.current = null\n }}>\n {layout.map((item) => {\n const meta = metaById.get(item.widgetId)\n if (!meta) return null\n const title = resolveWidgetTitle(meta)\n const description = resolveWidgetDescription(meta)\n return (\n <DashboardWidgetCard\n key={item.id}\n item={item}\n meta={meta}\n title={title}\n description={description}\n context={context}\n editing={editing && canConfigure}\n activeSettings={settingsId === item.id}\n onToggleSettings={() => setSettingsId((current) => (current === item.id ? null : item.id))}\n onRemove={() => handleRemoveWidget(item.id)}\n onSettingsChange={(settings) => handleSettingsChange(item.id, settings)}\n onDragStart={() => { draggingIdRef.current = item.id }}\n onDragEnd={() => { draggingIdRef.current = null }}\n onDrop={(event) => {\n const dragId = event.dataTransfer.getData('text/plain') || draggingIdRef.current\n handleReorder(dragId, item.id)\n draggingIdRef.current = null\n }}\n onDragEnter={() => {}}\n onDragLeave={() => {}}\n sizeClass={sizeClass(item.size)}\n />\n )\n })}\n </div>\n </WidgetDataBatchProvider>\n\n {layout.length === 0 && (\n <div className=\"rounded-lg border border-dashed bg-muted/30 p-10 text-center text-sm text-muted-foreground\">\n {!hasRegisteredWidgets\n ? t(\n 'dashboard.empty.noWidgets.description',\n 'After you add the first module that exposes dashboard widgets, they will appear here.',\n )\n : canConfigure ? t('dashboard.empty.configurable') : t('dashboard.empty.readonly')}\n </div>\n )}\n\n <InjectionSpot spotId={dashboardAfterSpotId} context={injectionContext} />\n </div>\n )\n}\n\ntype DashboardWidgetCardProps = {\n item: LayoutItem\n meta: WidgetMeta\n title: string\n description: string | null\n context: LayoutContext | null\n editing: boolean\n activeSettings: boolean\n onToggleSettings: () => void\n onRemove: () => void\n onSettingsChange: (next: unknown) => void\n onDragStart: () => void\n onDragEnd: () => void\n onDrop: (event: React.DragEvent<HTMLDivElement>) => void\n onDragEnter: () => void\n onDragLeave: () => void\n sizeClass: string\n}\n\nfunction DashboardWidgetCard({\n item,\n meta,\n title,\n description,\n context,\n editing,\n activeSettings,\n onToggleSettings,\n onRemove,\n onSettingsChange,\n onDragStart,\n onDragEnd,\n onDrop,\n onDragEnter,\n onDragLeave,\n sizeClass,\n}: DashboardWidgetCardProps) {\n const t = useT()\n const [module, setModule] = React.useState<WidgetModule | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [loadError, setLoadError] = React.useState<string | null>(null)\n const [isDragOver, setIsDragOver] = React.useState(false)\n const [refreshToken, setRefreshToken] = React.useState(0)\n const [refreshing, setRefreshing] = React.useState(false)\n\n React.useEffect(() => {\n let cancelled = false\n setLoading(true)\n setLoadError(null)\n loadDashboardWidgetModule(meta.loaderKey)\n .then((loaded) => {\n if (cancelled) return\n if (!loaded) {\n setModule(null)\n setLoadError(t('dashboard.widget.loadError'))\n setLoading(false)\n return\n }\n setModule(loaded)\n setLoading(false)\n })\n .catch((err) => {\n if (cancelled) return\n logger.error('Failed to load widget module', { err })\n setLoadError(t('dashboard.widget.loadError'))\n setLoading(false)\n })\n return () => { cancelled = true }\n }, [meta.loaderKey, t])\n\n React.useEffect(() => {\n if (!meta.supportsRefresh) {\n setRefreshing(false)\n }\n }, [meta.supportsRefresh])\n\n React.useEffect(() => {\n if (activeSettings) {\n setRefreshing(false)\n }\n }, [activeSettings])\n\n React.useEffect(() => {\n if (loadError) {\n setRefreshing(false)\n }\n }, [loadError])\n\n const handleRefreshStateChange = React.useCallback((next: boolean) => {\n setRefreshing(next)\n }, [])\n\n const triggerRefresh = React.useCallback(() => {\n if (loading || !!loadError) return\n setRefreshing(true)\n setRefreshToken((current) => current + 1)\n }, [loadError, loading])\n\n const hydratedSettings = React.useMemo(() => {\n const raw = item.settings ?? meta.defaultSettings ?? null\n if (module?.hydrateSettings) {\n try {\n return module.hydrateSettings(raw)\n } catch (err) {\n logger.warn('Failed to hydrate widget settings', { err })\n return raw\n }\n }\n return raw\n }, [item.settings, meta.defaultSettings, module])\n\n const handleSettingsChange = React.useCallback((next: unknown) => {\n let raw = next\n if (module?.dehydrateSettings) {\n try {\n raw = module.dehydrateSettings(next as never)\n } catch (err) {\n logger.warn('Failed to dehydrate widget settings', { err })\n }\n }\n onSettingsChange(raw)\n }, [module, onSettingsChange])\n\n const WidgetComponent = module?.Widget\n const mode = activeSettings ? 'settings' : 'view'\n\n return (\n <div\n className={cn(\n 'group relative flex h-full flex-col rounded-lg border bg-background shadow-sm transition',\n isDragOver ? 'border-primary ring-2 ring-primary/20' : 'hover:border-primary/40',\n editing ? 'cursor-grab' : 'cursor-default',\n sizeClass\n )}\n draggable={editing}\n onDragStart={(event) => {\n if (!editing) return\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', item.id)\n onDragStart()\n }}\n onDragEnd={() => {\n if (!editing) return\n onDragEnd()\n }}\n onDragOver={(event) => {\n if (!editing) return\n event.preventDefault()\n event.stopPropagation()\n event.dataTransfer.dropEffect = 'move'\n if (!isDragOver) {\n setIsDragOver(true)\n onDragEnter()\n }\n }}\n onDrop={(event) => {\n if (!editing) return\n event.preventDefault()\n event.stopPropagation()\n onDrop(event)\n setIsDragOver(false)\n onDragLeave()\n }}\n onDragLeave={(event) => {\n if (!editing) return\n event.stopPropagation()\n if (event.currentTarget.contains(event.relatedTarget as Node)) return\n setIsDragOver(false)\n onDragLeave()\n }}\n >\n <div className=\"flex items-center justify-between gap-2 border-b px-3 py-2\">\n <div className=\"flex items-center gap-2\">\n {editing && <GripVertical className=\"h-4 w-4 text-muted-foreground\" />}\n <div>\n <div className=\"text-sm font-medium leading-none\">{title}</div>\n {description ? <div className=\"mt-1 text-xs text-muted-foreground\">{description}</div> : null}\n </div>\n </div>\n <div className=\"flex items-center gap-1\">\n {!editing && meta.supportsRefresh && (\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n disabled={refreshing || loading || !!loadError}\n onClick={triggerRefresh}\n aria-label={t('dashboard.widget.refresh')}\n >\n {refreshing ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCw className=\"h-4 w-4\" />}\n </IconButton>\n )}\n {editing && (\n <>\n <IconButton\n variant={activeSettings ? 'outline' : 'ghost'}\n size=\"sm\"\n onClick={onToggleSettings}\n aria-label={activeSettings ? t('dashboard.widget.closeSettings') : t('dashboard.widget.editSettings')}\n >\n {activeSettings ? <X className=\"h-4 w-4\" /> : <Settings2 className=\"h-4 w-4\" />}\n </IconButton>\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n onClick={onRemove}\n aria-label={t('dashboard.widget.remove')}\n >\n <Trash2 className=\"h-4 w-4\" />\n </IconButton>\n </>\n )}\n </div>\n </div>\n <div className=\"flex-1 p-4\">\n {loading && (\n <div className=\"flex h-full min-h-[120px] items-center justify-center\">\n <Spinner />\n </div>\n )}\n {loadError && !loading && (\n <div className=\"text-sm text-muted-foreground\">{loadError}</div>\n )}\n {!loading && !loadError && WidgetComponent && (\n <WidgetComponent\n mode={mode as 'view' | 'settings'}\n layout={item}\n context={context ?? { userId: '', tenantId: null, organizationId: null, userName: null, userEmail: null, userLabel: null }}\n settings={hydratedSettings}\n onSettingsChange={handleSettingsChange}\n refreshToken={refreshToken}\n onRefreshStateChange={handleRefreshStateChange}\n />\n )}\n </div>\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["sizeClass"]
7
7
  }