@open-mercato/ui 0.5.1-develop.2860.07af3a6a9d → 0.5.1-develop.2874.77704bccbd

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 (246) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +204 -121
  3. package/dist/backend/AppShell.js +25 -28
  4. package/dist/backend/AppShell.js.map +2 -2
  5. package/dist/backend/ContextHelp.js +1 -1
  6. package/dist/backend/ContextHelp.js.map +1 -1
  7. package/dist/backend/CrudForm.js +12 -15
  8. package/dist/backend/CrudForm.js.map +2 -2
  9. package/dist/backend/DataTable.js +9 -10
  10. package/dist/backend/DataTable.js.map +2 -2
  11. package/dist/backend/FilterBar.js +6 -8
  12. package/dist/backend/FilterBar.js.map +2 -2
  13. package/dist/backend/FilterOverlay.js +10 -10
  14. package/dist/backend/FilterOverlay.js.map +2 -2
  15. package/dist/backend/FlashMessages.js +1 -1
  16. package/dist/backend/FlashMessages.js.map +2 -2
  17. package/dist/backend/JsonBuilder.js +6 -6
  18. package/dist/backend/JsonBuilder.js.map +1 -1
  19. package/dist/backend/NextStepCallout.js +1 -1
  20. package/dist/backend/NextStepCallout.js.map +1 -1
  21. package/dist/backend/PerspectiveSidebar.js +2 -2
  22. package/dist/backend/PerspectiveSidebar.js.map +2 -2
  23. package/dist/backend/ProfileDropdown.js +1 -1
  24. package/dist/backend/ProfileDropdown.js.map +1 -1
  25. package/dist/backend/RowActions.js +1 -1
  26. package/dist/backend/RowActions.js.map +1 -1
  27. package/dist/backend/UserMenu.js +2 -2
  28. package/dist/backend/UserMenu.js.map +1 -1
  29. package/dist/backend/WebhookSetupGuide.js +11 -11
  30. package/dist/backend/WebhookSetupGuide.js.map +2 -2
  31. package/dist/backend/charts/KpiCard.js +3 -3
  32. package/dist/backend/charts/KpiCard.js.map +1 -1
  33. package/dist/backend/columns/ColumnChooserPanel.js +1 -1
  34. package/dist/backend/columns/ColumnChooserPanel.js.map +2 -2
  35. package/dist/backend/custom-fields/FieldDefinitionsEditor.js +3 -3
  36. package/dist/backend/custom-fields/FieldDefinitionsEditor.js.map +2 -2
  37. package/dist/backend/dashboard/DashboardScreen.js +1 -1
  38. package/dist/backend/dashboard/DashboardScreen.js.map +1 -1
  39. package/dist/backend/date-range/DateRangeSelect.js +1 -1
  40. package/dist/backend/date-range/DateRangeSelect.js.map +1 -1
  41. package/dist/backend/date-range/InlineDateRangeSelect.js +1 -1
  42. package/dist/backend/date-range/InlineDateRangeSelect.js.map +1 -1
  43. package/dist/backend/detail/AccessDeniedMessage.js +1 -1
  44. package/dist/backend/detail/AccessDeniedMessage.js.map +1 -1
  45. package/dist/backend/detail/ActivitiesSection.js +5 -5
  46. package/dist/backend/detail/ActivitiesSection.js.map +1 -1
  47. package/dist/backend/detail/AddressEditor.js +3 -3
  48. package/dist/backend/detail/AddressEditor.js.map +2 -2
  49. package/dist/backend/detail/AddressTiles.js +3 -3
  50. package/dist/backend/detail/AddressTiles.js.map +2 -2
  51. package/dist/backend/detail/AttachmentMetadataDialog.js +1 -1
  52. package/dist/backend/detail/AttachmentMetadataDialog.js.map +1 -1
  53. package/dist/backend/detail/CustomDataSection.js +1 -1
  54. package/dist/backend/detail/CustomDataSection.js.map +1 -1
  55. package/dist/backend/detail/InlineEditors.js +5 -5
  56. package/dist/backend/detail/InlineEditors.js.map +1 -1
  57. package/dist/backend/detail/NotesSection.js +6 -6
  58. package/dist/backend/detail/NotesSection.js.map +1 -1
  59. package/dist/backend/detail/TagsSection.js +1 -1
  60. package/dist/backend/detail/TagsSection.js.map +1 -1
  61. package/dist/backend/devtools/UmesDevToolsPanel.js +6 -6
  62. package/dist/backend/devtools/UmesDevToolsPanel.js.map +2 -2
  63. package/dist/backend/devtools/components/ConflictWarnings.js +3 -3
  64. package/dist/backend/devtools/components/ConflictWarnings.js.map +2 -2
  65. package/dist/backend/devtools/components/EnricherTiming.js +2 -2
  66. package/dist/backend/devtools/components/EnricherTiming.js.map +2 -2
  67. package/dist/backend/devtools/components/EventFlow.js +5 -5
  68. package/dist/backend/devtools/components/EventFlow.js.map +2 -2
  69. package/dist/backend/devtools/components/ExtensionPointList.js +3 -3
  70. package/dist/backend/devtools/components/ExtensionPointList.js.map +2 -2
  71. package/dist/backend/devtools/components/InterceptorActivity.js +6 -6
  72. package/dist/backend/devtools/components/InterceptorActivity.js.map +2 -2
  73. package/dist/backend/forms/ActionsDropdown.js +1 -1
  74. package/dist/backend/forms/ActionsDropdown.js.map +1 -1
  75. package/dist/backend/forms/FormActionButtons.js +2 -3
  76. package/dist/backend/forms/FormActionButtons.js.map +2 -2
  77. package/dist/backend/indexes/PartialIndexBanner.js +8 -8
  78. package/dist/backend/indexes/PartialIndexBanner.js.map +2 -2
  79. package/dist/backend/inputs/ComboboxInput.js +1 -1
  80. package/dist/backend/inputs/ComboboxInput.js.map +2 -2
  81. package/dist/backend/inputs/DatePicker.js +3 -3
  82. package/dist/backend/inputs/DatePicker.js.map +1 -1
  83. package/dist/backend/inputs/DateTimePicker.js +3 -3
  84. package/dist/backend/inputs/DateTimePicker.js.map +1 -1
  85. package/dist/backend/inputs/EventSelect.js +1 -1
  86. package/dist/backend/inputs/EventSelect.js.map +2 -2
  87. package/dist/backend/inputs/LookupSelect.js +1 -1
  88. package/dist/backend/inputs/LookupSelect.js.map +1 -1
  89. package/dist/backend/inputs/SwitchableMarkdownInput.js +1 -1
  90. package/dist/backend/inputs/SwitchableMarkdownInput.js.map +1 -1
  91. package/dist/backend/inputs/TagsInput.js +2 -2
  92. package/dist/backend/inputs/TagsInput.js.map +2 -2
  93. package/dist/backend/inputs/TimeInput.js +1 -1
  94. package/dist/backend/inputs/TimeInput.js.map +1 -1
  95. package/dist/backend/inputs/TimePicker.js +3 -3
  96. package/dist/backend/inputs/TimePicker.js.map +1 -1
  97. package/dist/backend/messages/MessageObjectDetail.js +1 -1
  98. package/dist/backend/messages/MessageObjectDetail.js.map +1 -1
  99. package/dist/backend/messages/MessageObjectPreview.js +1 -1
  100. package/dist/backend/messages/MessageObjectPreview.js.map +1 -1
  101. package/dist/backend/messages/message-compose-form-groups.js +3 -3
  102. package/dist/backend/messages/message-compose-form-groups.js.map +1 -1
  103. package/dist/backend/notifications/NotificationCountBadge.js +1 -1
  104. package/dist/backend/notifications/NotificationCountBadge.js.map +2 -2
  105. package/dist/backend/notifications/NotificationPanel.js +3 -3
  106. package/dist/backend/notifications/NotificationPanel.js.map +1 -1
  107. package/dist/backend/progress/ProgressTopBar.js +4 -4
  108. package/dist/backend/progress/ProgressTopBar.js.map +2 -2
  109. package/dist/backend/schedule/ScheduleAgenda.js +1 -1
  110. package/dist/backend/schedule/ScheduleAgenda.js.map +2 -2
  111. package/dist/backend/schedule/ScheduleCalendar.js +1 -1
  112. package/dist/backend/schedule/ScheduleCalendar.js.map +1 -1
  113. package/dist/backend/schedule/ScheduleGrid.js +1 -1
  114. package/dist/backend/schedule/ScheduleGrid.js.map +2 -2
  115. package/dist/backend/version-history/VersionHistoryPanel.js +4 -4
  116. package/dist/backend/version-history/VersionHistoryPanel.js.map +2 -2
  117. package/dist/frontend/AuthFooter.js +1 -1
  118. package/dist/frontend/AuthFooter.js.map +1 -1
  119. package/dist/frontend/LanguageSwitcher.js +1 -1
  120. package/dist/frontend/LanguageSwitcher.js.map +1 -1
  121. package/dist/frontend/Layout.js +2 -2
  122. package/dist/frontend/Layout.js.map +1 -1
  123. package/dist/index.js +5 -0
  124. package/dist/index.js.map +2 -2
  125. package/dist/portal/PortalShell.js +15 -15
  126. package/dist/portal/PortalShell.js.map +2 -2
  127. package/dist/portal/components/PortalCard.js +2 -2
  128. package/dist/portal/components/PortalCard.js.map +2 -2
  129. package/dist/portal/components/PortalNotificationPanel.js +18 -18
  130. package/dist/portal/components/PortalNotificationPanel.js.map +2 -2
  131. package/dist/portal/components/PortalPageHeader.js +1 -1
  132. package/dist/portal/components/PortalPageHeader.js.map +2 -2
  133. package/dist/primitives/avatar.js +11 -1
  134. package/dist/primitives/avatar.js.map +2 -2
  135. package/dist/primitives/badge.js +1 -1
  136. package/dist/primitives/badge.js.map +1 -1
  137. package/dist/primitives/button.js +9 -5
  138. package/dist/primitives/button.js.map +2 -2
  139. package/dist/primitives/calendar.js +1 -1
  140. package/dist/primitives/calendar.js.map +1 -1
  141. package/dist/primitives/checkbox-field.js +63 -0
  142. package/dist/primitives/checkbox-field.js.map +7 -0
  143. package/dist/primitives/checkbox.js +31 -17
  144. package/dist/primitives/checkbox.js.map +2 -2
  145. package/dist/primitives/dialog.js +4 -4
  146. package/dist/primitives/dialog.js.map +1 -1
  147. package/dist/primitives/fancy-button.js +72 -0
  148. package/dist/primitives/fancy-button.js.map +7 -0
  149. package/dist/primitives/icon-button.js +20 -4
  150. package/dist/primitives/icon-button.js.map +2 -2
  151. package/dist/primitives/kbd.js +27 -0
  152. package/dist/primitives/kbd.js.map +7 -0
  153. package/dist/primitives/link-button.js +56 -0
  154. package/dist/primitives/link-button.js.map +7 -0
  155. package/dist/primitives/popover.js +1 -1
  156. package/dist/primitives/popover.js.map +1 -1
  157. package/dist/primitives/social-button.js +61 -0
  158. package/dist/primitives/social-button.js.map +7 -0
  159. package/dist/primitives/tabs.js +1 -1
  160. package/dist/primitives/tabs.js.map +1 -1
  161. package/dist/primitives/tag.js +45 -0
  162. package/dist/primitives/tag.js.map +7 -0
  163. package/dist/primitives/tooltip.js +1 -1
  164. package/dist/primitives/tooltip.js.map +1 -1
  165. package/package.json +3 -3
  166. package/src/backend/AppShell.tsx +25 -28
  167. package/src/backend/ContextHelp.tsx +1 -1
  168. package/src/backend/CrudForm.tsx +12 -15
  169. package/src/backend/DataTable.tsx +9 -10
  170. package/src/backend/FilterBar.tsx +6 -5
  171. package/src/backend/FilterOverlay.tsx +10 -10
  172. package/src/backend/FlashMessages.tsx +1 -1
  173. package/src/backend/JsonBuilder.tsx +6 -6
  174. package/src/backend/NextStepCallout.tsx +1 -1
  175. package/src/backend/PerspectiveSidebar.tsx +2 -2
  176. package/src/backend/ProfileDropdown.tsx +1 -1
  177. package/src/backend/RowActions.tsx +1 -1
  178. package/src/backend/UserMenu.tsx +2 -2
  179. package/src/backend/WebhookSetupGuide.tsx +11 -11
  180. package/src/backend/charts/KpiCard.tsx +3 -3
  181. package/src/backend/columns/ColumnChooserPanel.tsx +1 -1
  182. package/src/backend/custom-fields/FieldDefinitionsEditor.tsx +3 -3
  183. package/src/backend/dashboard/DashboardScreen.tsx +1 -1
  184. package/src/backend/date-range/DateRangeSelect.tsx +1 -1
  185. package/src/backend/date-range/InlineDateRangeSelect.tsx +1 -1
  186. package/src/backend/detail/AccessDeniedMessage.tsx +1 -1
  187. package/src/backend/detail/ActivitiesSection.tsx +5 -5
  188. package/src/backend/detail/AddressEditor.tsx +3 -3
  189. package/src/backend/detail/AddressTiles.tsx +3 -3
  190. package/src/backend/detail/AttachmentMetadataDialog.tsx +1 -1
  191. package/src/backend/detail/CustomDataSection.tsx +1 -1
  192. package/src/backend/detail/InlineEditors.tsx +5 -5
  193. package/src/backend/detail/NotesSection.tsx +6 -6
  194. package/src/backend/detail/TagsSection.tsx +1 -1
  195. package/src/backend/devtools/UmesDevToolsPanel.tsx +6 -6
  196. package/src/backend/devtools/components/ConflictWarnings.tsx +4 -4
  197. package/src/backend/devtools/components/EnricherTiming.tsx +2 -2
  198. package/src/backend/devtools/components/EventFlow.tsx +5 -5
  199. package/src/backend/devtools/components/ExtensionPointList.tsx +3 -3
  200. package/src/backend/devtools/components/InterceptorActivity.tsx +6 -6
  201. package/src/backend/forms/ActionsDropdown.tsx +1 -1
  202. package/src/backend/forms/FormActionButtons.tsx +4 -5
  203. package/src/backend/indexes/PartialIndexBanner.tsx +8 -8
  204. package/src/backend/inputs/ComboboxInput.tsx +1 -1
  205. package/src/backend/inputs/DatePicker.tsx +3 -3
  206. package/src/backend/inputs/DateTimePicker.tsx +3 -3
  207. package/src/backend/inputs/EventSelect.tsx +1 -1
  208. package/src/backend/inputs/LookupSelect.tsx +1 -1
  209. package/src/backend/inputs/SwitchableMarkdownInput.tsx +1 -1
  210. package/src/backend/inputs/TagsInput.tsx +2 -2
  211. package/src/backend/inputs/TimeInput.tsx +1 -1
  212. package/src/backend/inputs/TimePicker.tsx +3 -3
  213. package/src/backend/messages/MessageObjectDetail.tsx +1 -1
  214. package/src/backend/messages/MessageObjectPreview.tsx +1 -1
  215. package/src/backend/messages/message-compose-form-groups.tsx +3 -3
  216. package/src/backend/notifications/NotificationCountBadge.tsx +1 -1
  217. package/src/backend/notifications/NotificationPanel.tsx +3 -3
  218. package/src/backend/progress/ProgressTopBar.tsx +4 -4
  219. package/src/backend/schedule/ScheduleAgenda.tsx +1 -1
  220. package/src/backend/schedule/ScheduleCalendar.tsx +1 -1
  221. package/src/backend/schedule/ScheduleGrid.tsx +1 -1
  222. package/src/backend/version-history/VersionHistoryPanel.tsx +4 -4
  223. package/src/frontend/AuthFooter.tsx +1 -1
  224. package/src/frontend/LanguageSwitcher.tsx +1 -1
  225. package/src/frontend/Layout.tsx +2 -2
  226. package/src/index.ts +6 -1
  227. package/src/portal/PortalShell.tsx +15 -15
  228. package/src/portal/components/PortalCard.tsx +2 -2
  229. package/src/portal/components/PortalNotificationPanel.tsx +18 -18
  230. package/src/portal/components/PortalPageHeader.tsx +1 -1
  231. package/src/primitives/avatar.tsx +22 -0
  232. package/src/primitives/badge.tsx +1 -1
  233. package/src/primitives/button.tsx +12 -5
  234. package/src/primitives/calendar.tsx +1 -1
  235. package/src/primitives/checkbox-field.tsx +85 -0
  236. package/src/primitives/checkbox.tsx +44 -18
  237. package/src/primitives/dialog.tsx +4 -4
  238. package/src/primitives/fancy-button.tsx +89 -0
  239. package/src/primitives/icon-button.tsx +19 -2
  240. package/src/primitives/kbd.tsx +38 -0
  241. package/src/primitives/link-button.tsx +55 -0
  242. package/src/primitives/popover.tsx +1 -1
  243. package/src/primitives/social-button.tsx +80 -0
  244. package/src/primitives/tabs.tsx +1 -1
  245. package/src/primitives/tag.tsx +66 -0
  246. package/src/primitives/tooltip.tsx +1 -1
@@ -296,7 +296,7 @@ function DashboardScreen() {
296
296
  }
297
297
  ),
298
298
  /* @__PURE__ */ jsx(InjectionSpot, { spotId: dashboardBeforeSpotId, context: injectionContext }),
299
- editing && availableWidgets.length > 0 && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-dashed bg-muted/40 p-4", children: [
299
+ editing && availableWidgets.length > 0 && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-dashed bg-muted/50 p-4", children: [
300
300
  /* @__PURE__ */ jsx("div", { className: "mb-2 text-sm font-medium text-muted-foreground", children: t("dashboard.addWidget") }),
301
301
  /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: availableWidgets.map((meta) => /* @__PURE__ */ jsxs(
302
302
  Button,
@@ -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 { ErrorNotice } from '@open-mercato/ui/primitives/ErrorNotice'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { 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 { InjectionSpot } from '../injection/InjectionSpot'\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 [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\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 normalizedLayout = sortLayout(data.layout?.items ?? [])\n setLayout(normalizedLayout)\n setWidgetCatalog(data.widgets ?? [])\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 console.error('Failed to load dashboard layout', err)\n setError(t('dashboard.loadError'))\n } finally {\n setLoading(false)\n }\n }, [t])\n\n React.useEffect(() => {\n load()\n }, [load])\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 console.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 console.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 <ErrorNotice\n title={t('dashboard.unavailable')}\n message={error}\n action={<Button variant=\"outline\" onClick={handleRefresh}>{t('dashboard.retry')}</Button>}\n />\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 <ErrorNotice\n title={t('dashboard.error.partial')}\n message={error}\n action={<Button variant=\"ghost\" onClick={handleRefresh}>{t('dashboard.error.reload')}</Button>}\n />\n )}\n\n <InjectionSpot spotId={dashboardBeforeSpotId} context={injectionContext} />\n\n {editing && availableWidgets.length > 0 && (\n <div className=\"rounded-lg border border-dashed bg-muted/40 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 <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\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 {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 setModule(loaded)\n setLoading(false)\n })\n .catch((err) => {\n if (cancelled) return\n console.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 console.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 console.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"],
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 { ErrorNotice } from '@open-mercato/ui/primitives/ErrorNotice'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { 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 { InjectionSpot } from '../injection/InjectionSpot'\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 [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\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 normalizedLayout = sortLayout(data.layout?.items ?? [])\n setLayout(normalizedLayout)\n setWidgetCatalog(data.widgets ?? [])\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 console.error('Failed to load dashboard layout', err)\n setError(t('dashboard.loadError'))\n } finally {\n setLoading(false)\n }\n }, [t])\n\n React.useEffect(() => {\n load()\n }, [load])\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 console.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 console.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 <ErrorNotice\n title={t('dashboard.unavailable')}\n message={error}\n action={<Button variant=\"outline\" onClick={handleRefresh}>{t('dashboard.retry')}</Button>}\n />\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 <ErrorNotice\n title={t('dashboard.error.partial')}\n message={error}\n action={<Button variant=\"ghost\" onClick={handleRefresh}>{t('dashboard.error.reload')}</Button>}\n />\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 <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\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 {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 setModule(loaded)\n setLoading(false)\n })\n .catch((err) => {\n if (cancelled) return\n console.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 console.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 console.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
5
  "mappings": "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",
6
6
  "names": ["sizeClass"]
7
7
  }
@@ -23,7 +23,7 @@ function DateRangeSelect({
23
23
  "select",
24
24
  {
25
25
  id,
26
- className: "w-full rounded-md border bg-background px-2 py-1 text-sm text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary",
26
+ className: "w-full rounded-md border bg-background px-2 py-1 text-sm text-foreground focus-visible:border-ring focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
27
27
  value,
28
28
  onChange: (e) => onChange(e.target.value),
29
29
  children: DATE_RANGE_OPTIONS.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: t(option.labelKey, option.value.replace(/_/g, " ")) }, option.value))
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/date-range/DateRangeSelect.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { DATE_RANGE_OPTIONS, type DateRangePreset } from './dateRanges'\n\nexport type DateRangeSelectProps = {\n value: DateRangePreset\n onChange: (value: DateRangePreset) => void\n id?: string\n label?: string\n className?: string\n}\n\nexport function DateRangeSelect({\n value,\n onChange,\n id = 'date-range-select',\n label,\n className = '',\n}: DateRangeSelectProps) {\n const t = useT()\n\n return (\n <div className={`space-y-1.5 ${className}`}>\n {label && (\n <label\n htmlFor={id}\n className=\"text-xs font-semibold uppercase text-muted-foreground\"\n >\n {label}\n </label>\n )}\n <select\n id={id}\n className=\"w-full rounded-md border bg-background px-2 py-1 text-sm text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary\"\n value={value}\n onChange={(e) => onChange(e.target.value as DateRangePreset)}\n >\n {DATE_RANGE_OPTIONS.map((option) => (\n <option key={option.value} value={option.value}>\n {t(option.labelKey, option.value.replace(/_/g, ' '))}\n </option>\n ))}\n </select>\n </div>\n )\n}\n\nexport default DateRangeSelect\n"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { DATE_RANGE_OPTIONS, type DateRangePreset } from './dateRanges'\n\nexport type DateRangeSelectProps = {\n value: DateRangePreset\n onChange: (value: DateRangePreset) => void\n id?: string\n label?: string\n className?: string\n}\n\nexport function DateRangeSelect({\n value,\n onChange,\n id = 'date-range-select',\n label,\n className = '',\n}: DateRangeSelectProps) {\n const t = useT()\n\n return (\n <div className={`space-y-1.5 ${className}`}>\n {label && (\n <label\n htmlFor={id}\n className=\"text-xs font-semibold uppercase text-muted-foreground\"\n >\n {label}\n </label>\n )}\n <select\n id={id}\n className=\"w-full rounded-md border bg-background px-2 py-1 text-sm text-foreground focus-visible:border-ring focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\"\n value={value}\n onChange={(e) => onChange(e.target.value as DateRangePreset)}\n >\n {DATE_RANGE_OPTIONS.map((option) => (\n <option key={option.value} value={option.value}>\n {t(option.labelKey, option.value.replace(/_/g, ' '))}\n </option>\n ))}\n </select>\n </div>\n )\n}\n\nexport default DateRangeSelect\n"],
5
5
  "mappings": ";AAwBI,SAEI,KAFJ;AArBJ,SAAS,YAAY;AACrB,SAAS,0BAAgD;AAUlD,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA,KAAK;AAAA,EACL;AAAA,EACA,YAAY;AACd,GAAyB;AACvB,QAAM,IAAI,KAAK;AAEf,SACE,qBAAC,SAAI,WAAW,eAAe,SAAS,IACrC;AAAA,aACC;AAAA,MAAC;AAAA;AAAA,QACC,SAAS;AAAA,QACT,WAAU;AAAA,QAET;AAAA;AAAA,IACH;AAAA,IAEF;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,WAAU;AAAA,QACV;AAAA,QACA,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAwB;AAAA,QAE1D,6BAAmB,IAAI,CAAC,WACvB,oBAAC,YAA0B,OAAO,OAAO,OACtC,YAAE,OAAO,UAAU,OAAO,MAAM,QAAQ,MAAM,GAAG,CAAC,KADxC,OAAO,KAEpB,CACD;AAAA;AAAA,IACH;AAAA,KACF;AAEJ;AAEA,IAAO,0BAAQ;",
6
6
  "names": []
7
7
  }
@@ -14,7 +14,7 @@ function InlineDateRangeSelect({
14
14
  /* @__PURE__ */ jsx(
15
15
  "select",
16
16
  {
17
- className: "appearance-none rounded-md border border-border bg-background px-2 py-0.5 pr-6 text-xs text-foreground hover:border-primary/50 focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary cursor-pointer",
17
+ className: "appearance-none rounded-md border border-border bg-background px-2 py-0.5 pr-6 text-xs text-foreground hover:border-primary/50 focus-visible:border-ring focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring cursor-pointer",
18
18
  value,
19
19
  onChange: (e) => onChange(e.target.value),
20
20
  title: displayLabel,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/date-range/InlineDateRangeSelect.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { DATE_RANGE_OPTIONS, type DateRangePreset } from './dateRanges'\n\nexport type InlineDateRangeSelectProps = {\n value: DateRangePreset\n onChange: (value: DateRangePreset) => void\n className?: string\n}\n\nexport function InlineDateRangeSelect({\n value,\n onChange,\n className = '',\n}: InlineDateRangeSelectProps) {\n const t = useT()\n\n const currentOption = DATE_RANGE_OPTIONS.find((opt) => opt.value === value)\n const displayLabel = currentOption\n ? t(currentOption.labelKey, currentOption.value.replace(/_/g, ' '))\n : value.replace(/_/g, ' ')\n\n return (\n <div className={`relative inline-flex items-center ${className}`}>\n <select\n className=\"appearance-none rounded-md border border-border bg-background px-2 py-0.5 pr-6 text-xs text-foreground hover:border-primary/50 focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary cursor-pointer\"\n value={value}\n onChange={(e) => onChange(e.target.value as DateRangePreset)}\n title={displayLabel}\n >\n {DATE_RANGE_OPTIONS.map((option) => (\n <option key={option.value} value={option.value}>\n {t(option.labelKey, option.value.replace(/_/g, ' '))}\n </option>\n ))}\n </select>\n <svg\n className=\"pointer-events-none absolute right-1.5 h-3 w-3 text-muted-foreground\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth={2}\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M19 9l-7 7-7-7\" />\n </svg>\n </div>\n )\n}\n\nexport default InlineDateRangeSelect\n"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { DATE_RANGE_OPTIONS, type DateRangePreset } from './dateRanges'\n\nexport type InlineDateRangeSelectProps = {\n value: DateRangePreset\n onChange: (value: DateRangePreset) => void\n className?: string\n}\n\nexport function InlineDateRangeSelect({\n value,\n onChange,\n className = '',\n}: InlineDateRangeSelectProps) {\n const t = useT()\n\n const currentOption = DATE_RANGE_OPTIONS.find((opt) => opt.value === value)\n const displayLabel = currentOption\n ? t(currentOption.labelKey, currentOption.value.replace(/_/g, ' '))\n : value.replace(/_/g, ' ')\n\n return (\n <div className={`relative inline-flex items-center ${className}`}>\n <select\n className=\"appearance-none rounded-md border border-border bg-background px-2 py-0.5 pr-6 text-xs text-foreground hover:border-primary/50 focus-visible:border-ring focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring cursor-pointer\"\n value={value}\n onChange={(e) => onChange(e.target.value as DateRangePreset)}\n title={displayLabel}\n >\n {DATE_RANGE_OPTIONS.map((option) => (\n <option key={option.value} value={option.value}>\n {t(option.labelKey, option.value.replace(/_/g, ' '))}\n </option>\n ))}\n </select>\n <svg\n className=\"pointer-events-none absolute right-1.5 h-3 w-3 text-muted-foreground\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth={2}\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M19 9l-7 7-7-7\" />\n </svg>\n </div>\n )\n}\n\nexport default InlineDateRangeSelect\n"],
5
5
  "mappings": ";AAyBI,SAQM,KARN;AAtBJ,SAAS,YAAY;AACrB,SAAS,0BAAgD;AAQlD,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA;AAAA,EACA,YAAY;AACd,GAA+B;AAC7B,QAAM,IAAI,KAAK;AAEf,QAAM,gBAAgB,mBAAmB,KAAK,CAAC,QAAQ,IAAI,UAAU,KAAK;AAC1E,QAAM,eAAe,gBACjB,EAAE,cAAc,UAAU,cAAc,MAAM,QAAQ,MAAM,GAAG,CAAC,IAChE,MAAM,QAAQ,MAAM,GAAG;AAE3B,SACE,qBAAC,SAAI,WAAW,qCAAqC,SAAS,IAC5D;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV;AAAA,QACA,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAwB;AAAA,QAC3D,OAAO;AAAA,QAEN,6BAAmB,IAAI,CAAC,WACvB,oBAAC,YAA0B,OAAO,OAAO,OACtC,YAAE,OAAO,UAAU,OAAO,MAAM,QAAQ,MAAM,GAAG,CAAC,KADxC,OAAO,KAEpB,CACD;AAAA;AAAA,IACH;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,MAAK;AAAA,QACL,SAAQ;AAAA,QACR,QAAO;AAAA,QACP,aAAa;AAAA,QAEb,8BAAC,UAAK,eAAc,SAAQ,gBAAe,SAAQ,GAAE,kBAAiB;AAAA;AAAA,IACxE;AAAA,KACF;AAEJ;AAEA,IAAO,gCAAQ;",
6
6
  "names": []
7
7
  }
@@ -7,7 +7,7 @@ function AccessDeniedMessage({ label, description, action, className, iconClassN
7
7
  "div",
8
8
  {
9
9
  className: cn(
10
- "flex items-start gap-3 rounded border border-amber-300/50 bg-amber-50/50 px-3 py-2 text-sm text-amber-900 dark:border-amber-700/50 dark:bg-amber-950/30 dark:text-amber-200",
10
+ "flex items-start gap-3 rounded border border-status-warning-border bg-status-warning-bg px-3 py-2 text-sm text-status-warning-text",
11
11
  className
12
12
  ),
13
13
  role: "alert",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/detail/AccessDeniedMessage.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { ShieldAlert } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\ntype AccessDeniedMessageProps = {\n label: string\n description?: string\n action?: React.ReactNode\n className?: string\n iconClassName?: string\n}\n\nexport function AccessDeniedMessage({ label, description, action, className, iconClassName }: AccessDeniedMessageProps) {\n return (\n <div\n className={cn(\n 'flex items-start gap-3 rounded border border-amber-300/50 bg-amber-50/50 px-3 py-2 text-sm text-amber-900 dark:border-amber-700/50 dark:bg-amber-950/30 dark:text-amber-200',\n className\n )}\n role=\"alert\"\n >\n <ShieldAlert className={cn('h-4 w-4 flex-none', iconClassName)} aria-hidden />\n <div className=\"space-y-1\">\n <p className=\"leading-tight\">{label}</p>\n {description ? <p className=\"text-muted-foreground\">{description}</p> : null}\n {action ? <div className=\"pt-1\">{action}</div> : null}\n </div>\n </div>\n )\n}\n"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { ShieldAlert } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\ntype AccessDeniedMessageProps = {\n label: string\n description?: string\n action?: React.ReactNode\n className?: string\n iconClassName?: string\n}\n\nexport function AccessDeniedMessage({ label, description, action, className, iconClassName }: AccessDeniedMessageProps) {\n return (\n <div\n className={cn(\n 'flex items-start gap-3 rounded border border-status-warning-border bg-status-warning-bg px-3 py-2 text-sm text-status-warning-text',\n className\n )}\n role=\"alert\"\n >\n <ShieldAlert className={cn('h-4 w-4 flex-none', iconClassName)} aria-hidden />\n <div className=\"space-y-1\">\n <p className=\"leading-tight\">{label}</p>\n {description ? <p className=\"text-muted-foreground\">{description}</p> : null}\n {action ? <div className=\"pt-1\">{action}</div> : null}\n </div>\n </div>\n )\n}\n"],
5
5
  "mappings": ";AAuBM,cACA,YADA;AApBN,SAAS,mBAAmB;AAC5B,SAAS,UAAU;AAUZ,SAAS,oBAAoB,EAAE,OAAO,aAAa,QAAQ,WAAW,cAAc,GAA6B;AACtH,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACA,MAAK;AAAA,MAEL;AAAA,4BAAC,eAAY,WAAW,GAAG,qBAAqB,aAAa,GAAG,eAAW,MAAC;AAAA,QAC5E,qBAAC,SAAI,WAAU,aACb;AAAA,8BAAC,OAAE,WAAU,iBAAiB,iBAAM;AAAA,UACnC,cAAc,oBAAC,OAAE,WAAU,yBAAyB,uBAAY,IAAO;AAAA,UACvE,SAAS,oBAAC,SAAI,WAAU,QAAQ,kBAAO,IAAS;AAAA,WACnD;AAAA;AAAA;AAAA,EACF;AAEJ;",
6
6
  "names": []
7
7
  }
@@ -79,7 +79,7 @@ function TimelineItemHeader({
79
79
  }, [fallbackTimestampLabel, subtitle, timestamp]);
80
80
  const iconNode = icon && renderIcon ? renderIcon(icon, iconSizeClass) : null;
81
81
  return /* @__PURE__ */ jsxs("div", { className: ["flex items-start gap-3", className].filter(Boolean).join(" "), children: [
82
- iconNode ? /* @__PURE__ */ jsx("span", { className: ["inline-flex items-center justify-center rounded border border-border bg-muted/40", wrapperSize].join(" "), children: iconNode }) : null,
82
+ iconNode ? /* @__PURE__ */ jsx("span", { className: ["inline-flex items-center justify-center rounded border border-border bg-muted/50", wrapperSize].join(" "), children: iconNode }) : null,
83
83
  /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
84
84
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
85
85
  /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground", children: title }),
@@ -168,7 +168,7 @@ function ActivityForm({
168
168
  return /* @__PURE__ */ jsx(
169
169
  "select",
170
170
  {
171
- className: "h-9 w-full rounded border border-muted-foreground/40 bg-background px-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary",
171
+ className: "h-9 w-full rounded border border-muted-foreground/40 bg-background px-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
172
172
  value: currentValue,
173
173
  onChange: (event) => setValue(event.target.value),
174
174
  children: normalizedEntityOptions.map((option) => /* @__PURE__ */ jsx("option", { value: option.id, children: option.label }, option.id))
@@ -188,7 +188,7 @@ function ActivityForm({
188
188
  return /* @__PURE__ */ jsxs(
189
189
  "select",
190
190
  {
191
- className: "h-9 w-full rounded border border-muted-foreground/40 bg-background px-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary",
191
+ className: "h-9 w-full rounded border border-muted-foreground/40 bg-background px-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
192
192
  value: currentValue,
193
193
  onChange: (event) => setValue(event.target.value),
194
194
  children: [
@@ -243,7 +243,7 @@ function ActivityForm({
243
243
  "input",
244
244
  {
245
245
  type: "datetime-local",
246
- className: "w-full rounded-md border px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring",
246
+ className: "w-full rounded-md border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
247
247
  value: typeof value === "string" ? value : "",
248
248
  onChange: (event) => setValue(event.target.value || ""),
249
249
  onFocus: (event) => {
@@ -824,7 +824,7 @@ function ActivitiesSectionImpl({
824
824
  return /* @__PURE__ */ jsxs(
825
825
  "div",
826
826
  {
827
- className: "group space-y-3 rounded-lg border bg-card p-4 transition hover:border-border/80 cursor-pointer",
827
+ className: "group space-y-3 rounded-lg border bg-card p-4 transition hover:border-border/70 cursor-pointer",
828
828
  role: "button",
829
829
  tabIndex: 0,
830
830
  onClick: () => openEditDialog(activity),