@open-mercato/ui 0.5.1-develop.2856.35de414092 → 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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/detail/ActivitiesSection.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { ArrowUpRightSquare, Pencil, Plus, Trash2 } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { DictionaryEntrySelect, type DictionarySelectLabels } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'\nimport type { AppearanceSelectorLabels } from '@open-mercato/core/modules/dictionaries/components/AppearanceSelector'\nimport { formatRelativeTime, formatDateTime } from '@open-mercato/shared/lib/time'\nimport { LoadingMessage, TabEmptyState } from './'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { createTranslatorWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport { useConfirmDialog } from '../confirm-dialog'\nimport { ComponentReplacementHandles } from '@open-mercato/shared/modules/widgets/component-registry'\nimport { useRegisteredComponent } from '../injection/useRegisteredComponent'\n\ntype Translator = (key: string, fallback?: string, params?: Record<string, string | number>) => string\n\nexport type ActivitySummary = {\n id: string\n activityType: string\n subject?: string | null\n body?: string | null\n occurredAt?: string | null\n createdAt: string\n appearanceIcon?: string | null\n appearanceColor?: string | null\n entityId?: string | null\n authorUserId?: string | null\n authorName?: string | null\n authorEmail?: string | null\n dealId?: string | null\n dealTitle?: string | null\n customFields?: Array<{ key: string; label?: string | null; value: unknown }>\n customValues?: Record<string, unknown> | null\n}\n\nexport type SectionAction = {\n label: React.ReactNode\n onClick: () => void\n disabled?: boolean\n icon?: React.ReactNode\n}\n\nexport type TabEmptyStateConfig = {\n title: string\n actionLabel: string\n description?: string\n}\n\nexport type ActivityCreatePayload = {\n entityId: string\n activityType: string\n subject?: string | null\n body?: string | null\n occurredAt?: string | null\n dealId?: string | null\n customFields?: Record<string, unknown>\n}\n\nexport type ActivityUpdatePayload = Partial<ActivityCreatePayload>\n\nexport type ActivitiesDataAdapter<C = unknown> = {\n list: (params: { entityId: string | null; dealId?: string | null; context?: C }) => Promise<ActivitySummary[]>\n create: (params: ActivityCreatePayload & { context?: C }) => Promise<void>\n update: (params: { id: string; patch: ActivityUpdatePayload; context?: C }) => Promise<void>\n delete: (params: { id: string; context?: C }) => Promise<void>\n}\n\ntype DictionaryOption = {\n value: string\n label: string\n color: string | null\n icon: string | null\n}\n\ntype ActivityTypePresentation = {\n label: string\n icon?: string | null\n color?: string | null\n}\n\ntype PendingAction =\n | { kind: 'create' }\n | { kind: 'update'; id: string }\n | { kind: 'delete'; id: string }\n\nconst INVALID_DATE_MESSAGE = 'invalidDate'\n\nconst schema = {\n validate(values: Record<string, unknown>) {\n const result: { ok: boolean; errors?: Array<{ path: string; message: string }> } = { ok: true }\n const activityType = typeof values.activityType === 'string' ? values.activityType.trim() : ''\n if (!activityType) {\n result.ok = false\n result.errors = [{ path: 'activityType', message: 'required' }]\n return result\n }\n const occurredAt = typeof values.occurredAt === 'string' ? values.occurredAt.trim() : ''\n if (occurredAt.length) {\n const parsed = new Date(occurredAt)\n if (Number.isNaN(parsed.getTime())) {\n result.ok = false\n result.errors = [{ path: 'occurredAt', message: INVALID_DATE_MESSAGE }]\n }\n }\n return result\n },\n}\n\nfunction toLocalDateTimeInput(value?: string | null): string {\n if (!value) return ''\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return ''\n const pad = (input: number) => `${input}`.padStart(2, '0')\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(\n date.getMinutes(),\n )}`\n}\n\n\ntype TimelineItemHeaderProps = {\n title: React.ReactNode\n subtitle?: React.ReactNode\n timestamp?: string | Date | null\n fallbackTimestampLabel?: React.ReactNode\n icon?: string | null\n color?: string | null\n iconSize?: 'sm' | 'md'\n className?: string\n renderIcon?: (icon: string, className?: string) => React.ReactNode\n renderColor?: (color: string, className?: string) => React.ReactNode\n}\n\nfunction TimelineItemHeader({\n title,\n subtitle,\n timestamp,\n fallbackTimestampLabel,\n icon,\n color,\n iconSize = 'md',\n className,\n renderIcon,\n renderColor,\n}: TimelineItemHeaderProps) {\n const wrapperSize = iconSize === 'sm' ? 'h-6 w-6' : 'h-8 w-8'\n const iconSizeClass = iconSize === 'sm' ? 'h-3.5 w-3.5' : 'h-4 w-4'\n const resolvedTimestamp = React.useMemo(() => {\n if (subtitle) return subtitle\n if (!timestamp) return fallbackTimestampLabel ?? null\n const value = typeof timestamp === 'string' ? timestamp : timestamp.toISOString()\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return fallbackTimestampLabel ?? null\n const now = Date.now()\n const diff = Math.abs(now - date.getTime())\n const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000\n const relativeLabel = diff <= THIRTY_DAYS_MS ? formatRelativeTime(value) : null\n const absoluteLabel = formatDateTime(value)\n if (relativeLabel) {\n return (\n <span title={absoluteLabel ?? undefined}>\n {relativeLabel}\n </span>\n )\n }\n return absoluteLabel ?? fallbackTimestampLabel ?? null\n }, [fallbackTimestampLabel, subtitle, timestamp])\n\n const iconNode = icon && renderIcon ? renderIcon(icon, iconSizeClass) : null\n\n return (\n <div className={['flex items-start gap-3', className].filter(Boolean).join(' ')}>\n {iconNode ? (\n <span className={['inline-flex items-center justify-center rounded border border-border bg-muted/40', wrapperSize].join(' ')}>\n {iconNode}\n </span>\n ) : null}\n <div className=\"space-y-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-semibold text-foreground\">{title}</span>\n {color && renderColor ? renderColor(color, 'h-3 w-3 rounded-full border border-border') : null}\n </div>\n {resolvedTimestamp ? <div className=\"text-xs text-muted-foreground\">{resolvedTimestamp}</div> : null}\n </div>\n </div>\n )\n}\n\nexport type ActivityFormBaseValues = {\n activityType: string\n subject?: string | null\n body?: string | null\n occurredAt?: string | null\n dealId?: string | null\n}\n\nexport type ActivityFormSubmitPayload = {\n base: ActivityFormBaseValues\n custom: Record<string, unknown>\n entityId?: string | null\n}\n\ntype ActivityFormProps = {\n mode: 'create' | 'edit'\n initialValues?: Partial<ActivityFormBaseValues & Record<string, unknown>>\n onSubmit: (payload: ActivityFormSubmitPayload) => Promise<void>\n onCancel: () => void\n submitLabel?: string\n cancelLabel?: string\n isSubmitting?: boolean\n activityTypeLabels: DictionarySelectLabels\n loadActivityOptions: () => Promise<DictionaryOption[]>\n createActivityOption?: (input: { value: string; label?: string; color?: string | null; icon?: string | null }) => Promise<DictionaryOption>\n dealOptions?: Array<{ id: string; label: string }>\n entityOptions?: Array<{ id: string; label: string }>\n defaultEntityId?: string | null\n manageHref?: string\n customFieldEntityIds?: string[]\n labelPrefix?: string\n appearanceLabels?: AppearanceSelectorLabels\n}\n\nfunction normalizeCustomFieldSubmitValue(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.filter((entry) => entry !== undefined)\n }\n if (value === undefined) return null\n return value\n}\n\nfunction buildActivityValidationError(errors: Array<{ path: string; message: string }>, translate: (key: string, fallback?: string) => string) {\n const issue = errors[0]\n if (!issue) {\n throw createCrudFormError(translate('error', 'Failed to save activity.'))\n }\n const message = issue.message === INVALID_DATE_MESSAGE\n ? translate('invalidDate', 'Invalid date')\n : translate('error', 'Failed to save activity.')\n const field = issue.path\n throw createCrudFormError(message, field ? { [field]: message } : undefined)\n}\n\nfunction ActivityForm({\n mode,\n initialValues,\n onSubmit,\n onCancel,\n submitLabel,\n cancelLabel,\n isSubmitting = false,\n activityTypeLabels,\n loadActivityOptions,\n createActivityOption,\n dealOptions,\n entityOptions,\n defaultEntityId,\n manageHref = '/backend/config/dictionaries',\n customFieldEntityIds,\n labelPrefix = 'customers.people.detail.activities',\n appearanceLabels,\n}: ActivityFormProps) {\n const tHook = useT()\n const t = React.useMemo<Translator>(() => createTranslatorWithFallback(tHook), [tHook])\n const translate = React.useCallback(\n (suffix: string, fallback?: string) => t(`${labelPrefix}.${suffix}`, fallback ?? ''),\n [labelPrefix, t],\n )\n const [pending, setPending] = React.useState(false)\n\n const normalizedDealOptions = React.useMemo(() => {\n if (!Array.isArray(dealOptions)) return []\n const seen = new Set<string>()\n return dealOptions\n .map((option) => {\n if (!option || typeof option !== 'object') return null\n const id = typeof option.id === 'string' ? option.id.trim() : ''\n if (!id || seen.has(id)) return null\n const label =\n typeof option.label === 'string' && option.label.trim().length\n ? option.label.trim()\n : id\n seen.add(id)\n return { id, label }\n })\n .filter((option): option is { id: string; label: string } => !!option)\n }, [dealOptions])\n\n const normalizedEntityOptions = React.useMemo(() => {\n if (!Array.isArray(entityOptions)) return []\n const seen = new Set<string>()\n return entityOptions\n .map((option) => {\n if (!option || typeof option !== 'object') return null\n const id = typeof option.id === 'string' ? option.id.trim() : ''\n if (!id || seen.has(id)) return null\n const label =\n typeof option.label === 'string' && option.label.trim().length\n ? option.label.trim()\n : id\n seen.add(id)\n return { id, label }\n })\n .filter((option): option is { id: string; label: string } => !!option)\n }, [entityOptions])\n\n const baseFields = React.useMemo<CrudField[]>(() => {\n const fields: CrudField[] = []\n\n if (normalizedEntityOptions.length) {\n fields.push({\n id: 'entityId',\n label: translate('fields.entity', 'Assign to record'),\n type: 'custom',\n layout: 'half',\n component: ({ value, setValue }) => {\n const currentValue =\n typeof value === 'string' && value.length ? value : normalizedEntityOptions[0]?.id ?? ''\n return (\n <select\n 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\"\n value={currentValue}\n onChange={(event) => setValue(event.target.value)}\n >\n {normalizedEntityOptions.map((option) => (\n <option key={option.id} value={option.id}>\n {option.label}\n </option>\n ))}\n </select>\n )\n },\n } as CrudField)\n }\n\n if (normalizedDealOptions.length) {\n fields.push({\n id: 'dealId',\n label: translate('fields.deal', 'Link to deal (optional)'),\n type: 'custom',\n layout: 'half',\n component: ({ value, setValue }) => {\n const currentValue = typeof value === 'string' ? value : ''\n return (\n <select\n 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\"\n value={currentValue}\n onChange={(event) => setValue(event.target.value)}\n >\n <option value=\"\">\n {translate('fields.dealPlaceholder', 'No linked deal')}\n </option>\n {normalizedDealOptions.map((option) => (\n <option key={option.id} value={option.id}>\n {option.label}\n </option>\n ))}\n </select>\n )\n },\n } as CrudField)\n }\n\n fields.push({\n id: 'activityType',\n label: translate('fields.type', 'Activity type'),\n type: 'custom',\n required: true,\n layout: 'half',\n component: ({ value, setValue }) => (\n <DictionaryEntrySelect\n value={typeof value === 'string' ? value : undefined}\n onChange={(next) => setValue(next ?? '')}\n fetchOptions={loadActivityOptions}\n createOption={createActivityOption}\n labels={activityTypeLabels}\n allowAppearance\n allowInlineCreate\n appearanceLabels={appearanceLabels}\n selectClassName=\"w-full\"\n manageHref={manageHref}\n />\n ),\n } as CrudField)\n\n fields.push({\n id: 'subject',\n label: translate('fields.subject', 'Subject'),\n type: 'text',\n layout: 'half',\n placeholder: translate('subjectPlaceholder', 'Add a subject (optional)'),\n } as CrudField)\n\n fields.push({\n id: 'body',\n label: translate('fields.body', 'Details'),\n type: 'textarea',\n placeholder: translate('bodyPlaceholder', 'Describe the interaction'),\n } as CrudField)\n\n fields.push({\n id: 'occurredAt',\n label: translate('fields.occurredAt', 'Occurred / will occur at'),\n type: 'custom',\n component: ({ value, setValue }) => (\n <input\n type=\"datetime-local\"\n className=\"w-full rounded-md border px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring\"\n value={typeof value === 'string' ? value : ''}\n onChange={(event) => setValue(event.target.value || '')}\n onFocus={(event) => {\n const target = event.currentTarget as HTMLInputElement & { showPicker?: () => void }\n if (typeof target.showPicker === 'function') {\n try { target.showPicker() } catch { /* ignore unsupported */ }\n }\n }}\n onClick={(event) => {\n const target = event.currentTarget as HTMLInputElement & { showPicker?: () => void }\n if (typeof target.showPicker === 'function') {\n try { target.showPicker() } catch { /* ignore unsupported */ }\n }\n }}\n />\n ),\n layout: 'half',\n } as CrudField)\n\n return fields\n }, [\n activityTypeLabels,\n appearanceLabels,\n createActivityOption,\n loadActivityOptions,\n manageHref,\n normalizedDealOptions,\n normalizedEntityOptions,\n translate,\n ])\n\n const baseFieldIds = React.useMemo(() => new Set(baseFields.map((field) => field.id)), [baseFields])\n\n const groups = React.useMemo<CrudFormGroup[]>(() => {\n const detailFields: string[] = []\n if (normalizedEntityOptions.length) detailFields.push('entityId')\n if (normalizedDealOptions.length) detailFields.push('dealId')\n detailFields.push('activityType', 'subject', 'occurredAt', 'body')\n const baseGroups: CrudFormGroup[] = [\n {\n id: 'details',\n title: translate('form.details', 'Activity details'),\n column: 1,\n fields: detailFields,\n },\n ]\n baseGroups.push({\n id: 'custom',\n title: translate('form.customFields', 'Custom fields'),\n column: 2,\n kind: 'customFields',\n })\n return baseGroups\n }, [normalizedDealOptions.length, normalizedEntityOptions.length, translate])\n\n const handleSubmit = React.useCallback(\n async (values: Record<string, unknown>) => {\n if (pending || isSubmitting) return\n setPending(true)\n try {\n const parsed = schema.validate(values)\n if (!parsed.ok) {\n throw buildActivityValidationError(parsed.errors ?? [], translate)\n }\n const rawEntityId = typeof values.entityId === 'string' ? values.entityId.trim() : ''\n const resolvedEntityId = rawEntityId || (typeof defaultEntityId === 'string' ? defaultEntityId : '')\n const rawDealId = typeof values.dealId === 'string' ? values.dealId.trim() : ''\n const base: ActivityFormBaseValues = {\n activityType: typeof values.activityType === 'string' ? values.activityType.trim() : '',\n subject: typeof values.subject === 'string' && values.subject.trim().length ? values.subject.trim() : undefined,\n body: typeof values.body === 'string' && values.body.trim().length ? values.body.trim() : undefined,\n occurredAt: typeof values.occurredAt === 'string' && values.occurredAt.trim().length\n ? new Date(values.occurredAt as string).toISOString()\n : undefined,\n dealId: rawDealId.length ? rawDealId : undefined,\n }\n const reservedCustomKeys = new Set(['entityId', 'dealId'])\n const customEntries = collectCustomFieldValues(values, {\n transform: (value) => normalizeCustomFieldSubmitValue(value),\n accept: (fieldId) => !reservedCustomKeys.has(fieldId),\n })\n Object.entries(values).forEach(([key, value]) => {\n if (key.startsWith('cf_')) return\n if (!baseFieldIds.has(key) && key !== 'id') {\n if (reservedCustomKeys.has(key)) return\n customEntries[key] = normalizeCustomFieldSubmitValue(value)\n }\n })\n await onSubmit({ base, custom: customEntries, entityId: resolvedEntityId.length ? resolvedEntityId : undefined })\n } finally {\n setPending(false)\n }\n },\n [baseFieldIds, defaultEntityId, isSubmitting, onSubmit, pending, translate],\n )\n\n const embeddedInitialValues = React.useMemo(() => {\n const occurredAt = toLocalDateTimeInput(initialValues?.occurredAt ?? null)\n const resolvedEntity = (() => {\n const raw = typeof (initialValues as Record<string, unknown> | undefined)?.entityId === 'string'\n ? (initialValues as Record<string, unknown>).entityId as string\n : typeof defaultEntityId === 'string'\n ? defaultEntityId\n : normalizedEntityOptions[0]?.id ?? ''\n return raw ?? ''\n })()\n const resolvedDeal = typeof (initialValues as Record<string, unknown> | undefined)?.dealId === 'string'\n ? (initialValues as Record<string, unknown>).dealId as string\n : ''\n\n return {\n entityId: resolvedEntity,\n dealId: resolvedDeal,\n activityType: initialValues?.activityType ?? '',\n subject: initialValues?.subject ?? '',\n body: initialValues?.body ?? '',\n occurredAt,\n ...Object.fromEntries(\n Object.entries(initialValues ?? {})\n .filter(([key]) => {\n if (!key.startsWith('cf_')) return false\n const trimmed = key.slice(3)\n return trimmed !== 'entityId' && trimmed !== 'dealId'\n })\n .map(([key, value]) => [key, value]),\n ),\n }\n }, [defaultEntityId, initialValues, normalizedEntityOptions])\n\n return (\n <CrudForm<Record<string, unknown>>\n embedded\n fields={baseFields}\n groups={groups}\n initialValues={embeddedInitialValues}\n onSubmit={handleSubmit}\n submitLabel={submitLabel ?? (mode === 'edit'\n ? translate('update', 'Update activity (\u2318/Ctrl + Enter)')\n : translate('save', 'Save activity (\u2318/Ctrl + Enter)'))}\n extraActions={(\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={onCancel}\n disabled={pending || isSubmitting}\n >\n {cancelLabel ?? translate('cancel', 'Cancel')}\n </Button>\n )}\n entityIds={customFieldEntityIds}\n />\n )\n}\n\ntype ActivityDialogProps = {\n open: boolean\n mode: 'create' | 'edit'\n onOpenChange: (next: boolean) => void\n initialValues?: Partial<ActivityFormBaseValues & Record<string, unknown>>\n onSubmit: (payload: ActivityFormSubmitPayload) => Promise<void>\n isSubmitting?: boolean\n activityTypeLabels: DictionarySelectLabels\n loadActivityOptions: () => Promise<DictionaryOption[]>\n createActivityOption?: (input: { value: string; label?: string; color?: string | null; icon?: string | null }) => Promise<DictionaryOption>\n titles?: {\n create?: string\n edit?: string\n }\n submitLabels?: {\n create?: string\n edit?: string\n }\n cancelLabel?: string\n dealOptions?: Array<{ id: string; label: string }>\n entityOptions?: Array<{ id: string; label: string }>\n defaultEntityId?: string | null\n manageHref?: string\n customFieldEntityIds?: string[]\n labelPrefix?: string\n appearanceLabels?: AppearanceSelectorLabels\n}\n\nfunction ActivityDialog({\n open,\n mode,\n onOpenChange,\n initialValues,\n onSubmit,\n isSubmitting,\n activityTypeLabels,\n loadActivityOptions,\n createActivityOption,\n titles,\n submitLabels,\n cancelLabel,\n dealOptions,\n entityOptions,\n defaultEntityId,\n manageHref,\n customFieldEntityIds,\n labelPrefix = 'customers.people.detail.activities',\n appearanceLabels,\n}: ActivityDialogProps) {\n const tHook = useT()\n const t = React.useMemo<Translator>(() => createTranslatorWithFallback(tHook), [tHook])\n const translate = React.useCallback(\n (suffix: string, fallback?: string, params?: Record<string, string | number>) =>\n t(`${labelPrefix}.${suffix}`, fallback ?? '', params),\n [labelPrefix, t],\n )\n\n const dialogTitle =\n mode === 'edit'\n ? titles?.edit ?? translate('editTitle', 'Edit activity')\n : titles?.create ?? translate('addTitle', 'Add activity')\n\n const resolvedSubmitLabel =\n mode === 'edit'\n ? submitLabels?.edit ?? translate('update', 'Update activity (\u2318/Ctrl + Enter)')\n : submitLabels?.create ?? translate('save', 'Save activity (\u2318/Ctrl + Enter)')\n\n const resolvedCancelLabel = cancelLabel ?? translate('cancel', 'Cancel')\n\n const handleCancel = React.useCallback(() => {\n onOpenChange(false)\n }, [onOpenChange])\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"sm:max-w-3xl\">\n <DialogHeader>\n <DialogTitle>{dialogTitle}</DialogTitle>\n </DialogHeader>\n <ActivityForm\n mode={mode}\n initialValues={initialValues}\n onSubmit={onSubmit}\n onCancel={handleCancel}\n submitLabel={resolvedSubmitLabel}\n cancelLabel={resolvedCancelLabel}\n isSubmitting={isSubmitting}\n activityTypeLabels={activityTypeLabels}\n loadActivityOptions={loadActivityOptions}\n createActivityOption={createActivityOption}\n dealOptions={dealOptions}\n entityOptions={entityOptions}\n defaultEntityId={defaultEntityId}\n manageHref={manageHref}\n customFieldEntityIds={customFieldEntityIds}\n labelPrefix={labelPrefix}\n appearanceLabels={appearanceLabels}\n />\n </DialogContent>\n </Dialog>\n )\n}\n\nexport type ActivitiesSectionProps<C = unknown> = {\n entityId: string | null\n dealId?: string | null\n addActionLabel: string\n emptyState: TabEmptyStateConfig\n onActionChange?: (action: SectionAction | null) => void\n onLoadingChange?: (isLoading: boolean) => void\n dealOptions?: Array<{ id: string; label: string }>\n entityOptions?: Array<{ id: string; label: string }>\n defaultEntityId?: string | null\n dataAdapter: ActivitiesDataAdapter<C>\n dataContext?: C\n activityTypeLabels: DictionarySelectLabels\n loadActivityOptions: () => Promise<DictionaryOption[]>\n createActivityOption?: (input: { value: string; label?: string; color?: string | null; icon?: string | null }) => Promise<DictionaryOption>\n resolveActivityPresentation?: (activity: ActivitySummary) => ActivityTypePresentation\n renderCustomFields?: (activity: ActivitySummary) => React.ReactNode\n customFieldEntityIds?: string[]\n labelPrefix?: string\n renderIcon?: (icon: string, className?: string) => React.ReactNode\n renderColor?: (color: string, className?: string) => React.ReactNode\n appearanceLabels?: AppearanceSelectorLabels\n dealLinkHref?: (dealId: string) => string\n manageHref?: string\n}\n\nfunction ActivitiesSectionImpl<C = unknown>({\n entityId,\n dealId,\n addActionLabel,\n emptyState,\n onActionChange,\n onLoadingChange,\n dealOptions,\n entityOptions,\n defaultEntityId,\n dataAdapter,\n dataContext,\n activityTypeLabels,\n loadActivityOptions,\n createActivityOption,\n resolveActivityPresentation,\n renderCustomFields,\n customFieldEntityIds,\n labelPrefix = 'customers.people.detail.activities',\n renderIcon,\n renderColor,\n appearanceLabels,\n dealLinkHref,\n manageHref,\n}: ActivitiesSectionProps<C>) {\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const tHook = useT()\n const baseTranslator = React.useMemo<Translator>(() => createTranslatorWithFallback(tHook), [tHook])\n const translate = React.useCallback(\n (suffix: string, fallback?: string, params?: Record<string, string | number>) =>\n baseTranslator(`${labelPrefix}.${suffix}`, fallback ?? '', params),\n [baseTranslator, labelPrefix],\n )\n const resolvedDefaultEntityId = React.useMemo(() => {\n const primary = typeof entityId === 'string' ? entityId.trim() : ''\n if (primary.length) return primary\n const fallback = typeof defaultEntityId === 'string' ? defaultEntityId.trim() : ''\n if (fallback.length) return fallback\n if (Array.isArray(entityOptions)) {\n for (const option of entityOptions) {\n if (!option || typeof option !== 'object') continue\n const id = typeof option.id === 'string' ? option.id.trim() : ''\n if (id.length) return id\n }\n }\n return ''\n }, [defaultEntityId, entityId, entityOptions])\n\n const resolveEntityForSubmission = React.useCallback(\n (input?: string | null) => {\n const candidate = typeof input === 'string' ? input.trim() : ''\n if (candidate.length) return candidate\n return resolvedDefaultEntityId.length ? resolvedDefaultEntityId : null\n },\n [resolvedDefaultEntityId],\n )\n\n const [activities, setActivities] = React.useState<ActivitySummary[]>([])\n const [isLoading, setIsLoading] = React.useState<boolean>(() => {\n const entity = typeof entityId === 'string' ? entityId.trim() : ''\n const deal = typeof dealId === 'string' ? dealId.trim() : ''\n return Boolean(entity || deal || resolvedDefaultEntityId)\n })\n const [loadError, setLoadError] = React.useState<string | null>(null)\n const [pendingAction, setPendingAction] = React.useState<PendingAction | null>(null)\n const [dialogOpen, setDialogOpen] = React.useState(false)\n const [dialogMode, setDialogMode] = React.useState<'create' | 'edit'>('create')\n const [editingActivityId, setEditingActivityId] = React.useState<string | null>(null)\n const [initialValues, setInitialValues] = React.useState<Partial<ActivityFormBaseValues & Record<string, unknown>> | undefined>(undefined)\n const [visibleCount, setVisibleCount] = React.useState(0)\n const pendingCounterRef = React.useRef(0)\n const onLoadingChangeRef = React.useRef(onLoadingChange)\n React.useEffect(() => { onLoadingChangeRef.current = onLoadingChange })\n\n const t = translate\n\n const pushLoading = React.useCallback(() => {\n pendingCounterRef.current += 1\n if (pendingCounterRef.current === 1) {\n onLoadingChangeRef.current?.(true)\n }\n }, [])\n\n const popLoading = React.useCallback(() => {\n pendingCounterRef.current = Math.max(0, pendingCounterRef.current - 1)\n if (pendingCounterRef.current === 0) {\n onLoadingChangeRef.current?.(false)\n }\n }, [])\n\n const updateVisibleCount = React.useCallback((length: number) => {\n if (!length) {\n setVisibleCount(0)\n return\n }\n const baseline = Math.min(5, length)\n setVisibleCount((prev) => {\n if (prev >= length) {\n return Math.min(prev, length)\n }\n return Math.min(Math.max(prev, baseline), length)\n })\n }, [])\n\n const loadActivities = React.useCallback(async () => {\n const queryEntityId = typeof entityId === 'string' ? entityId.trim() : ''\n const queryDealId = typeof dealId === 'string' ? dealId.trim() : ''\n if (!queryEntityId && !queryDealId) {\n setActivities([])\n setLoadError(null)\n updateVisibleCount(0)\n return\n }\n pushLoading()\n setIsLoading(true)\n try {\n const items = await dataAdapter.list({\n entityId: queryEntityId || null,\n dealId: queryDealId || null,\n context: dataContext,\n })\n setActivities(items)\n setLoadError(null)\n updateVisibleCount(items.length)\n } catch (err) {\n const message =\n err instanceof Error\n ? err.message\n : t('loadError', 'Failed to load activities.')\n setLoadError(message)\n } finally {\n setIsLoading(false)\n popLoading()\n }\n }, [dataAdapter, dataContext, dealId, entityId, popLoading, pushLoading, t, updateVisibleCount])\n\n React.useEffect(() => {\n updateVisibleCount(activities.length)\n }, [activities.length, updateVisibleCount])\n\n React.useEffect(() => {\n const queryEntityId = typeof entityId === 'string' ? entityId.trim() : ''\n const queryDealId = typeof dealId === 'string' ? dealId.trim() : ''\n if (!queryEntityId && !queryDealId) {\n setActivities([])\n setLoadError(null)\n setIsLoading(false)\n pendingCounterRef.current = 0\n onLoadingChangeRef.current?.(false)\n updateVisibleCount(0)\n return\n }\n loadActivities().catch(() => {})\n }, [dealId, entityId, loadActivities, updateVisibleCount])\n\n const openCreateDialog = React.useCallback(() => {\n setDialogMode('create')\n setEditingActivityId(null)\n setInitialValues(undefined)\n setDialogOpen(true)\n }, [])\n\n const openEditDialog = React.useCallback((activity: ActivitySummary) => {\n setDialogMode('edit')\n setEditingActivityId(activity.id)\n const baseValues: Partial<ActivityFormBaseValues & Record<string, unknown>> = {\n activityType: activity.activityType,\n subject: activity.subject ?? '',\n body: activity.body ?? '',\n occurredAt: activity.occurredAt ?? activity.createdAt ?? null,\n dealId: activity.dealId ?? '',\n entityId: activity.entityId ?? '',\n }\n const customEntries = Array.isArray(activity.customFields) ? activity.customFields : []\n customEntries.forEach((entry) => {\n if (entry.key === 'entityId' || entry.key === 'dealId') return\n baseValues[`cf_${entry.key}`] = entry.value ?? null\n })\n setInitialValues(baseValues)\n setDialogOpen(true)\n }, [])\n\n const closeDialog = React.useCallback(() => {\n setDialogOpen(false)\n setDialogMode('create')\n setEditingActivityId(null)\n setInitialValues(undefined)\n }, [])\n\n const handleDialogOpenChange = React.useCallback(\n (next: boolean) => {\n if (!next) {\n closeDialog()\n } else {\n setDialogOpen(true)\n }\n },\n [closeDialog],\n )\n\n const handleCreate = React.useCallback(\n async ({ base, custom, entityId: formEntityId }: ActivityFormSubmitPayload) => {\n const submissionEntityId = resolveEntityForSubmission(formEntityId)\n if (!submissionEntityId) {\n const message = t('entityMissing', 'Select a related record before saving.')\n flash(message, 'error')\n throw new Error(message)\n }\n setPendingAction({ kind: 'create' })\n pushLoading()\n try {\n const payload: ActivityCreatePayload = {\n entityId: submissionEntityId,\n activityType: base.activityType,\n subject: base.subject ?? undefined,\n body: base.body ?? undefined,\n occurredAt: base.occurredAt ?? undefined,\n dealId: base.dealId ?? undefined,\n customFields: Object.keys(custom).length ? custom : undefined,\n }\n await dataAdapter.create({ ...payload, context: dataContext })\n await loadActivities()\n flash(t('success', 'Activity saved'), 'success')\n } finally {\n setPendingAction(null)\n popLoading()\n }\n },\n [dataAdapter, dataContext, loadActivities, popLoading, pushLoading, resolveEntityForSubmission, t],\n )\n\n const handleUpdate = React.useCallback(\n async (activityId: string, { base, custom, entityId: formEntityId }: ActivityFormSubmitPayload) => {\n const submissionEntityId = resolveEntityForSubmission(formEntityId)\n if (!submissionEntityId) {\n const message = t('entityMissing', 'Select a related record before saving.')\n flash(message, 'error')\n throw new Error(message)\n }\n setPendingAction({ kind: 'update', id: activityId })\n pushLoading()\n try {\n const patch: ActivityUpdatePayload = {\n entityId: submissionEntityId,\n activityType: base.activityType,\n subject: base.subject ?? undefined,\n body: base.body ?? undefined,\n occurredAt: base.occurredAt ?? undefined,\n dealId: base.dealId ?? undefined,\n customFields: Object.keys(custom).length ? custom : undefined,\n }\n await dataAdapter.update({ id: activityId, patch, context: dataContext })\n await loadActivities()\n flash(t('updateSuccess', 'Activity updated.'), 'success')\n } finally {\n setPendingAction(null)\n popLoading()\n }\n },\n [dataAdapter, dataContext, loadActivities, popLoading, pushLoading, resolveEntityForSubmission, t],\n )\n\n const handleDelete = React.useCallback(\n async (activity: ActivitySummary) => {\n if (!activity.id) return\n const confirmed = await confirm({\n title: t('deleteConfirm', 'Delete this activity? You can restore it using version history.'),\n variant: 'destructive',\n })\n if (!confirmed) return\n setPendingAction({ kind: 'delete', id: activity.id })\n try {\n await dataAdapter.delete({ id: activity.id, context: dataContext })\n setActivities((prev) => prev.filter((existing) => existing.id !== activity.id))\n flash(t('deleteSuccess', 'Activity deleted.'), 'success')\n } catch (err) {\n const message =\n err instanceof Error\n ? err.message\n : t('deleteError', 'Failed to delete activity.')\n flash(message, 'error')\n } finally {\n setPendingAction(null)\n }\n },\n [confirm, dataAdapter, dataContext, t],\n )\n\n const handleDialogSubmit = React.useCallback(\n async (payload: ActivityFormSubmitPayload) => {\n try {\n if (dialogMode === 'edit' && editingActivityId) {\n await handleUpdate(editingActivityId, payload)\n } else {\n await handleCreate(payload)\n }\n closeDialog()\n } catch (err) {\n const message =\n err instanceof Error ? err.message : t('error', 'Failed to save activity')\n flash(message, 'error')\n }\n },\n [closeDialog, dialogMode, editingActivityId, handleCreate, handleUpdate, t],\n )\n\n React.useEffect(() => {\n if (!onActionChange) return\n if (activities.length === 0) {\n onActionChange(null)\n return () => {\n onActionChange(null)\n }\n }\n const disabled = resolveEntityForSubmission(null) === null || pendingAction !== null || isLoading\n const action: SectionAction = {\n label: (\n <span className=\"inline-flex items-center gap-1.5\">\n <Plus className=\"h-4 w-4\" />\n {addActionLabel}\n </span>\n ),\n onClick: () => {\n if (!disabled) openCreateDialog()\n },\n disabled,\n }\n onActionChange(action)\n return () => {\n onActionChange(null)\n }\n }, [\n activities.length,\n addActionLabel,\n isLoading,\n onActionChange,\n openCreateDialog,\n pendingAction,\n resolveEntityForSubmission,\n ])\n\n const isFormPending =\n pendingAction?.kind === 'create' ||\n (pendingAction?.kind === 'update' && pendingAction.id === editingActivityId)\n const visibleActivities = React.useMemo(\n () => activities.slice(0, visibleCount),\n [activities, visibleCount],\n )\n const hasMoreActivities = visibleCount < activities.length\n const loadMoreLabel = t('loadMore', 'Load more activities')\n\n const handleLoadMore = React.useCallback(() => {\n setVisibleCount((prev) => {\n if (prev >= activities.length) return prev\n return Math.min(prev + 5, activities.length)\n })\n }, [activities.length])\n\n const resolvePresentation = React.useCallback(\n (activity: ActivitySummary): ActivityTypePresentation => {\n if (resolveActivityPresentation) return resolveActivityPresentation(activity)\n return {\n label: activity.activityType,\n icon: activity.appearanceIcon ?? null,\n color: activity.appearanceColor ?? null,\n }\n },\n [resolveActivityPresentation],\n )\n\n const resolveDealHref = React.useCallback(\n (id: string) => (dealLinkHref ? dealLinkHref(id) : `/backend/customers/deals/${encodeURIComponent(id)}`),\n [dealLinkHref],\n )\n\n return (\n <div className=\"mt-3 space-y-4\">\n {loadError ? (\n <div className=\"rounded-md border border-destructive/40 bg-destructive/5 px-3 py-2 text-sm text-destructive\">\n {loadError}\n </div>\n ) : null}\n <div className=\"space-y-4\">\n {isLoading && activities.length === 0 ? (\n <LoadingMessage\n label={t('loading', 'Loading activities\u2026')}\n className=\"border-0 bg-transparent p-0 py-8 justify-center\"\n />\n ) : (\n <>\n {!isLoading && activities.length === 0 && !dialogOpen ? (\n <TabEmptyState\n title={emptyState.title}\n action={{\n label: emptyState.actionLabel,\n onClick: openCreateDialog,\n disabled: resolveEntityForSubmission(null) === null || pendingAction !== null,\n }}\n />\n ) : null}\n {visibleActivities.length > 0\n ? visibleActivities.map((activity) => {\n const presentation = resolvePresentation(activity)\n const timestampValue = activity.occurredAt ?? activity.createdAt ?? null\n const occurredLabel =\n formatDateTime(timestampValue) ?? t('noDate', 'No date provided')\n const authorLabel = activity.authorName ?? activity.authorEmail ?? null\n const loggedByText = authorLabel\n ? (() => {\n const translated = t('loggedBy', `Logged by ${authorLabel}`, { user: authorLabel })\n if (\n !translated ||\n translated.includes('{{') ||\n translated.includes('{user')\n ) {\n return `Logged by ${authorLabel}`\n }\n return translated\n })()\n : null\n const isUpdatePending = pendingAction?.kind === 'update' && pendingAction.id === activity.id\n const isDeletePending = pendingAction?.kind === 'delete' && pendingAction.id === activity.id\n\n return (\n <div\n key={activity.id}\n className=\"group space-y-3 rounded-lg border bg-card p-4 transition hover:border-border/80 cursor-pointer\"\n role=\"button\"\n tabIndex={0}\n onClick={() => openEditDialog(activity)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n openEditDialog(activity)\n }\n }}\n >\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <TimelineItemHeader\n title={presentation.label}\n timestamp={timestampValue}\n fallbackTimestampLabel={occurredLabel}\n icon={presentation.icon}\n color={presentation.color}\n renderIcon={renderIcon}\n renderColor={renderColor}\n />\n {activity.dealId ? (\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <ArrowUpRightSquare className=\"h-3.5 w-3.5\" />\n <Link\n href={resolveDealHref(activity.dealId)}\n className=\"font-medium text-foreground hover:underline\"\n onClick={(event) => event.stopPropagation()}\n >\n {activity.dealTitle && activity.dealTitle.length\n ? activity.dealTitle\n : t('linkedDeal', 'Linked deal')}\n </Link>\n </div>\n ) : null}\n </div>\n <div className=\"flex items-center gap-1 opacity-100 md:opacity-0 transition-opacity md:group-hover:opacity-100 focus-within:opacity-100\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={(event) => {\n event.stopPropagation()\n openEditDialog(activity)\n }}\n disabled={pendingAction !== null}\n >\n {isUpdatePending ? (\n <span className=\"relative flex h-4 w-4 items-center justify-center\">\n <span className=\"absolute h-4 w-4 animate-spin rounded-full border border-primary border-t-transparent\" />\n </span>\n ) : (\n <Pencil className=\"h-4 w-4\" />\n )}\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={(event) => {\n event.stopPropagation()\n handleDelete(activity).catch(() => {})\n }}\n disabled={pendingAction !== null}\n >\n {isDeletePending ? (\n <span className=\"relative flex h-4 w-4 items-center justify-center text-destructive\">\n <span className=\"absolute h-4 w-4 animate-spin rounded-full border border-destructive border-t-transparent\" />\n </span>\n ) : (\n <Trash2 className=\"h-4 w-4\" />\n )}\n </Button>\n </div>\n </div>\n {activity.subject ? <p className=\"text-sm font-medium\">{activity.subject}</p> : null}\n {activity.body ? (\n <p className=\"text-sm whitespace-pre-wrap text-muted-foreground\">{activity.body}</p>\n ) : null}\n {renderCustomFields ? renderCustomFields(activity) : null}\n {loggedByText ? (\n <p className=\"text-xs text-muted-foreground\">{loggedByText}</p>\n ) : null}\n </div>\n )\n })\n : null}\n {hasMoreActivities ? (\n <div className=\"flex justify-center\">\n <Button variant=\"outline\" size=\"sm\" onClick={handleLoadMore} disabled={pendingAction !== null}>\n {loadMoreLabel}\n </Button>\n </div>\n ) : null}\n </>\n )}\n </div>\n\n <ActivityDialog\n open={dialogOpen}\n mode={dialogMode}\n onOpenChange={handleDialogOpenChange}\n initialValues={initialValues}\n onSubmit={async (payload) => {\n await handleDialogSubmit(payload)\n }}\n isSubmitting={Boolean(isFormPending)}\n activityTypeLabels={activityTypeLabels}\n loadActivityOptions={loadActivityOptions}\n createActivityOption={createActivityOption}\n dealOptions={dealOptions}\n entityOptions={entityOptions}\n defaultEntityId={resolvedDefaultEntityId || undefined}\n manageHref={manageHref}\n customFieldEntityIds={customFieldEntityIds}\n labelPrefix={labelPrefix}\n appearanceLabels={appearanceLabels}\n />\n {ConfirmDialogElement}\n </div>\n )\n}\n\nexport function ActivitiesSection<C = unknown>(props: ActivitiesSectionProps<C>) {\n const handle = ComponentReplacementHandles.section('ui.detail', 'ActivitiesSection')\n const Resolved = useRegisteredComponent<ActivitiesSectionProps<C>>(\n handle,\n ActivitiesSectionImpl as React.ComponentType<ActivitiesSectionProps<C>>,\n )\n\n return (\n <div data-component-handle={handle}>\n <Resolved {...props} />\n </div>\n )\n}\n\nexport default ActivitiesSection\n"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { ArrowUpRightSquare, Pencil, Plus, Trash2 } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { DictionaryEntrySelect, type DictionarySelectLabels } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'\nimport type { AppearanceSelectorLabels } from '@open-mercato/core/modules/dictionaries/components/AppearanceSelector'\nimport { formatRelativeTime, formatDateTime } from '@open-mercato/shared/lib/time'\nimport { LoadingMessage, TabEmptyState } from './'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { createTranslatorWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport { useConfirmDialog } from '../confirm-dialog'\nimport { ComponentReplacementHandles } from '@open-mercato/shared/modules/widgets/component-registry'\nimport { useRegisteredComponent } from '../injection/useRegisteredComponent'\n\ntype Translator = (key: string, fallback?: string, params?: Record<string, string | number>) => string\n\nexport type ActivitySummary = {\n id: string\n activityType: string\n subject?: string | null\n body?: string | null\n occurredAt?: string | null\n createdAt: string\n appearanceIcon?: string | null\n appearanceColor?: string | null\n entityId?: string | null\n authorUserId?: string | null\n authorName?: string | null\n authorEmail?: string | null\n dealId?: string | null\n dealTitle?: string | null\n customFields?: Array<{ key: string; label?: string | null; value: unknown }>\n customValues?: Record<string, unknown> | null\n}\n\nexport type SectionAction = {\n label: React.ReactNode\n onClick: () => void\n disabled?: boolean\n icon?: React.ReactNode\n}\n\nexport type TabEmptyStateConfig = {\n title: string\n actionLabel: string\n description?: string\n}\n\nexport type ActivityCreatePayload = {\n entityId: string\n activityType: string\n subject?: string | null\n body?: string | null\n occurredAt?: string | null\n dealId?: string | null\n customFields?: Record<string, unknown>\n}\n\nexport type ActivityUpdatePayload = Partial<ActivityCreatePayload>\n\nexport type ActivitiesDataAdapter<C = unknown> = {\n list: (params: { entityId: string | null; dealId?: string | null; context?: C }) => Promise<ActivitySummary[]>\n create: (params: ActivityCreatePayload & { context?: C }) => Promise<void>\n update: (params: { id: string; patch: ActivityUpdatePayload; context?: C }) => Promise<void>\n delete: (params: { id: string; context?: C }) => Promise<void>\n}\n\ntype DictionaryOption = {\n value: string\n label: string\n color: string | null\n icon: string | null\n}\n\ntype ActivityTypePresentation = {\n label: string\n icon?: string | null\n color?: string | null\n}\n\ntype PendingAction =\n | { kind: 'create' }\n | { kind: 'update'; id: string }\n | { kind: 'delete'; id: string }\n\nconst INVALID_DATE_MESSAGE = 'invalidDate'\n\nconst schema = {\n validate(values: Record<string, unknown>) {\n const result: { ok: boolean; errors?: Array<{ path: string; message: string }> } = { ok: true }\n const activityType = typeof values.activityType === 'string' ? values.activityType.trim() : ''\n if (!activityType) {\n result.ok = false\n result.errors = [{ path: 'activityType', message: 'required' }]\n return result\n }\n const occurredAt = typeof values.occurredAt === 'string' ? values.occurredAt.trim() : ''\n if (occurredAt.length) {\n const parsed = new Date(occurredAt)\n if (Number.isNaN(parsed.getTime())) {\n result.ok = false\n result.errors = [{ path: 'occurredAt', message: INVALID_DATE_MESSAGE }]\n }\n }\n return result\n },\n}\n\nfunction toLocalDateTimeInput(value?: string | null): string {\n if (!value) return ''\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return ''\n const pad = (input: number) => `${input}`.padStart(2, '0')\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(\n date.getMinutes(),\n )}`\n}\n\n\ntype TimelineItemHeaderProps = {\n title: React.ReactNode\n subtitle?: React.ReactNode\n timestamp?: string | Date | null\n fallbackTimestampLabel?: React.ReactNode\n icon?: string | null\n color?: string | null\n iconSize?: 'sm' | 'md'\n className?: string\n renderIcon?: (icon: string, className?: string) => React.ReactNode\n renderColor?: (color: string, className?: string) => React.ReactNode\n}\n\nfunction TimelineItemHeader({\n title,\n subtitle,\n timestamp,\n fallbackTimestampLabel,\n icon,\n color,\n iconSize = 'md',\n className,\n renderIcon,\n renderColor,\n}: TimelineItemHeaderProps) {\n const wrapperSize = iconSize === 'sm' ? 'h-6 w-6' : 'h-8 w-8'\n const iconSizeClass = iconSize === 'sm' ? 'h-3.5 w-3.5' : 'h-4 w-4'\n const resolvedTimestamp = React.useMemo(() => {\n if (subtitle) return subtitle\n if (!timestamp) return fallbackTimestampLabel ?? null\n const value = typeof timestamp === 'string' ? timestamp : timestamp.toISOString()\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return fallbackTimestampLabel ?? null\n const now = Date.now()\n const diff = Math.abs(now - date.getTime())\n const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000\n const relativeLabel = diff <= THIRTY_DAYS_MS ? formatRelativeTime(value) : null\n const absoluteLabel = formatDateTime(value)\n if (relativeLabel) {\n return (\n <span title={absoluteLabel ?? undefined}>\n {relativeLabel}\n </span>\n )\n }\n return absoluteLabel ?? fallbackTimestampLabel ?? null\n }, [fallbackTimestampLabel, subtitle, timestamp])\n\n const iconNode = icon && renderIcon ? renderIcon(icon, iconSizeClass) : null\n\n return (\n <div className={['flex items-start gap-3', className].filter(Boolean).join(' ')}>\n {iconNode ? (\n <span className={['inline-flex items-center justify-center rounded border border-border bg-muted/50', wrapperSize].join(' ')}>\n {iconNode}\n </span>\n ) : null}\n <div className=\"space-y-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-semibold text-foreground\">{title}</span>\n {color && renderColor ? renderColor(color, 'h-3 w-3 rounded-full border border-border') : null}\n </div>\n {resolvedTimestamp ? <div className=\"text-xs text-muted-foreground\">{resolvedTimestamp}</div> : null}\n </div>\n </div>\n )\n}\n\nexport type ActivityFormBaseValues = {\n activityType: string\n subject?: string | null\n body?: string | null\n occurredAt?: string | null\n dealId?: string | null\n}\n\nexport type ActivityFormSubmitPayload = {\n base: ActivityFormBaseValues\n custom: Record<string, unknown>\n entityId?: string | null\n}\n\ntype ActivityFormProps = {\n mode: 'create' | 'edit'\n initialValues?: Partial<ActivityFormBaseValues & Record<string, unknown>>\n onSubmit: (payload: ActivityFormSubmitPayload) => Promise<void>\n onCancel: () => void\n submitLabel?: string\n cancelLabel?: string\n isSubmitting?: boolean\n activityTypeLabels: DictionarySelectLabels\n loadActivityOptions: () => Promise<DictionaryOption[]>\n createActivityOption?: (input: { value: string; label?: string; color?: string | null; icon?: string | null }) => Promise<DictionaryOption>\n dealOptions?: Array<{ id: string; label: string }>\n entityOptions?: Array<{ id: string; label: string }>\n defaultEntityId?: string | null\n manageHref?: string\n customFieldEntityIds?: string[]\n labelPrefix?: string\n appearanceLabels?: AppearanceSelectorLabels\n}\n\nfunction normalizeCustomFieldSubmitValue(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.filter((entry) => entry !== undefined)\n }\n if (value === undefined) return null\n return value\n}\n\nfunction buildActivityValidationError(errors: Array<{ path: string; message: string }>, translate: (key: string, fallback?: string) => string) {\n const issue = errors[0]\n if (!issue) {\n throw createCrudFormError(translate('error', 'Failed to save activity.'))\n }\n const message = issue.message === INVALID_DATE_MESSAGE\n ? translate('invalidDate', 'Invalid date')\n : translate('error', 'Failed to save activity.')\n const field = issue.path\n throw createCrudFormError(message, field ? { [field]: message } : undefined)\n}\n\nfunction ActivityForm({\n mode,\n initialValues,\n onSubmit,\n onCancel,\n submitLabel,\n cancelLabel,\n isSubmitting = false,\n activityTypeLabels,\n loadActivityOptions,\n createActivityOption,\n dealOptions,\n entityOptions,\n defaultEntityId,\n manageHref = '/backend/config/dictionaries',\n customFieldEntityIds,\n labelPrefix = 'customers.people.detail.activities',\n appearanceLabels,\n}: ActivityFormProps) {\n const tHook = useT()\n const t = React.useMemo<Translator>(() => createTranslatorWithFallback(tHook), [tHook])\n const translate = React.useCallback(\n (suffix: string, fallback?: string) => t(`${labelPrefix}.${suffix}`, fallback ?? ''),\n [labelPrefix, t],\n )\n const [pending, setPending] = React.useState(false)\n\n const normalizedDealOptions = React.useMemo(() => {\n if (!Array.isArray(dealOptions)) return []\n const seen = new Set<string>()\n return dealOptions\n .map((option) => {\n if (!option || typeof option !== 'object') return null\n const id = typeof option.id === 'string' ? option.id.trim() : ''\n if (!id || seen.has(id)) return null\n const label =\n typeof option.label === 'string' && option.label.trim().length\n ? option.label.trim()\n : id\n seen.add(id)\n return { id, label }\n })\n .filter((option): option is { id: string; label: string } => !!option)\n }, [dealOptions])\n\n const normalizedEntityOptions = React.useMemo(() => {\n if (!Array.isArray(entityOptions)) return []\n const seen = new Set<string>()\n return entityOptions\n .map((option) => {\n if (!option || typeof option !== 'object') return null\n const id = typeof option.id === 'string' ? option.id.trim() : ''\n if (!id || seen.has(id)) return null\n const label =\n typeof option.label === 'string' && option.label.trim().length\n ? option.label.trim()\n : id\n seen.add(id)\n return { id, label }\n })\n .filter((option): option is { id: string; label: string } => !!option)\n }, [entityOptions])\n\n const baseFields = React.useMemo<CrudField[]>(() => {\n const fields: CrudField[] = []\n\n if (normalizedEntityOptions.length) {\n fields.push({\n id: 'entityId',\n label: translate('fields.entity', 'Assign to record'),\n type: 'custom',\n layout: 'half',\n component: ({ value, setValue }) => {\n const currentValue =\n typeof value === 'string' && value.length ? value : normalizedEntityOptions[0]?.id ?? ''\n return (\n <select\n 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\"\n value={currentValue}\n onChange={(event) => setValue(event.target.value)}\n >\n {normalizedEntityOptions.map((option) => (\n <option key={option.id} value={option.id}>\n {option.label}\n </option>\n ))}\n </select>\n )\n },\n } as CrudField)\n }\n\n if (normalizedDealOptions.length) {\n fields.push({\n id: 'dealId',\n label: translate('fields.deal', 'Link to deal (optional)'),\n type: 'custom',\n layout: 'half',\n component: ({ value, setValue }) => {\n const currentValue = typeof value === 'string' ? value : ''\n return (\n <select\n 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\"\n value={currentValue}\n onChange={(event) => setValue(event.target.value)}\n >\n <option value=\"\">\n {translate('fields.dealPlaceholder', 'No linked deal')}\n </option>\n {normalizedDealOptions.map((option) => (\n <option key={option.id} value={option.id}>\n {option.label}\n </option>\n ))}\n </select>\n )\n },\n } as CrudField)\n }\n\n fields.push({\n id: 'activityType',\n label: translate('fields.type', 'Activity type'),\n type: 'custom',\n required: true,\n layout: 'half',\n component: ({ value, setValue }) => (\n <DictionaryEntrySelect\n value={typeof value === 'string' ? value : undefined}\n onChange={(next) => setValue(next ?? '')}\n fetchOptions={loadActivityOptions}\n createOption={createActivityOption}\n labels={activityTypeLabels}\n allowAppearance\n allowInlineCreate\n appearanceLabels={appearanceLabels}\n selectClassName=\"w-full\"\n manageHref={manageHref}\n />\n ),\n } as CrudField)\n\n fields.push({\n id: 'subject',\n label: translate('fields.subject', 'Subject'),\n type: 'text',\n layout: 'half',\n placeholder: translate('subjectPlaceholder', 'Add a subject (optional)'),\n } as CrudField)\n\n fields.push({\n id: 'body',\n label: translate('fields.body', 'Details'),\n type: 'textarea',\n placeholder: translate('bodyPlaceholder', 'Describe the interaction'),\n } as CrudField)\n\n fields.push({\n id: 'occurredAt',\n label: translate('fields.occurredAt', 'Occurred / will occur at'),\n type: 'custom',\n component: ({ value, setValue }) => (\n <input\n type=\"datetime-local\"\n className=\"w-full rounded-md border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n value={typeof value === 'string' ? value : ''}\n onChange={(event) => setValue(event.target.value || '')}\n onFocus={(event) => {\n const target = event.currentTarget as HTMLInputElement & { showPicker?: () => void }\n if (typeof target.showPicker === 'function') {\n try { target.showPicker() } catch { /* ignore unsupported */ }\n }\n }}\n onClick={(event) => {\n const target = event.currentTarget as HTMLInputElement & { showPicker?: () => void }\n if (typeof target.showPicker === 'function') {\n try { target.showPicker() } catch { /* ignore unsupported */ }\n }\n }}\n />\n ),\n layout: 'half',\n } as CrudField)\n\n return fields\n }, [\n activityTypeLabels,\n appearanceLabels,\n createActivityOption,\n loadActivityOptions,\n manageHref,\n normalizedDealOptions,\n normalizedEntityOptions,\n translate,\n ])\n\n const baseFieldIds = React.useMemo(() => new Set(baseFields.map((field) => field.id)), [baseFields])\n\n const groups = React.useMemo<CrudFormGroup[]>(() => {\n const detailFields: string[] = []\n if (normalizedEntityOptions.length) detailFields.push('entityId')\n if (normalizedDealOptions.length) detailFields.push('dealId')\n detailFields.push('activityType', 'subject', 'occurredAt', 'body')\n const baseGroups: CrudFormGroup[] = [\n {\n id: 'details',\n title: translate('form.details', 'Activity details'),\n column: 1,\n fields: detailFields,\n },\n ]\n baseGroups.push({\n id: 'custom',\n title: translate('form.customFields', 'Custom fields'),\n column: 2,\n kind: 'customFields',\n })\n return baseGroups\n }, [normalizedDealOptions.length, normalizedEntityOptions.length, translate])\n\n const handleSubmit = React.useCallback(\n async (values: Record<string, unknown>) => {\n if (pending || isSubmitting) return\n setPending(true)\n try {\n const parsed = schema.validate(values)\n if (!parsed.ok) {\n throw buildActivityValidationError(parsed.errors ?? [], translate)\n }\n const rawEntityId = typeof values.entityId === 'string' ? values.entityId.trim() : ''\n const resolvedEntityId = rawEntityId || (typeof defaultEntityId === 'string' ? defaultEntityId : '')\n const rawDealId = typeof values.dealId === 'string' ? values.dealId.trim() : ''\n const base: ActivityFormBaseValues = {\n activityType: typeof values.activityType === 'string' ? values.activityType.trim() : '',\n subject: typeof values.subject === 'string' && values.subject.trim().length ? values.subject.trim() : undefined,\n body: typeof values.body === 'string' && values.body.trim().length ? values.body.trim() : undefined,\n occurredAt: typeof values.occurredAt === 'string' && values.occurredAt.trim().length\n ? new Date(values.occurredAt as string).toISOString()\n : undefined,\n dealId: rawDealId.length ? rawDealId : undefined,\n }\n const reservedCustomKeys = new Set(['entityId', 'dealId'])\n const customEntries = collectCustomFieldValues(values, {\n transform: (value) => normalizeCustomFieldSubmitValue(value),\n accept: (fieldId) => !reservedCustomKeys.has(fieldId),\n })\n Object.entries(values).forEach(([key, value]) => {\n if (key.startsWith('cf_')) return\n if (!baseFieldIds.has(key) && key !== 'id') {\n if (reservedCustomKeys.has(key)) return\n customEntries[key] = normalizeCustomFieldSubmitValue(value)\n }\n })\n await onSubmit({ base, custom: customEntries, entityId: resolvedEntityId.length ? resolvedEntityId : undefined })\n } finally {\n setPending(false)\n }\n },\n [baseFieldIds, defaultEntityId, isSubmitting, onSubmit, pending, translate],\n )\n\n const embeddedInitialValues = React.useMemo(() => {\n const occurredAt = toLocalDateTimeInput(initialValues?.occurredAt ?? null)\n const resolvedEntity = (() => {\n const raw = typeof (initialValues as Record<string, unknown> | undefined)?.entityId === 'string'\n ? (initialValues as Record<string, unknown>).entityId as string\n : typeof defaultEntityId === 'string'\n ? defaultEntityId\n : normalizedEntityOptions[0]?.id ?? ''\n return raw ?? ''\n })()\n const resolvedDeal = typeof (initialValues as Record<string, unknown> | undefined)?.dealId === 'string'\n ? (initialValues as Record<string, unknown>).dealId as string\n : ''\n\n return {\n entityId: resolvedEntity,\n dealId: resolvedDeal,\n activityType: initialValues?.activityType ?? '',\n subject: initialValues?.subject ?? '',\n body: initialValues?.body ?? '',\n occurredAt,\n ...Object.fromEntries(\n Object.entries(initialValues ?? {})\n .filter(([key]) => {\n if (!key.startsWith('cf_')) return false\n const trimmed = key.slice(3)\n return trimmed !== 'entityId' && trimmed !== 'dealId'\n })\n .map(([key, value]) => [key, value]),\n ),\n }\n }, [defaultEntityId, initialValues, normalizedEntityOptions])\n\n return (\n <CrudForm<Record<string, unknown>>\n embedded\n fields={baseFields}\n groups={groups}\n initialValues={embeddedInitialValues}\n onSubmit={handleSubmit}\n submitLabel={submitLabel ?? (mode === 'edit'\n ? translate('update', 'Update activity (\u2318/Ctrl + Enter)')\n : translate('save', 'Save activity (\u2318/Ctrl + Enter)'))}\n extraActions={(\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={onCancel}\n disabled={pending || isSubmitting}\n >\n {cancelLabel ?? translate('cancel', 'Cancel')}\n </Button>\n )}\n entityIds={customFieldEntityIds}\n />\n )\n}\n\ntype ActivityDialogProps = {\n open: boolean\n mode: 'create' | 'edit'\n onOpenChange: (next: boolean) => void\n initialValues?: Partial<ActivityFormBaseValues & Record<string, unknown>>\n onSubmit: (payload: ActivityFormSubmitPayload) => Promise<void>\n isSubmitting?: boolean\n activityTypeLabels: DictionarySelectLabels\n loadActivityOptions: () => Promise<DictionaryOption[]>\n createActivityOption?: (input: { value: string; label?: string; color?: string | null; icon?: string | null }) => Promise<DictionaryOption>\n titles?: {\n create?: string\n edit?: string\n }\n submitLabels?: {\n create?: string\n edit?: string\n }\n cancelLabel?: string\n dealOptions?: Array<{ id: string; label: string }>\n entityOptions?: Array<{ id: string; label: string }>\n defaultEntityId?: string | null\n manageHref?: string\n customFieldEntityIds?: string[]\n labelPrefix?: string\n appearanceLabels?: AppearanceSelectorLabels\n}\n\nfunction ActivityDialog({\n open,\n mode,\n onOpenChange,\n initialValues,\n onSubmit,\n isSubmitting,\n activityTypeLabels,\n loadActivityOptions,\n createActivityOption,\n titles,\n submitLabels,\n cancelLabel,\n dealOptions,\n entityOptions,\n defaultEntityId,\n manageHref,\n customFieldEntityIds,\n labelPrefix = 'customers.people.detail.activities',\n appearanceLabels,\n}: ActivityDialogProps) {\n const tHook = useT()\n const t = React.useMemo<Translator>(() => createTranslatorWithFallback(tHook), [tHook])\n const translate = React.useCallback(\n (suffix: string, fallback?: string, params?: Record<string, string | number>) =>\n t(`${labelPrefix}.${suffix}`, fallback ?? '', params),\n [labelPrefix, t],\n )\n\n const dialogTitle =\n mode === 'edit'\n ? titles?.edit ?? translate('editTitle', 'Edit activity')\n : titles?.create ?? translate('addTitle', 'Add activity')\n\n const resolvedSubmitLabel =\n mode === 'edit'\n ? submitLabels?.edit ?? translate('update', 'Update activity (\u2318/Ctrl + Enter)')\n : submitLabels?.create ?? translate('save', 'Save activity (\u2318/Ctrl + Enter)')\n\n const resolvedCancelLabel = cancelLabel ?? translate('cancel', 'Cancel')\n\n const handleCancel = React.useCallback(() => {\n onOpenChange(false)\n }, [onOpenChange])\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"sm:max-w-3xl\">\n <DialogHeader>\n <DialogTitle>{dialogTitle}</DialogTitle>\n </DialogHeader>\n <ActivityForm\n mode={mode}\n initialValues={initialValues}\n onSubmit={onSubmit}\n onCancel={handleCancel}\n submitLabel={resolvedSubmitLabel}\n cancelLabel={resolvedCancelLabel}\n isSubmitting={isSubmitting}\n activityTypeLabels={activityTypeLabels}\n loadActivityOptions={loadActivityOptions}\n createActivityOption={createActivityOption}\n dealOptions={dealOptions}\n entityOptions={entityOptions}\n defaultEntityId={defaultEntityId}\n manageHref={manageHref}\n customFieldEntityIds={customFieldEntityIds}\n labelPrefix={labelPrefix}\n appearanceLabels={appearanceLabels}\n />\n </DialogContent>\n </Dialog>\n )\n}\n\nexport type ActivitiesSectionProps<C = unknown> = {\n entityId: string | null\n dealId?: string | null\n addActionLabel: string\n emptyState: TabEmptyStateConfig\n onActionChange?: (action: SectionAction | null) => void\n onLoadingChange?: (isLoading: boolean) => void\n dealOptions?: Array<{ id: string; label: string }>\n entityOptions?: Array<{ id: string; label: string }>\n defaultEntityId?: string | null\n dataAdapter: ActivitiesDataAdapter<C>\n dataContext?: C\n activityTypeLabels: DictionarySelectLabels\n loadActivityOptions: () => Promise<DictionaryOption[]>\n createActivityOption?: (input: { value: string; label?: string; color?: string | null; icon?: string | null }) => Promise<DictionaryOption>\n resolveActivityPresentation?: (activity: ActivitySummary) => ActivityTypePresentation\n renderCustomFields?: (activity: ActivitySummary) => React.ReactNode\n customFieldEntityIds?: string[]\n labelPrefix?: string\n renderIcon?: (icon: string, className?: string) => React.ReactNode\n renderColor?: (color: string, className?: string) => React.ReactNode\n appearanceLabels?: AppearanceSelectorLabels\n dealLinkHref?: (dealId: string) => string\n manageHref?: string\n}\n\nfunction ActivitiesSectionImpl<C = unknown>({\n entityId,\n dealId,\n addActionLabel,\n emptyState,\n onActionChange,\n onLoadingChange,\n dealOptions,\n entityOptions,\n defaultEntityId,\n dataAdapter,\n dataContext,\n activityTypeLabels,\n loadActivityOptions,\n createActivityOption,\n resolveActivityPresentation,\n renderCustomFields,\n customFieldEntityIds,\n labelPrefix = 'customers.people.detail.activities',\n renderIcon,\n renderColor,\n appearanceLabels,\n dealLinkHref,\n manageHref,\n}: ActivitiesSectionProps<C>) {\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const tHook = useT()\n const baseTranslator = React.useMemo<Translator>(() => createTranslatorWithFallback(tHook), [tHook])\n const translate = React.useCallback(\n (suffix: string, fallback?: string, params?: Record<string, string | number>) =>\n baseTranslator(`${labelPrefix}.${suffix}`, fallback ?? '', params),\n [baseTranslator, labelPrefix],\n )\n const resolvedDefaultEntityId = React.useMemo(() => {\n const primary = typeof entityId === 'string' ? entityId.trim() : ''\n if (primary.length) return primary\n const fallback = typeof defaultEntityId === 'string' ? defaultEntityId.trim() : ''\n if (fallback.length) return fallback\n if (Array.isArray(entityOptions)) {\n for (const option of entityOptions) {\n if (!option || typeof option !== 'object') continue\n const id = typeof option.id === 'string' ? option.id.trim() : ''\n if (id.length) return id\n }\n }\n return ''\n }, [defaultEntityId, entityId, entityOptions])\n\n const resolveEntityForSubmission = React.useCallback(\n (input?: string | null) => {\n const candidate = typeof input === 'string' ? input.trim() : ''\n if (candidate.length) return candidate\n return resolvedDefaultEntityId.length ? resolvedDefaultEntityId : null\n },\n [resolvedDefaultEntityId],\n )\n\n const [activities, setActivities] = React.useState<ActivitySummary[]>([])\n const [isLoading, setIsLoading] = React.useState<boolean>(() => {\n const entity = typeof entityId === 'string' ? entityId.trim() : ''\n const deal = typeof dealId === 'string' ? dealId.trim() : ''\n return Boolean(entity || deal || resolvedDefaultEntityId)\n })\n const [loadError, setLoadError] = React.useState<string | null>(null)\n const [pendingAction, setPendingAction] = React.useState<PendingAction | null>(null)\n const [dialogOpen, setDialogOpen] = React.useState(false)\n const [dialogMode, setDialogMode] = React.useState<'create' | 'edit'>('create')\n const [editingActivityId, setEditingActivityId] = React.useState<string | null>(null)\n const [initialValues, setInitialValues] = React.useState<Partial<ActivityFormBaseValues & Record<string, unknown>> | undefined>(undefined)\n const [visibleCount, setVisibleCount] = React.useState(0)\n const pendingCounterRef = React.useRef(0)\n const onLoadingChangeRef = React.useRef(onLoadingChange)\n React.useEffect(() => { onLoadingChangeRef.current = onLoadingChange })\n\n const t = translate\n\n const pushLoading = React.useCallback(() => {\n pendingCounterRef.current += 1\n if (pendingCounterRef.current === 1) {\n onLoadingChangeRef.current?.(true)\n }\n }, [])\n\n const popLoading = React.useCallback(() => {\n pendingCounterRef.current = Math.max(0, pendingCounterRef.current - 1)\n if (pendingCounterRef.current === 0) {\n onLoadingChangeRef.current?.(false)\n }\n }, [])\n\n const updateVisibleCount = React.useCallback((length: number) => {\n if (!length) {\n setVisibleCount(0)\n return\n }\n const baseline = Math.min(5, length)\n setVisibleCount((prev) => {\n if (prev >= length) {\n return Math.min(prev, length)\n }\n return Math.min(Math.max(prev, baseline), length)\n })\n }, [])\n\n const loadActivities = React.useCallback(async () => {\n const queryEntityId = typeof entityId === 'string' ? entityId.trim() : ''\n const queryDealId = typeof dealId === 'string' ? dealId.trim() : ''\n if (!queryEntityId && !queryDealId) {\n setActivities([])\n setLoadError(null)\n updateVisibleCount(0)\n return\n }\n pushLoading()\n setIsLoading(true)\n try {\n const items = await dataAdapter.list({\n entityId: queryEntityId || null,\n dealId: queryDealId || null,\n context: dataContext,\n })\n setActivities(items)\n setLoadError(null)\n updateVisibleCount(items.length)\n } catch (err) {\n const message =\n err instanceof Error\n ? err.message\n : t('loadError', 'Failed to load activities.')\n setLoadError(message)\n } finally {\n setIsLoading(false)\n popLoading()\n }\n }, [dataAdapter, dataContext, dealId, entityId, popLoading, pushLoading, t, updateVisibleCount])\n\n React.useEffect(() => {\n updateVisibleCount(activities.length)\n }, [activities.length, updateVisibleCount])\n\n React.useEffect(() => {\n const queryEntityId = typeof entityId === 'string' ? entityId.trim() : ''\n const queryDealId = typeof dealId === 'string' ? dealId.trim() : ''\n if (!queryEntityId && !queryDealId) {\n setActivities([])\n setLoadError(null)\n setIsLoading(false)\n pendingCounterRef.current = 0\n onLoadingChangeRef.current?.(false)\n updateVisibleCount(0)\n return\n }\n loadActivities().catch(() => {})\n }, [dealId, entityId, loadActivities, updateVisibleCount])\n\n const openCreateDialog = React.useCallback(() => {\n setDialogMode('create')\n setEditingActivityId(null)\n setInitialValues(undefined)\n setDialogOpen(true)\n }, [])\n\n const openEditDialog = React.useCallback((activity: ActivitySummary) => {\n setDialogMode('edit')\n setEditingActivityId(activity.id)\n const baseValues: Partial<ActivityFormBaseValues & Record<string, unknown>> = {\n activityType: activity.activityType,\n subject: activity.subject ?? '',\n body: activity.body ?? '',\n occurredAt: activity.occurredAt ?? activity.createdAt ?? null,\n dealId: activity.dealId ?? '',\n entityId: activity.entityId ?? '',\n }\n const customEntries = Array.isArray(activity.customFields) ? activity.customFields : []\n customEntries.forEach((entry) => {\n if (entry.key === 'entityId' || entry.key === 'dealId') return\n baseValues[`cf_${entry.key}`] = entry.value ?? null\n })\n setInitialValues(baseValues)\n setDialogOpen(true)\n }, [])\n\n const closeDialog = React.useCallback(() => {\n setDialogOpen(false)\n setDialogMode('create')\n setEditingActivityId(null)\n setInitialValues(undefined)\n }, [])\n\n const handleDialogOpenChange = React.useCallback(\n (next: boolean) => {\n if (!next) {\n closeDialog()\n } else {\n setDialogOpen(true)\n }\n },\n [closeDialog],\n )\n\n const handleCreate = React.useCallback(\n async ({ base, custom, entityId: formEntityId }: ActivityFormSubmitPayload) => {\n const submissionEntityId = resolveEntityForSubmission(formEntityId)\n if (!submissionEntityId) {\n const message = t('entityMissing', 'Select a related record before saving.')\n flash(message, 'error')\n throw new Error(message)\n }\n setPendingAction({ kind: 'create' })\n pushLoading()\n try {\n const payload: ActivityCreatePayload = {\n entityId: submissionEntityId,\n activityType: base.activityType,\n subject: base.subject ?? undefined,\n body: base.body ?? undefined,\n occurredAt: base.occurredAt ?? undefined,\n dealId: base.dealId ?? undefined,\n customFields: Object.keys(custom).length ? custom : undefined,\n }\n await dataAdapter.create({ ...payload, context: dataContext })\n await loadActivities()\n flash(t('success', 'Activity saved'), 'success')\n } finally {\n setPendingAction(null)\n popLoading()\n }\n },\n [dataAdapter, dataContext, loadActivities, popLoading, pushLoading, resolveEntityForSubmission, t],\n )\n\n const handleUpdate = React.useCallback(\n async (activityId: string, { base, custom, entityId: formEntityId }: ActivityFormSubmitPayload) => {\n const submissionEntityId = resolveEntityForSubmission(formEntityId)\n if (!submissionEntityId) {\n const message = t('entityMissing', 'Select a related record before saving.')\n flash(message, 'error')\n throw new Error(message)\n }\n setPendingAction({ kind: 'update', id: activityId })\n pushLoading()\n try {\n const patch: ActivityUpdatePayload = {\n entityId: submissionEntityId,\n activityType: base.activityType,\n subject: base.subject ?? undefined,\n body: base.body ?? undefined,\n occurredAt: base.occurredAt ?? undefined,\n dealId: base.dealId ?? undefined,\n customFields: Object.keys(custom).length ? custom : undefined,\n }\n await dataAdapter.update({ id: activityId, patch, context: dataContext })\n await loadActivities()\n flash(t('updateSuccess', 'Activity updated.'), 'success')\n } finally {\n setPendingAction(null)\n popLoading()\n }\n },\n [dataAdapter, dataContext, loadActivities, popLoading, pushLoading, resolveEntityForSubmission, t],\n )\n\n const handleDelete = React.useCallback(\n async (activity: ActivitySummary) => {\n if (!activity.id) return\n const confirmed = await confirm({\n title: t('deleteConfirm', 'Delete this activity? You can restore it using version history.'),\n variant: 'destructive',\n })\n if (!confirmed) return\n setPendingAction({ kind: 'delete', id: activity.id })\n try {\n await dataAdapter.delete({ id: activity.id, context: dataContext })\n setActivities((prev) => prev.filter((existing) => existing.id !== activity.id))\n flash(t('deleteSuccess', 'Activity deleted.'), 'success')\n } catch (err) {\n const message =\n err instanceof Error\n ? err.message\n : t('deleteError', 'Failed to delete activity.')\n flash(message, 'error')\n } finally {\n setPendingAction(null)\n }\n },\n [confirm, dataAdapter, dataContext, t],\n )\n\n const handleDialogSubmit = React.useCallback(\n async (payload: ActivityFormSubmitPayload) => {\n try {\n if (dialogMode === 'edit' && editingActivityId) {\n await handleUpdate(editingActivityId, payload)\n } else {\n await handleCreate(payload)\n }\n closeDialog()\n } catch (err) {\n const message =\n err instanceof Error ? err.message : t('error', 'Failed to save activity')\n flash(message, 'error')\n }\n },\n [closeDialog, dialogMode, editingActivityId, handleCreate, handleUpdate, t],\n )\n\n React.useEffect(() => {\n if (!onActionChange) return\n if (activities.length === 0) {\n onActionChange(null)\n return () => {\n onActionChange(null)\n }\n }\n const disabled = resolveEntityForSubmission(null) === null || pendingAction !== null || isLoading\n const action: SectionAction = {\n label: (\n <span className=\"inline-flex items-center gap-1.5\">\n <Plus className=\"h-4 w-4\" />\n {addActionLabel}\n </span>\n ),\n onClick: () => {\n if (!disabled) openCreateDialog()\n },\n disabled,\n }\n onActionChange(action)\n return () => {\n onActionChange(null)\n }\n }, [\n activities.length,\n addActionLabel,\n isLoading,\n onActionChange,\n openCreateDialog,\n pendingAction,\n resolveEntityForSubmission,\n ])\n\n const isFormPending =\n pendingAction?.kind === 'create' ||\n (pendingAction?.kind === 'update' && pendingAction.id === editingActivityId)\n const visibleActivities = React.useMemo(\n () => activities.slice(0, visibleCount),\n [activities, visibleCount],\n )\n const hasMoreActivities = visibleCount < activities.length\n const loadMoreLabel = t('loadMore', 'Load more activities')\n\n const handleLoadMore = React.useCallback(() => {\n setVisibleCount((prev) => {\n if (prev >= activities.length) return prev\n return Math.min(prev + 5, activities.length)\n })\n }, [activities.length])\n\n const resolvePresentation = React.useCallback(\n (activity: ActivitySummary): ActivityTypePresentation => {\n if (resolveActivityPresentation) return resolveActivityPresentation(activity)\n return {\n label: activity.activityType,\n icon: activity.appearanceIcon ?? null,\n color: activity.appearanceColor ?? null,\n }\n },\n [resolveActivityPresentation],\n )\n\n const resolveDealHref = React.useCallback(\n (id: string) => (dealLinkHref ? dealLinkHref(id) : `/backend/customers/deals/${encodeURIComponent(id)}`),\n [dealLinkHref],\n )\n\n return (\n <div className=\"mt-3 space-y-4\">\n {loadError ? (\n <div className=\"rounded-md border border-destructive/40 bg-destructive/5 px-3 py-2 text-sm text-destructive\">\n {loadError}\n </div>\n ) : null}\n <div className=\"space-y-4\">\n {isLoading && activities.length === 0 ? (\n <LoadingMessage\n label={t('loading', 'Loading activities\u2026')}\n className=\"border-0 bg-transparent p-0 py-8 justify-center\"\n />\n ) : (\n <>\n {!isLoading && activities.length === 0 && !dialogOpen ? (\n <TabEmptyState\n title={emptyState.title}\n action={{\n label: emptyState.actionLabel,\n onClick: openCreateDialog,\n disabled: resolveEntityForSubmission(null) === null || pendingAction !== null,\n }}\n />\n ) : null}\n {visibleActivities.length > 0\n ? visibleActivities.map((activity) => {\n const presentation = resolvePresentation(activity)\n const timestampValue = activity.occurredAt ?? activity.createdAt ?? null\n const occurredLabel =\n formatDateTime(timestampValue) ?? t('noDate', 'No date provided')\n const authorLabel = activity.authorName ?? activity.authorEmail ?? null\n const loggedByText = authorLabel\n ? (() => {\n const translated = t('loggedBy', `Logged by ${authorLabel}`, { user: authorLabel })\n if (\n !translated ||\n translated.includes('{{') ||\n translated.includes('{user')\n ) {\n return `Logged by ${authorLabel}`\n }\n return translated\n })()\n : null\n const isUpdatePending = pendingAction?.kind === 'update' && pendingAction.id === activity.id\n const isDeletePending = pendingAction?.kind === 'delete' && pendingAction.id === activity.id\n\n return (\n <div\n key={activity.id}\n className=\"group space-y-3 rounded-lg border bg-card p-4 transition hover:border-border/70 cursor-pointer\"\n role=\"button\"\n tabIndex={0}\n onClick={() => openEditDialog(activity)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n openEditDialog(activity)\n }\n }}\n >\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <TimelineItemHeader\n title={presentation.label}\n timestamp={timestampValue}\n fallbackTimestampLabel={occurredLabel}\n icon={presentation.icon}\n color={presentation.color}\n renderIcon={renderIcon}\n renderColor={renderColor}\n />\n {activity.dealId ? (\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <ArrowUpRightSquare className=\"h-3.5 w-3.5\" />\n <Link\n href={resolveDealHref(activity.dealId)}\n className=\"font-medium text-foreground hover:underline\"\n onClick={(event) => event.stopPropagation()}\n >\n {activity.dealTitle && activity.dealTitle.length\n ? activity.dealTitle\n : t('linkedDeal', 'Linked deal')}\n </Link>\n </div>\n ) : null}\n </div>\n <div className=\"flex items-center gap-1 opacity-100 md:opacity-0 transition-opacity md:group-hover:opacity-100 focus-within:opacity-100\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={(event) => {\n event.stopPropagation()\n openEditDialog(activity)\n }}\n disabled={pendingAction !== null}\n >\n {isUpdatePending ? (\n <span className=\"relative flex h-4 w-4 items-center justify-center\">\n <span className=\"absolute h-4 w-4 animate-spin rounded-full border border-primary border-t-transparent\" />\n </span>\n ) : (\n <Pencil className=\"h-4 w-4\" />\n )}\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={(event) => {\n event.stopPropagation()\n handleDelete(activity).catch(() => {})\n }}\n disabled={pendingAction !== null}\n >\n {isDeletePending ? (\n <span className=\"relative flex h-4 w-4 items-center justify-center text-destructive\">\n <span className=\"absolute h-4 w-4 animate-spin rounded-full border border-destructive border-t-transparent\" />\n </span>\n ) : (\n <Trash2 className=\"h-4 w-4\" />\n )}\n </Button>\n </div>\n </div>\n {activity.subject ? <p className=\"text-sm font-medium\">{activity.subject}</p> : null}\n {activity.body ? (\n <p className=\"text-sm whitespace-pre-wrap text-muted-foreground\">{activity.body}</p>\n ) : null}\n {renderCustomFields ? renderCustomFields(activity) : null}\n {loggedByText ? (\n <p className=\"text-xs text-muted-foreground\">{loggedByText}</p>\n ) : null}\n </div>\n )\n })\n : null}\n {hasMoreActivities ? (\n <div className=\"flex justify-center\">\n <Button variant=\"outline\" size=\"sm\" onClick={handleLoadMore} disabled={pendingAction !== null}>\n {loadMoreLabel}\n </Button>\n </div>\n ) : null}\n </>\n )}\n </div>\n\n <ActivityDialog\n open={dialogOpen}\n mode={dialogMode}\n onOpenChange={handleDialogOpenChange}\n initialValues={initialValues}\n onSubmit={async (payload) => {\n await handleDialogSubmit(payload)\n }}\n isSubmitting={Boolean(isFormPending)}\n activityTypeLabels={activityTypeLabels}\n loadActivityOptions={loadActivityOptions}\n createActivityOption={createActivityOption}\n dealOptions={dealOptions}\n entityOptions={entityOptions}\n defaultEntityId={resolvedDefaultEntityId || undefined}\n manageHref={manageHref}\n customFieldEntityIds={customFieldEntityIds}\n labelPrefix={labelPrefix}\n appearanceLabels={appearanceLabels}\n />\n {ConfirmDialogElement}\n </div>\n )\n}\n\nexport function ActivitiesSection<C = unknown>(props: ActivitiesSectionProps<C>) {\n const handle = ComponentReplacementHandles.section('ui.detail', 'ActivitiesSection')\n const Resolved = useRegisteredComponent<ActivitiesSectionProps<C>>(\n handle,\n ActivitiesSectionImpl as React.ComponentType<ActivitiesSectionProps<C>>,\n )\n\n return (\n <div data-component-handle={handle}>\n <Resolved {...props} />\n </div>\n )\n}\n\nexport default ActivitiesSection\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -146,8 +146,8 @@ function AddressEditor({
146
146
  [addressTypesAdapter, addressTypesContext, label, typeValue]
147
147
  );
148
148
  const inputClass = (field) => [
149
- "w-full rounded-md border px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring",
150
- errors[field] ? "border-red-500 focus:ring-red-500" : "border-input bg-background"
149
+ "w-full rounded-md border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
150
+ errors[field] ? "border-red-500 aria-invalid:ring-destructive" : "border-input bg-background"
151
151
  ].join(" ");
152
152
  return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
153
153
  /* @__PURE__ */ jsxs("div", { className: "grid gap-2 sm:grid-cols-2", children: [
@@ -374,7 +374,7 @@ function AddressEditor({
374
374
  onChange: (evt) => setCountryQuery(evt.target.value)
375
375
  }
376
376
  ),
377
- /* @__PURE__ */ jsx("div", { className: "max-h-64 overflow-auto rounded-md border border-border/60", children: /* @__PURE__ */ jsx("ul", { className: "divide-y divide-border/50", children: filteredCountryOptions.map((option) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
377
+ /* @__PURE__ */ jsx("div", { className: "max-h-64 overflow-auto rounded-md border border-border/70", children: /* @__PURE__ */ jsx("ul", { className: "divide-y divide-border/50", children: filteredCountryOptions.map((option) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
378
378
  Button,
379
379
  {
380
380
  variant: "ghost",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/detail/AddressEditor.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { usePathname, useSearchParams } from 'next/navigation'\nimport { Plus, Settings } from 'lucide-react'\nimport { Button } from '../../primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from '@open-mercato/ui/primitives/dialog'\nimport { buildCountryOptions } from '@open-mercato/shared/lib/location/countries'\nimport { buildHrefWithReturnTo } from '@open-mercato/shared/lib/navigation/returnTo'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport type { AddressFormatStrategy } from './addressFormat'\n\ntype Translator = (key: string, fallback?: string, params?: Record<string, string | number>) => string\n\nexport type AddressTypeOption = {\n value: string\n label: string\n}\n\nexport type AddressTypesAdapter<C = unknown> = {\n list: (context?: C) => Promise<AddressTypeOption[]>\n create?: (value: string, context?: C) => Promise<AddressTypeOption | null>\n manageHref?: string\n}\n\nexport type AddressEditorDraft = {\n name: string\n purpose: string\n companyName: string\n addressLine1: string\n addressLine2: string\n buildingNumber: string\n flatNumber: string\n city: string\n region: string\n postalCode: string\n country: string\n isPrimary: boolean\n}\n\nexport type AddressEditorField =\n | 'name'\n | 'purpose'\n | 'companyName'\n | 'addressLine1'\n | 'addressLine2'\n | 'buildingNumber'\n | 'flatNumber'\n | 'city'\n | 'region'\n | 'postalCode'\n | 'country'\n | 'isPrimary'\n\ntype AddressEditorProps<C = unknown> = {\n value: AddressEditorDraft\n onChange: (next: AddressEditorDraft) => void\n format: AddressFormatStrategy\n t: Translator\n labelPrefix?: string\n disabled?: boolean\n errors?: Partial<Record<AddressEditorField, string>>\n hidePrimaryToggle?: boolean\n showFormatHint?: boolean\n addressTypesAdapter?: AddressTypesAdapter<C>\n addressTypesContext?: C\n}\n\nexport function AddressEditor<C = unknown>({\n value,\n onChange,\n format,\n t,\n labelPrefix = 'customers.people.detail.addresses',\n disabled = false,\n errors = {},\n hidePrimaryToggle = false,\n showFormatHint = true,\n addressTypesAdapter,\n addressTypesContext,\n}: AddressEditorProps<C>) {\n const pathname = usePathname()\n const searchParams = useSearchParams()\n const label = React.useCallback(\n (suffix: string, fallback?: string, params?: Record<string, string | number>) =>\n t(`${labelPrefix}.${suffix}`, fallback, params),\n [labelPrefix, t],\n )\n\n const [addressTypes, setAddressTypes] = React.useState<AddressTypeOption[]>([])\n const [addressTypesLoading, setAddressTypesLoading] = React.useState(false)\n const [addressTypeError, setAddressTypeError] = React.useState<string | null>(null)\n\n const [typeDialogOpen, setTypeDialogOpen] = React.useState(false)\n const [typeValue, setTypeValue] = React.useState('')\n const [typeFormError, setTypeFormError] = React.useState<string | null>(null)\n const [countryDialogOpen, setCountryDialogOpen] = React.useState(false)\n const [countryQuery, setCountryQuery] = React.useState('')\n\n const countryOptions = React.useMemo(\n () =>\n buildCountryOptions({\n transformLabel: (code, fallback) => t(`customers.countries.${code.toLowerCase()}`, fallback ?? code),\n }),\n [t],\n )\n\n React.useEffect(() => {\n let cancelled = false\n const load = async () => {\n if (!addressTypesAdapter) {\n setAddressTypes([])\n setAddressTypeError(null)\n return\n }\n setAddressTypesLoading(true)\n try {\n const result = await addressTypesAdapter.list(addressTypesContext)\n if (!cancelled) {\n setAddressTypes(Array.isArray(result) ? result : [])\n setAddressTypeError(null)\n }\n } catch (err) {\n if (!cancelled) {\n setAddressTypes([])\n setAddressTypeError(label('types.loadError', 'Failed to load address types'))\n }\n } finally {\n if (!cancelled) setAddressTypesLoading(false)\n }\n }\n load().catch(() => {})\n return () => {\n cancelled = true\n }\n }, [addressTypesAdapter, addressTypesContext, label])\n\n const current: AddressEditorDraft = {\n name: value.name ?? '',\n purpose: value.purpose ?? '',\n companyName: value.companyName ?? '',\n addressLine1: value.addressLine1 ?? '',\n addressLine2: value.addressLine2 ?? '',\n buildingNumber: value.buildingNumber ?? '',\n flatNumber: value.flatNumber ?? '',\n city: value.city ?? '',\n region: value.region ?? '',\n postalCode: value.postalCode ?? '',\n country: value.country ?? '',\n isPrimary: value.isPrimary ?? false,\n }\n\n const update = React.useCallback(\n (key: keyof AddressEditorDraft, nextValue: string | boolean) => {\n onChange({ ...current, [key]: nextValue })\n },\n [current, onChange],\n )\n\n const filteredCountryOptions = React.useMemo(() => {\n const query = countryQuery.trim().toLowerCase()\n if (!query.length) return countryOptions\n return countryOptions.filter(\n (option) => option.label.toLowerCase().includes(query) || option.code.toLowerCase().includes(query),\n )\n }, [countryOptions, countryQuery])\n\n const selectedCountry = React.useMemo(() => {\n const code = (current.country ?? '').toUpperCase()\n if (!code.length) return null\n return countryOptions.find((option) => option.code === code) ?? null\n }, [countryOptions, current.country])\n const returnTo = React.useMemo(() => {\n const query = searchParams?.toString() ?? ''\n if (!pathname) return null\n return query.length ? `${pathname}?${query}` : pathname\n }, [pathname, searchParams])\n const manageAddressTypesHref = React.useMemo(\n () => buildHrefWithReturnTo(addressTypesAdapter?.manageHref ?? '/backend/config/dictionaries', returnTo),\n [addressTypesAdapter?.manageHref, returnTo],\n )\n\n const handleTypeSubmit = React.useCallback(\n async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault()\n const trimmed = typeValue.trim()\n if (!trimmed.length) {\n setTypeFormError(label('types.emptyError', 'Please provide a value'))\n return\n }\n if (!addressTypesAdapter?.create) return\n setTypeFormError(null)\n const created = await addressTypesAdapter.create(trimmed, addressTypesContext)\n if (created) {\n setAddressTypes((prev) => {\n const map = new Map(prev.map((entry) => [entry.value, entry]))\n map.set(created.value, created)\n return Array.from(map.values()).sort((a, b) => a.label.localeCompare(b.label))\n })\n }\n setTypeDialogOpen(false)\n setTypeValue('')\n },\n [addressTypesAdapter, addressTypesContext, label, typeValue],\n )\n\n const inputClass = (field: AddressEditorField) =>\n [\n 'w-full rounded-md border px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring',\n errors[field] ? 'border-red-500 focus:ring-red-500' : 'border-input bg-background',\n ].join(' ')\n\n return (\n <div className=\"space-y-3\">\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <Input\n className={inputClass('name')}\n placeholder={label('fields.label', 'Label')}\n value={current.name}\n onChange={(evt) => update('name', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.name ? 'true' : undefined}\n />\n <div className=\"flex gap-2\">\n <select\n className={inputClass('purpose')}\n value={current.purpose}\n onChange={(evt) => update('purpose', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.purpose ? 'true' : undefined}\n >\n <option value=\"\">\n {addressTypesLoading\n ? label('types.loading', 'Loading\u2026')\n : label('types.placeholder', 'Address type')}\n </option>\n {addressTypes.map((entry) => (\n <option key={entry.value} value={entry.value}>\n {entry.label}\n </option>\n ))}\n </select>\n {addressTypesAdapter?.create ? (\n <Dialog open={typeDialogOpen} onOpenChange={setTypeDialogOpen}>\n <DialogTrigger asChild>\n <Button type=\"button\" variant=\"outline\" size=\"icon\" className=\"shrink-0\" disabled={disabled}>\n <Plus className=\"h-4 w-4\" />\n </Button>\n </DialogTrigger>\n <DialogContent className=\"sm:max-w-md\">\n <DialogHeader>\n <DialogTitle>{label('types.add', 'Add address type')}</DialogTitle>\n <DialogDescription>\n {label('types.addHint', 'Create a new address type for reuse.')}\n </DialogDescription>\n </DialogHeader>\n <form className=\"space-y-3\" onSubmit={handleTypeSubmit}>\n <Input\n autoFocus\n value={typeValue}\n onChange={(evt) => {\n setTypeValue(evt.target.value)\n if (typeFormError) setTypeFormError(null)\n }}\n placeholder={label('types.placeholder', 'Address type')}\n disabled={disabled}\n aria-invalid={typeFormError ? 'true' : undefined}\n />\n {typeFormError ? <p className=\"text-sm text-destructive\">{typeFormError}</p> : null}\n <DialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setTypeDialogOpen(false)} disabled={disabled}>\n {label('types.cancel', 'Cancel')}\n </Button>\n <Button type=\"submit\" disabled={disabled || !typeValue.trim()}>\n {label('types.save', 'Save')}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n ) : null}\n <Button\n asChild\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"shrink-0\"\n disabled={disabled}\n title={label('types.manage', 'Manage address types')}\n >\n <Link\n href={manageAddressTypesHref}\n aria-label={label('types.manage', 'Manage address types')}\n >\n <Settings className=\"h-4 w-4\" />\n </Link>\n </Button>\n </div>\n </div>\n {errors.purpose ? <p className=\"text-xs text-destructive\">{errors.purpose}</p> : null}\n {addressTypeError ? <p className=\"text-xs text-destructive\">{addressTypeError}</p> : null}\n <Input\n className={inputClass('companyName')}\n placeholder={label('fields.companyName', 'Company name')}\n value={current.companyName}\n onChange={(evt) => update('companyName', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.companyName ? 'true' : undefined}\n />\n\n {format === 'street_first' ? (\n <div className=\"grid gap-2 sm:grid-cols-[1.5fr,0.7fr,0.7fr]\">\n <Input\n className={inputClass('addressLine1')}\n placeholder={label('fields.street', 'Street')}\n value={current.addressLine1}\n onChange={(evt) => update('addressLine1', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.addressLine1 ? 'true' : undefined}\n />\n <Input\n className={inputClass('buildingNumber')}\n placeholder={label('fields.buildingNumber', 'Building number')}\n value={current.buildingNumber}\n onChange={(evt) => update('buildingNumber', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.buildingNumber ? 'true' : undefined}\n />\n <Input\n className={inputClass('flatNumber')}\n placeholder={label('fields.flatNumber', 'Flat number')}\n value={current.flatNumber}\n onChange={(evt) => update('flatNumber', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.flatNumber ? 'true' : undefined}\n />\n </div>\n ) : (\n <Input\n className={inputClass('addressLine1')}\n placeholder={label('fields.line1', 'Address line 1')}\n value={current.addressLine1}\n onChange={(evt) => update('addressLine1', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.addressLine1 ? 'true' : undefined}\n />\n )}\n\n <Input\n className={inputClass('addressLine2')}\n placeholder={label('fields.line2', 'Address line 2')}\n value={current.addressLine2}\n onChange={(evt) => update('addressLine2', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.addressLine2 ? 'true' : undefined}\n />\n\n {format !== 'street_first' ? (\n <div className=\"grid gap-2 sm:grid-cols-[1.5fr,0.7fr,0.7fr]\">\n <Input\n className={inputClass('buildingNumber')}\n placeholder={label('fields.buildingNumber', 'Building number')}\n value={current.buildingNumber}\n onChange={(evt) => update('buildingNumber', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.buildingNumber ? 'true' : undefined}\n />\n <Input\n className={inputClass('flatNumber')}\n placeholder={label('fields.flatNumber', 'Flat number')}\n value={current.flatNumber}\n onChange={(evt) => update('flatNumber', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.flatNumber ? 'true' : undefined}\n />\n </div>\n ) : null}\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <Input\n className={inputClass('city')}\n placeholder={label('fields.city', 'City')}\n value={current.city}\n onChange={(evt) => update('city', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.city ? 'true' : undefined}\n />\n <Input\n className={inputClass('region')}\n placeholder={label('fields.region', 'Region')}\n value={current.region}\n onChange={(evt) => update('region', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.region ? 'true' : undefined}\n />\n </div>\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <Input\n className={inputClass('postalCode')}\n placeholder={label('fields.postalCode', 'Postal code')}\n value={current.postalCode}\n onChange={(evt) => update('postalCode', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.postalCode ? 'true' : undefined}\n />\n <Dialog open={countryDialogOpen} onOpenChange={setCountryDialogOpen}>\n <DialogTrigger asChild>\n <Button type=\"button\" variant=\"ghost\" className={cn(inputClass('country'), 'cursor-pointer')} disabled={disabled}>\n {selectedCountry?.label ?? label('fields.country', 'Country')}\n </Button>\n </DialogTrigger>\n <DialogContent className=\"sm:max-w-lg\">\n <DialogHeader>\n <DialogTitle>{label('country.title', 'Choose a country')}</DialogTitle>\n <DialogDescription>{label('country.subtitle', 'Search for a country')}</DialogDescription>\n </DialogHeader>\n <div className=\"space-y-3\">\n <Input\n placeholder={label('country.search', 'Search countries')}\n value={countryQuery}\n onChange={(evt) => setCountryQuery(evt.target.value)}\n />\n <div className=\"max-h-64 overflow-auto rounded-md border border-border/60\">\n <ul className=\"divide-y divide-border/50\">\n {filteredCountryOptions.map((option) => (\n <li key={option.code}>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"w-full justify-between font-normal rounded-none\"\n onClick={() => {\n update('country', option.code)\n setCountryDialogOpen(false)\n }}\n >\n <span>{option.label}</span>\n <span className=\"text-xs text-muted-foreground\">{option.code}</span>\n </Button>\n </li>\n ))}\n </ul>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n </div>\n\n {showFormatHint ? (\n <p className=\"text-xs text-muted-foreground\">\n {label('formatHint', 'Format based on address settings')}\n </p>\n ) : null}\n\n {!hidePrimaryToggle ? (\n <label className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <input\n type=\"checkbox\"\n checked={current.isPrimary}\n onChange={(evt) => update('isPrimary', evt.target.checked)}\n disabled={disabled}\n />\n {label('fields.primary', 'Primary address')}\n </label>\n ) : null}\n </div>\n )\n}\n\nexport default AddressEditor\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { usePathname, useSearchParams } from 'next/navigation'\nimport { Plus, Settings } from 'lucide-react'\nimport { Button } from '../../primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from '@open-mercato/ui/primitives/dialog'\nimport { buildCountryOptions } from '@open-mercato/shared/lib/location/countries'\nimport { buildHrefWithReturnTo } from '@open-mercato/shared/lib/navigation/returnTo'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport type { AddressFormatStrategy } from './addressFormat'\n\ntype Translator = (key: string, fallback?: string, params?: Record<string, string | number>) => string\n\nexport type AddressTypeOption = {\n value: string\n label: string\n}\n\nexport type AddressTypesAdapter<C = unknown> = {\n list: (context?: C) => Promise<AddressTypeOption[]>\n create?: (value: string, context?: C) => Promise<AddressTypeOption | null>\n manageHref?: string\n}\n\nexport type AddressEditorDraft = {\n name: string\n purpose: string\n companyName: string\n addressLine1: string\n addressLine2: string\n buildingNumber: string\n flatNumber: string\n city: string\n region: string\n postalCode: string\n country: string\n isPrimary: boolean\n}\n\nexport type AddressEditorField =\n | 'name'\n | 'purpose'\n | 'companyName'\n | 'addressLine1'\n | 'addressLine2'\n | 'buildingNumber'\n | 'flatNumber'\n | 'city'\n | 'region'\n | 'postalCode'\n | 'country'\n | 'isPrimary'\n\ntype AddressEditorProps<C = unknown> = {\n value: AddressEditorDraft\n onChange: (next: AddressEditorDraft) => void\n format: AddressFormatStrategy\n t: Translator\n labelPrefix?: string\n disabled?: boolean\n errors?: Partial<Record<AddressEditorField, string>>\n hidePrimaryToggle?: boolean\n showFormatHint?: boolean\n addressTypesAdapter?: AddressTypesAdapter<C>\n addressTypesContext?: C\n}\n\nexport function AddressEditor<C = unknown>({\n value,\n onChange,\n format,\n t,\n labelPrefix = 'customers.people.detail.addresses',\n disabled = false,\n errors = {},\n hidePrimaryToggle = false,\n showFormatHint = true,\n addressTypesAdapter,\n addressTypesContext,\n}: AddressEditorProps<C>) {\n const pathname = usePathname()\n const searchParams = useSearchParams()\n const label = React.useCallback(\n (suffix: string, fallback?: string, params?: Record<string, string | number>) =>\n t(`${labelPrefix}.${suffix}`, fallback, params),\n [labelPrefix, t],\n )\n\n const [addressTypes, setAddressTypes] = React.useState<AddressTypeOption[]>([])\n const [addressTypesLoading, setAddressTypesLoading] = React.useState(false)\n const [addressTypeError, setAddressTypeError] = React.useState<string | null>(null)\n\n const [typeDialogOpen, setTypeDialogOpen] = React.useState(false)\n const [typeValue, setTypeValue] = React.useState('')\n const [typeFormError, setTypeFormError] = React.useState<string | null>(null)\n const [countryDialogOpen, setCountryDialogOpen] = React.useState(false)\n const [countryQuery, setCountryQuery] = React.useState('')\n\n const countryOptions = React.useMemo(\n () =>\n buildCountryOptions({\n transformLabel: (code, fallback) => t(`customers.countries.${code.toLowerCase()}`, fallback ?? code),\n }),\n [t],\n )\n\n React.useEffect(() => {\n let cancelled = false\n const load = async () => {\n if (!addressTypesAdapter) {\n setAddressTypes([])\n setAddressTypeError(null)\n return\n }\n setAddressTypesLoading(true)\n try {\n const result = await addressTypesAdapter.list(addressTypesContext)\n if (!cancelled) {\n setAddressTypes(Array.isArray(result) ? result : [])\n setAddressTypeError(null)\n }\n } catch (err) {\n if (!cancelled) {\n setAddressTypes([])\n setAddressTypeError(label('types.loadError', 'Failed to load address types'))\n }\n } finally {\n if (!cancelled) setAddressTypesLoading(false)\n }\n }\n load().catch(() => {})\n return () => {\n cancelled = true\n }\n }, [addressTypesAdapter, addressTypesContext, label])\n\n const current: AddressEditorDraft = {\n name: value.name ?? '',\n purpose: value.purpose ?? '',\n companyName: value.companyName ?? '',\n addressLine1: value.addressLine1 ?? '',\n addressLine2: value.addressLine2 ?? '',\n buildingNumber: value.buildingNumber ?? '',\n flatNumber: value.flatNumber ?? '',\n city: value.city ?? '',\n region: value.region ?? '',\n postalCode: value.postalCode ?? '',\n country: value.country ?? '',\n isPrimary: value.isPrimary ?? false,\n }\n\n const update = React.useCallback(\n (key: keyof AddressEditorDraft, nextValue: string | boolean) => {\n onChange({ ...current, [key]: nextValue })\n },\n [current, onChange],\n )\n\n const filteredCountryOptions = React.useMemo(() => {\n const query = countryQuery.trim().toLowerCase()\n if (!query.length) return countryOptions\n return countryOptions.filter(\n (option) => option.label.toLowerCase().includes(query) || option.code.toLowerCase().includes(query),\n )\n }, [countryOptions, countryQuery])\n\n const selectedCountry = React.useMemo(() => {\n const code = (current.country ?? '').toUpperCase()\n if (!code.length) return null\n return countryOptions.find((option) => option.code === code) ?? null\n }, [countryOptions, current.country])\n const returnTo = React.useMemo(() => {\n const query = searchParams?.toString() ?? ''\n if (!pathname) return null\n return query.length ? `${pathname}?${query}` : pathname\n }, [pathname, searchParams])\n const manageAddressTypesHref = React.useMemo(\n () => buildHrefWithReturnTo(addressTypesAdapter?.manageHref ?? '/backend/config/dictionaries', returnTo),\n [addressTypesAdapter?.manageHref, returnTo],\n )\n\n const handleTypeSubmit = React.useCallback(\n async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault()\n const trimmed = typeValue.trim()\n if (!trimmed.length) {\n setTypeFormError(label('types.emptyError', 'Please provide a value'))\n return\n }\n if (!addressTypesAdapter?.create) return\n setTypeFormError(null)\n const created = await addressTypesAdapter.create(trimmed, addressTypesContext)\n if (created) {\n setAddressTypes((prev) => {\n const map = new Map(prev.map((entry) => [entry.value, entry]))\n map.set(created.value, created)\n return Array.from(map.values()).sort((a, b) => a.label.localeCompare(b.label))\n })\n }\n setTypeDialogOpen(false)\n setTypeValue('')\n },\n [addressTypesAdapter, addressTypesContext, label, typeValue],\n )\n\n const inputClass = (field: AddressEditorField) =>\n [\n 'w-full rounded-md border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n errors[field] ? 'border-red-500 aria-invalid:ring-destructive' : 'border-input bg-background',\n ].join(' ')\n\n return (\n <div className=\"space-y-3\">\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <Input\n className={inputClass('name')}\n placeholder={label('fields.label', 'Label')}\n value={current.name}\n onChange={(evt) => update('name', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.name ? 'true' : undefined}\n />\n <div className=\"flex gap-2\">\n <select\n className={inputClass('purpose')}\n value={current.purpose}\n onChange={(evt) => update('purpose', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.purpose ? 'true' : undefined}\n >\n <option value=\"\">\n {addressTypesLoading\n ? label('types.loading', 'Loading\u2026')\n : label('types.placeholder', 'Address type')}\n </option>\n {addressTypes.map((entry) => (\n <option key={entry.value} value={entry.value}>\n {entry.label}\n </option>\n ))}\n </select>\n {addressTypesAdapter?.create ? (\n <Dialog open={typeDialogOpen} onOpenChange={setTypeDialogOpen}>\n <DialogTrigger asChild>\n <Button type=\"button\" variant=\"outline\" size=\"icon\" className=\"shrink-0\" disabled={disabled}>\n <Plus className=\"h-4 w-4\" />\n </Button>\n </DialogTrigger>\n <DialogContent className=\"sm:max-w-md\">\n <DialogHeader>\n <DialogTitle>{label('types.add', 'Add address type')}</DialogTitle>\n <DialogDescription>\n {label('types.addHint', 'Create a new address type for reuse.')}\n </DialogDescription>\n </DialogHeader>\n <form className=\"space-y-3\" onSubmit={handleTypeSubmit}>\n <Input\n autoFocus\n value={typeValue}\n onChange={(evt) => {\n setTypeValue(evt.target.value)\n if (typeFormError) setTypeFormError(null)\n }}\n placeholder={label('types.placeholder', 'Address type')}\n disabled={disabled}\n aria-invalid={typeFormError ? 'true' : undefined}\n />\n {typeFormError ? <p className=\"text-sm text-destructive\">{typeFormError}</p> : null}\n <DialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setTypeDialogOpen(false)} disabled={disabled}>\n {label('types.cancel', 'Cancel')}\n </Button>\n <Button type=\"submit\" disabled={disabled || !typeValue.trim()}>\n {label('types.save', 'Save')}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n ) : null}\n <Button\n asChild\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"shrink-0\"\n disabled={disabled}\n title={label('types.manage', 'Manage address types')}\n >\n <Link\n href={manageAddressTypesHref}\n aria-label={label('types.manage', 'Manage address types')}\n >\n <Settings className=\"h-4 w-4\" />\n </Link>\n </Button>\n </div>\n </div>\n {errors.purpose ? <p className=\"text-xs text-destructive\">{errors.purpose}</p> : null}\n {addressTypeError ? <p className=\"text-xs text-destructive\">{addressTypeError}</p> : null}\n <Input\n className={inputClass('companyName')}\n placeholder={label('fields.companyName', 'Company name')}\n value={current.companyName}\n onChange={(evt) => update('companyName', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.companyName ? 'true' : undefined}\n />\n\n {format === 'street_first' ? (\n <div className=\"grid gap-2 sm:grid-cols-[1.5fr,0.7fr,0.7fr]\">\n <Input\n className={inputClass('addressLine1')}\n placeholder={label('fields.street', 'Street')}\n value={current.addressLine1}\n onChange={(evt) => update('addressLine1', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.addressLine1 ? 'true' : undefined}\n />\n <Input\n className={inputClass('buildingNumber')}\n placeholder={label('fields.buildingNumber', 'Building number')}\n value={current.buildingNumber}\n onChange={(evt) => update('buildingNumber', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.buildingNumber ? 'true' : undefined}\n />\n <Input\n className={inputClass('flatNumber')}\n placeholder={label('fields.flatNumber', 'Flat number')}\n value={current.flatNumber}\n onChange={(evt) => update('flatNumber', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.flatNumber ? 'true' : undefined}\n />\n </div>\n ) : (\n <Input\n className={inputClass('addressLine1')}\n placeholder={label('fields.line1', 'Address line 1')}\n value={current.addressLine1}\n onChange={(evt) => update('addressLine1', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.addressLine1 ? 'true' : undefined}\n />\n )}\n\n <Input\n className={inputClass('addressLine2')}\n placeholder={label('fields.line2', 'Address line 2')}\n value={current.addressLine2}\n onChange={(evt) => update('addressLine2', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.addressLine2 ? 'true' : undefined}\n />\n\n {format !== 'street_first' ? (\n <div className=\"grid gap-2 sm:grid-cols-[1.5fr,0.7fr,0.7fr]\">\n <Input\n className={inputClass('buildingNumber')}\n placeholder={label('fields.buildingNumber', 'Building number')}\n value={current.buildingNumber}\n onChange={(evt) => update('buildingNumber', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.buildingNumber ? 'true' : undefined}\n />\n <Input\n className={inputClass('flatNumber')}\n placeholder={label('fields.flatNumber', 'Flat number')}\n value={current.flatNumber}\n onChange={(evt) => update('flatNumber', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.flatNumber ? 'true' : undefined}\n />\n </div>\n ) : null}\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <Input\n className={inputClass('city')}\n placeholder={label('fields.city', 'City')}\n value={current.city}\n onChange={(evt) => update('city', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.city ? 'true' : undefined}\n />\n <Input\n className={inputClass('region')}\n placeholder={label('fields.region', 'Region')}\n value={current.region}\n onChange={(evt) => update('region', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.region ? 'true' : undefined}\n />\n </div>\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <Input\n className={inputClass('postalCode')}\n placeholder={label('fields.postalCode', 'Postal code')}\n value={current.postalCode}\n onChange={(evt) => update('postalCode', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.postalCode ? 'true' : undefined}\n />\n <Dialog open={countryDialogOpen} onOpenChange={setCountryDialogOpen}>\n <DialogTrigger asChild>\n <Button type=\"button\" variant=\"ghost\" className={cn(inputClass('country'), 'cursor-pointer')} disabled={disabled}>\n {selectedCountry?.label ?? label('fields.country', 'Country')}\n </Button>\n </DialogTrigger>\n <DialogContent className=\"sm:max-w-lg\">\n <DialogHeader>\n <DialogTitle>{label('country.title', 'Choose a country')}</DialogTitle>\n <DialogDescription>{label('country.subtitle', 'Search for a country')}</DialogDescription>\n </DialogHeader>\n <div className=\"space-y-3\">\n <Input\n placeholder={label('country.search', 'Search countries')}\n value={countryQuery}\n onChange={(evt) => setCountryQuery(evt.target.value)}\n />\n <div className=\"max-h-64 overflow-auto rounded-md border border-border/70\">\n <ul className=\"divide-y divide-border/50\">\n {filteredCountryOptions.map((option) => (\n <li key={option.code}>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"w-full justify-between font-normal rounded-none\"\n onClick={() => {\n update('country', option.code)\n setCountryDialogOpen(false)\n }}\n >\n <span>{option.label}</span>\n <span className=\"text-xs text-muted-foreground\">{option.code}</span>\n </Button>\n </li>\n ))}\n </ul>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n </div>\n\n {showFormatHint ? (\n <p className=\"text-xs text-muted-foreground\">\n {label('formatHint', 'Format based on address settings')}\n </p>\n ) : null}\n\n {!hidePrimaryToggle ? (\n <label className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <input\n type=\"checkbox\"\n checked={current.isPrimary}\n onChange={(evt) => update('isPrimary', evt.target.checked)}\n disabled={disabled}\n />\n {label('fields.primary', 'Primary address')}\n </label>\n ) : null}\n </div>\n )\n}\n\nexport default AddressEditor\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -282,7 +282,7 @@ function AddressTiles({
282
282
  (key) => /* @__PURE__ */ jsxs(
283
283
  "div",
284
284
  {
285
- className: "rounded-lg border-2 border-dashed border-muted-foreground/50 bg-muted/20 p-4 text-sm",
285
+ className: "rounded-lg border-2 border-dashed border-muted-foreground/50 bg-muted/30 p-4 text-sm",
286
286
  onKeyDown: (event) => {
287
287
  if (!(event.metaKey || event.ctrlKey)) return;
288
288
  if (event.key !== "Enter") return;
@@ -379,12 +379,12 @@ function AddressTiles({
379
379
  return /* @__PURE__ */ jsx(
380
380
  "div",
381
381
  {
382
- className: "group rounded-lg border border-border/60 bg-card p-4 text-sm transition hover:border-border",
382
+ className: "group rounded-lg border border-border/70 bg-card p-4 text-sm transition hover:border-border",
383
383
  children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
384
384
  /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
385
385
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
386
386
  /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-foreground", children: address.name ?? label("labelFallback", "Address") }),
387
- address.isPrimary ? /* @__PURE__ */ jsx("span", { className: "rounded-full border border-border bg-muted px-2 py-0.5 text-[10px] font-semibold uppercase", children: label("primaryBadge", "Primary") }) : null
387
+ address.isPrimary ? /* @__PURE__ */ jsx("span", { className: "rounded-full border border-border bg-muted px-2 py-0.5 text-overline font-semibold uppercase", children: label("primaryBadge", "Primary") }) : null
388
388
  ] }),
389
389
  address.purpose ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: address.purpose }) : null,
390
390
  /* @__PURE__ */ jsx(AddressView, { address, format, className: "text-sm text-foreground" }),