@open-mercato/core 0.5.1-develop.2800.bfe2178a4f → 0.5.1-develop.2851.2854b4507f

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 (91) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/generated/entities/action_log/index.js +4 -0
  3. package/dist/generated/entities/action_log/index.js.map +2 -2
  4. package/dist/generated/entity-fields-registry.js +2 -0
  5. package/dist/generated/entity-fields-registry.js.map +2 -2
  6. package/dist/modules/audit_logs/data/entities.js +10 -1
  7. package/dist/modules/audit_logs/data/entities.js.map +2 -2
  8. package/dist/modules/audit_logs/data/validators.js +2 -0
  9. package/dist/modules/audit_logs/data/validators.js.map +2 -2
  10. package/dist/modules/audit_logs/migrations/Migration20260423202109.js +15 -0
  11. package/dist/modules/audit_logs/migrations/Migration20260423202109.js.map +7 -0
  12. package/dist/modules/audit_logs/services/accessLogService.js +3 -2
  13. package/dist/modules/audit_logs/services/accessLogService.js.map +3 -3
  14. package/dist/modules/audit_logs/services/actionLogService.js +13 -2
  15. package/dist/modules/audit_logs/services/actionLogService.js.map +3 -3
  16. package/dist/modules/auth/cli.js.map +2 -2
  17. package/dist/modules/customers/api/entity-roles-factory.js +3 -18
  18. package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
  19. package/dist/modules/customers/api/interactions/cancel/route.js +7 -2
  20. package/dist/modules/customers/api/interactions/cancel/route.js.map +2 -2
  21. package/dist/modules/customers/api/interactions/complete/route.js +7 -2
  22. package/dist/modules/customers/api/interactions/complete/route.js.map +2 -2
  23. package/dist/modules/customers/backend/customers/deals/page.js +45 -44
  24. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  25. package/dist/modules/customers/commands/comments.js +6 -0
  26. package/dist/modules/customers/commands/comments.js.map +2 -2
  27. package/dist/modules/customers/components/detail/AssignRoleDialog.js +41 -13
  28. package/dist/modules/customers/components/detail/AssignRoleDialog.js.map +2 -2
  29. package/dist/modules/customers/components/detail/CompanyDetailHeader.js +30 -0
  30. package/dist/modules/customers/components/detail/CompanyDetailHeader.js.map +2 -2
  31. package/dist/modules/customers/components/detail/DealDetailHeader.js +32 -0
  32. package/dist/modules/customers/components/detail/DealDetailHeader.js.map +2 -2
  33. package/dist/modules/customers/components/detail/DealWonPopup.js +2 -2
  34. package/dist/modules/customers/components/detail/DealWonPopup.js.map +2 -2
  35. package/dist/modules/customers/components/detail/InlineActivityComposer.js +62 -6
  36. package/dist/modules/customers/components/detail/InlineActivityComposer.js.map +2 -2
  37. package/dist/modules/customers/components/detail/ObjectHistoryButton.js +39 -0
  38. package/dist/modules/customers/components/detail/ObjectHistoryButton.js.map +7 -0
  39. package/dist/modules/customers/components/detail/PersonDetailHeader.js +30 -0
  40. package/dist/modules/customers/components/detail/PersonDetailHeader.js.map +2 -2
  41. package/dist/modules/customers/components/detail/RolesSection.js +14 -4
  42. package/dist/modules/customers/components/detail/RolesSection.js.map +3 -3
  43. package/dist/modules/customers/components/formConfig.js +16 -2
  44. package/dist/modules/customers/components/formConfig.js.map +2 -2
  45. package/dist/modules/customers/lib/displayName.js +15 -0
  46. package/dist/modules/customers/lib/displayName.js.map +7 -0
  47. package/dist/modules/customers/lib/interactionReadModel.js +1 -2
  48. package/dist/modules/customers/lib/interactionReadModel.js.map +2 -2
  49. package/dist/modules/customers/lib/operationMetadata.js +21 -0
  50. package/dist/modules/customers/lib/operationMetadata.js.map +7 -0
  51. package/dist/modules/messages/components/MessagesInboxPageClient.js +106 -107
  52. package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
  53. package/dist/modules/messages/components/useMessagesInboxBulkActions.js +235 -0
  54. package/dist/modules/messages/components/useMessagesInboxBulkActions.js.map +7 -0
  55. package/generated/entities/action_log/index.ts +2 -0
  56. package/generated/entity-fields-registry.ts +2 -0
  57. package/package.json +3 -3
  58. package/src/modules/audit_logs/data/entities.ts +7 -0
  59. package/src/modules/audit_logs/data/validators.ts +2 -0
  60. package/src/modules/audit_logs/migrations/.snapshot-open-mercato.json +51 -5
  61. package/src/modules/audit_logs/migrations/Migration20260423202109.ts +15 -0
  62. package/src/modules/audit_logs/services/accessLogService.ts +1 -3
  63. package/src/modules/audit_logs/services/actionLogService.ts +11 -6
  64. package/src/modules/auth/cli.ts +1 -1
  65. package/src/modules/customers/api/entity-roles-factory.ts +3 -23
  66. package/src/modules/customers/api/interactions/cancel/route.ts +7 -2
  67. package/src/modules/customers/api/interactions/complete/route.ts +7 -2
  68. package/src/modules/customers/backend/customers/deals/page.tsx +48 -44
  69. package/src/modules/customers/commands/comments.ts +6 -0
  70. package/src/modules/customers/components/detail/AssignRoleDialog.tsx +37 -9
  71. package/src/modules/customers/components/detail/CompanyDetailHeader.tsx +25 -0
  72. package/src/modules/customers/components/detail/DealDetailHeader.tsx +29 -0
  73. package/src/modules/customers/components/detail/DealWonPopup.tsx +2 -2
  74. package/src/modules/customers/components/detail/InlineActivityComposer.tsx +65 -6
  75. package/src/modules/customers/components/detail/ObjectHistoryButton.tsx +47 -0
  76. package/src/modules/customers/components/detail/PersonDetailHeader.tsx +25 -0
  77. package/src/modules/customers/components/detail/RolesSection.tsx +20 -1
  78. package/src/modules/customers/components/formConfig.tsx +14 -2
  79. package/src/modules/customers/i18n/de.json +12 -0
  80. package/src/modules/customers/i18n/en.json +12 -0
  81. package/src/modules/customers/i18n/es.json +13 -1
  82. package/src/modules/customers/i18n/pl.json +13 -1
  83. package/src/modules/customers/lib/displayName.ts +16 -0
  84. package/src/modules/customers/lib/interactionReadModel.ts +1 -7
  85. package/src/modules/customers/lib/operationMetadata.ts +38 -0
  86. package/src/modules/messages/components/MessagesInboxPageClient.tsx +17 -29
  87. package/src/modules/messages/components/useMessagesInboxBulkActions.ts +324 -0
  88. package/src/modules/messages/i18n/de.json +8 -0
  89. package/src/modules/messages/i18n/en.json +8 -0
  90. package/src/modules/messages/i18n/es.json +8 -0
  91. package/src/modules/messages/i18n/pl.json +8 -0
@@ -6,7 +6,9 @@ import { cn } from "@open-mercato/shared/lib/utils";
6
6
  import { useT } from "@open-mercato/shared/lib/i18n/context";
7
7
  import { Button } from "@open-mercato/ui/primitives/button";
8
8
  import { IconButton } from "@open-mercato/ui/primitives/icon-button";
9
+ import { SendObjectMessageDialog } from "@open-mercato/ui/backend/messages";
9
10
  import { Popover, PopoverContent, PopoverTrigger } from "@open-mercato/ui/primitives/popover";
11
+ import { ObjectHistoryButton } from "./ObjectHistoryButton.js";
10
12
  import { useCustomerDictionary } from "./hooks/useCustomerDictionary.js";
11
13
  import { formatFallbackLabel } from "./utils.js";
12
14
  import { isTerminalPipelineOutcomeLabel } from "./pipelineStageUtils.js";
@@ -34,6 +36,7 @@ const headerChipDotVariantClasses = {
34
36
  error: "bg-status-error-icon",
35
37
  neutral: "bg-status-neutral-icon"
36
38
  };
39
+ const HEADER_ICON_BUTTON_CLASS = "size-8 rounded-md";
37
40
  function HeaderChip({
38
41
  children,
39
42
  icon,
@@ -160,6 +163,7 @@ function DealDetailHeader({
160
163
  const showStatusChip = statusLabel && (!deal.closureOutcome || !isTerminalPipelineOutcomeLabel(deal.status));
161
164
  const pipelineBadgeLabel = pipelineName ?? null;
162
165
  const canMoveStage = stageOptions.length > 0 && !deal.closureOutcome && typeof onStageChange === "function";
166
+ const messageSubtitle = React.useMemo(() => [companyLabel, amountLabel].filter(Boolean).join(" \xB7 ") || statusLabel || void 0, [amountLabel, companyLabel, statusLabel]);
163
167
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-5 xl:flex-row xl:items-start xl:justify-between", children: [
164
168
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 gap-5", children: [
165
169
  /* @__PURE__ */ jsx("div", { className: "flex size-16 shrink-0 items-center justify-center rounded-lg border border-border bg-muted text-muted-foreground", children: /* @__PURE__ */ jsx(FileText, { className: "size-7" }) }),
@@ -200,6 +204,34 @@ function DealDetailHeader({
200
204
  isSaving: isStageSaving
201
205
  }
202
206
  ) : null,
207
+ /* @__PURE__ */ jsx(
208
+ SendObjectMessageDialog,
209
+ {
210
+ object: {
211
+ entityModule: "customers",
212
+ entityType: "deal",
213
+ entityId: deal.id,
214
+ previewData: {
215
+ title: deal.title || t("customers.deals.detail.untitled", "Untitled deal"),
216
+ subtitle: messageSubtitle
217
+ }
218
+ },
219
+ viewHref: `/backend/customers/deals/${deal.id}`,
220
+ buttonVariant: "outline",
221
+ buttonSize: "icon",
222
+ buttonClassName: HEADER_ICON_BUTTON_CLASS,
223
+ buttonLabel: t("customers.deals.detail.actions.sendMessage", "Send message")
224
+ }
225
+ ),
226
+ /* @__PURE__ */ jsx(
227
+ ObjectHistoryButton,
228
+ {
229
+ resourceKind: "customers.deal",
230
+ resourceId: deal.id,
231
+ organizationId: deal.organizationId ?? void 0,
232
+ includeRelated: true
233
+ }
234
+ ),
203
235
  /* @__PURE__ */ jsx(
204
236
  IconButton,
205
237
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/components/detail/DealDetailHeader.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Building2, CalendarClock, Check, ChevronDown, FileText, Save, Trash2, Users, Workflow } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'\nimport { useCustomerDictionary } from './hooks/useCustomerDictionary'\nimport { formatFallbackLabel } from './utils'\nimport { isTerminalPipelineOutcomeLabel } from './pipelineStageUtils'\n\ntype DealAssociation = {\n id: string\n label: string\n subtitle: string | null\n kind: 'person' | 'company'\n}\n\ntype DealDetailHeaderProps = {\n deal: {\n id: string\n title: string\n status: string | null\n pipelineStage: string | null\n valueAmount: string | null\n valueCurrency: string | null\n expectedCloseAt: string | null\n createdAt: string\n closureOutcome: 'won' | 'lost' | null\n organizationId: string | null\n }\n owner?: { id: string; name: string; email: string } | null\n people: DealAssociation[]\n companies: DealAssociation[]\n pipelineName?: string | null\n stageOptions?: Array<{ id: string; label: string; order: number }>\n currentStageId?: string | null\n onStageChange?: (stageId: string) => Promise<void> | void\n isStageSaving?: boolean\n onSave: () => void\n onDelete: () => Promise<void> | void\n isDirty: boolean\n isSaving: boolean\n}\n\nfunction formatCurrency(amount: string | null, currency: string | null): string | null {\n if (!amount) return null\n const parsed = Number(amount)\n if (!Number.isFinite(parsed)) return currency ? `${amount} ${currency}` : amount\n if (!currency) return parsed.toLocaleString()\n try {\n return new Intl.NumberFormat(undefined, { style: 'currency', currency }).format(parsed)\n } catch {\n return `${parsed.toLocaleString()} ${currency}`\n }\n}\n\nfunction formatDate(value: string | null): string | null {\n if (!value) return null\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return null\n return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })\n}\n\ntype HeaderChipVariant = 'info' | 'warning' | 'success' | 'error' | 'neutral'\n\nconst headerChipDotVariantClasses: Record<HeaderChipVariant, string> = {\n info: 'bg-status-info-icon',\n warning: 'bg-status-warning-icon',\n success: 'bg-status-success-icon',\n error: 'bg-status-error-icon',\n neutral: 'bg-status-neutral-icon',\n}\n\nfunction HeaderChip({\n children,\n icon,\n variant,\n}: {\n children: React.ReactNode\n icon?: React.ReactNode\n variant?: HeaderChipVariant\n}) {\n return (\n <span className=\"inline-flex items-center gap-1.5 rounded-sm bg-muted px-2 py-1 text-xs font-medium leading-none text-muted-foreground\">\n {variant ? <span className={cn('size-2 rounded-full', headerChipDotVariantClasses[variant])} /> : null}\n {icon ? <span className=\"text-muted-foreground\">{icon}</span> : null}\n {children}\n </span>\n )\n}\n\nfunction StageActionButton({\n stageOptions,\n currentStageId,\n onStageChange,\n disabled,\n isSaving,\n}: {\n stageOptions: Array<{ id: string; label: string; order: number }>\n currentStageId: string | null\n onStageChange: (stageId: string) => Promise<void> | void\n disabled: boolean\n isSaving: boolean\n}) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n const sortedStages = React.useMemo(\n () =>\n [...stageOptions]\n .filter((stage) => !isTerminalPipelineOutcomeLabel(stage.label))\n .sort((left, right) => left.order - right.order),\n [stageOptions],\n )\n const currentStage = React.useMemo(\n () => sortedStages.find((stage) => stage.id === currentStageId) ?? null,\n [currentStageId, sortedStages],\n )\n\n if (!sortedStages.length) return null\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={disabled || isSaving}\n className=\"h-8 max-w-[13rem] rounded-md px-3\"\n >\n <Workflow className=\"size-4\" />\n <span className=\"truncate\">\n {currentStage?.label ?? t('customers.deals.detail.actions.moveStage', 'Move stage')}\n </span>\n <ChevronDown className=\"size-4\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className=\"w-64 p-1\">\n <div className=\"border-b border-border px-3 py-2\">\n <div className=\"text-xs font-semibold text-foreground\">\n {t('customers.deals.detail.stageMenu.title', 'Update pipeline stage')}\n </div>\n <div className=\"mt-0.5 text-xs text-muted-foreground\">\n {t('customers.deals.detail.stageMenu.description', 'Move the deal forward without opening the form.')}\n </div>\n </div>\n <div className=\"py-1\">\n {sortedStages.map((stage) => {\n const isCurrent = stage.id === currentStageId\n return (\n <Button\n key={stage.id}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n disabled={disabled || isSaving || isCurrent}\n className=\"h-auto w-full justify-between rounded-md px-3 py-2 text-left\"\n onClick={() => {\n setOpen(false)\n if (!isCurrent) {\n void onStageChange(stage.id)\n }\n }}\n >\n <span className=\"min-w-0 truncate text-sm font-medium\">\n {stage.label}\n </span>\n {isCurrent ? <Check className=\"size-4 text-foreground\" /> : null}\n </Button>\n )\n })}\n </div>\n </PopoverContent>\n </Popover>\n )\n}\n\nexport function DealDetailHeader({\n deal,\n owner,\n people,\n companies,\n pipelineName,\n stageOptions = [],\n currentStageId = null,\n onStageChange,\n isStageSaving = false,\n onSave,\n onDelete,\n isDirty,\n isSaving,\n}: DealDetailHeaderProps) {\n const t = useT()\n const amountLabel = React.useMemo(\n () => formatCurrency(deal.valueAmount, deal.valueCurrency),\n [deal.valueAmount, deal.valueCurrency],\n )\n const createdAtLabel = React.useMemo(() => formatDate(deal.createdAt), [deal.createdAt])\n const expectedCloseLabel = React.useMemo(() => formatDate(deal.expectedCloseAt), [deal.expectedCloseAt])\n const { data: statusDictionary } = useCustomerDictionary('deal-statuses', 0, deal.organizationId ?? null)\n const statusEntry = deal.status ? statusDictionary?.map?.[deal.status] : null\n const companyLabel = React.useMemo(() => {\n const primaryCompany = companies[0]\n if (!primaryCompany) return null\n const extraCount = companies.length - 1\n return extraCount > 0 ? `${primaryCompany.label} +${extraCount}` : primaryCompany.label\n }, [companies])\n const timelineLabel = React.useMemo(() => {\n if (createdAtLabel && expectedCloseLabel) {\n return t('customers.deals.detail.header.timeline', 'Created {{created}} \u00B7 Expected close {{expected}}', {\n created: createdAtLabel,\n expected: expectedCloseLabel,\n })\n }\n if (createdAtLabel) {\n return t('customers.deals.detail.header.createdAt', 'Created {{date}}', { date: createdAtLabel })\n }\n if (expectedCloseLabel) {\n return t('customers.deals.detail.header.expectedClose', 'Expected close {{date}}', { date: expectedCloseLabel })\n }\n return null\n }, [createdAtLabel, expectedCloseLabel, t])\n const statusLabel = statusEntry?.label ?? (deal.status ? formatFallbackLabel(deal.status) : null)\n const showStatusChip = statusLabel && (!deal.closureOutcome || !isTerminalPipelineOutcomeLabel(deal.status))\n const pipelineBadgeLabel = pipelineName ?? null\n const canMoveStage = stageOptions.length > 0 && !deal.closureOutcome && typeof onStageChange === 'function'\n\n return (\n <div className=\"flex flex-col gap-5 xl:flex-row xl:items-start xl:justify-between\">\n <div className=\"flex min-w-0 gap-5\">\n <div className=\"flex size-16 shrink-0 items-center justify-center rounded-lg border border-border bg-muted text-muted-foreground\">\n <FileText className=\"size-7\" />\n </div>\n <div className=\"min-w-0 space-y-3\">\n <div className=\"flex flex-wrap items-baseline gap-2\">\n <h1 className=\"min-w-0 truncate text-2xl font-bold leading-tight text-foreground md:text-2xl\">\n {deal.title || t('customers.deals.detail.untitled', 'Untitled deal')}\n </h1>\n <span className=\"rounded-sm bg-muted px-2 py-1 text-overline font-semibold uppercase tracking-[0.04em] text-muted-foreground\">\n {t('customers.deals.detail.badge.deal', 'Deal')}\n </span>\n </div>\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-2 text-sm text-muted-foreground\">\n {companyLabel ? (\n <span className=\"inline-flex items-center gap-1.5\">\n <Building2 className=\"size-3.5\" />\n {companyLabel}\n </span>\n ) : null}\n {timelineLabel ? (\n <span className=\"inline-flex items-center gap-1.5\">\n <CalendarClock className=\"size-3.5\" />\n {timelineLabel}\n </span>\n ) : null}\n {amountLabel ? (\n <span className=\"font-semibold text-foreground\">{amountLabel}</span>\n ) : null}\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n {showStatusChip ? (\n <HeaderChip variant=\"info\">\n {statusLabel}\n </HeaderChip>\n ) : null}\n {deal.pipelineStage && !isTerminalPipelineOutcomeLabel(deal.pipelineStage) ? (\n <HeaderChip variant=\"warning\">\n {deal.pipelineStage}\n </HeaderChip>\n ) : null}\n {pipelineBadgeLabel ? (\n <HeaderChip icon={<Workflow className=\"size-3.5\" />}>\n {pipelineBadgeLabel}\n </HeaderChip>\n ) : null}\n {deal.closureOutcome ? (\n <HeaderChip variant={deal.closureOutcome === 'won' ? 'success' : 'error'}>\n {deal.closureOutcome === 'won'\n ? t('customers.deals.detail.badge.won', 'Won')\n : t('customers.deals.detail.badge.lost', 'Lost')}\n </HeaderChip>\n ) : null}\n {!companyLabel && people.length > 0 ? (\n <HeaderChip icon={<Users className=\"size-3.5\" />}>\n {people[0]?.label}\n </HeaderChip>\n ) : null}\n {owner && !pipelineBadgeLabel ? (\n <HeaderChip icon={<Workflow className=\"size-3.5\" />}>\n {owner.name}\n </HeaderChip>\n ) : null}\n </div>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2 xl:justify-end\">\n {canMoveStage ? (\n <StageActionButton\n stageOptions={stageOptions}\n currentStageId={currentStageId}\n onStageChange={onStageChange}\n disabled={isDirty || isSaving}\n isSaving={isStageSaving}\n />\n ) : null}\n <IconButton\n variant=\"outline\"\n size=\"sm\"\n type=\"button\"\n aria-label={t('customers.deals.detail.actions.delete', 'Delete')}\n className=\"h-8 w-8 rounded-md\"\n onClick={() => {\n void onDelete()\n }}\n >\n <Trash2 className=\"size-4\" />\n </IconButton>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={onSave}\n disabled={!isDirty || isSaving}\n className=\"h-8 rounded-md px-3\"\n >\n <Save className=\"size-4\" />\n {t('customers.deals.detail.actions.save', 'Save')}\n </Button>\n </div>\n </div>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Building2, CalendarClock, Check, ChevronDown, FileText, Save, Trash2, Users, Workflow } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\nimport { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'\nimport { ObjectHistoryButton } from './ObjectHistoryButton'\nimport { useCustomerDictionary } from './hooks/useCustomerDictionary'\nimport { formatFallbackLabel } from './utils'\nimport { isTerminalPipelineOutcomeLabel } from './pipelineStageUtils'\n\ntype DealAssociation = {\n id: string\n label: string\n subtitle: string | null\n kind: 'person' | 'company'\n}\n\ntype DealDetailHeaderProps = {\n deal: {\n id: string\n title: string\n status: string | null\n pipelineStage: string | null\n valueAmount: string | null\n valueCurrency: string | null\n expectedCloseAt: string | null\n createdAt: string\n closureOutcome: 'won' | 'lost' | null\n organizationId: string | null\n }\n owner?: { id: string; name: string; email: string } | null\n people: DealAssociation[]\n companies: DealAssociation[]\n pipelineName?: string | null\n stageOptions?: Array<{ id: string; label: string; order: number }>\n currentStageId?: string | null\n onStageChange?: (stageId: string) => Promise<void> | void\n isStageSaving?: boolean\n onSave: () => void\n onDelete: () => Promise<void> | void\n isDirty: boolean\n isSaving: boolean\n}\n\nfunction formatCurrency(amount: string | null, currency: string | null): string | null {\n if (!amount) return null\n const parsed = Number(amount)\n if (!Number.isFinite(parsed)) return currency ? `${amount} ${currency}` : amount\n if (!currency) return parsed.toLocaleString()\n try {\n return new Intl.NumberFormat(undefined, { style: 'currency', currency }).format(parsed)\n } catch {\n return `${parsed.toLocaleString()} ${currency}`\n }\n}\n\nfunction formatDate(value: string | null): string | null {\n if (!value) return null\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return null\n return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })\n}\n\ntype HeaderChipVariant = 'info' | 'warning' | 'success' | 'error' | 'neutral'\n\nconst headerChipDotVariantClasses: Record<HeaderChipVariant, string> = {\n info: 'bg-status-info-icon',\n warning: 'bg-status-warning-icon',\n success: 'bg-status-success-icon',\n error: 'bg-status-error-icon',\n neutral: 'bg-status-neutral-icon',\n}\n\nconst HEADER_ICON_BUTTON_CLASS = 'size-8 rounded-md'\n\nfunction HeaderChip({\n children,\n icon,\n variant,\n}: {\n children: React.ReactNode\n icon?: React.ReactNode\n variant?: HeaderChipVariant\n}) {\n return (\n <span className=\"inline-flex items-center gap-1.5 rounded-sm bg-muted px-2 py-1 text-xs font-medium leading-none text-muted-foreground\">\n {variant ? <span className={cn('size-2 rounded-full', headerChipDotVariantClasses[variant])} /> : null}\n {icon ? <span className=\"text-muted-foreground\">{icon}</span> : null}\n {children}\n </span>\n )\n}\n\nfunction StageActionButton({\n stageOptions,\n currentStageId,\n onStageChange,\n disabled,\n isSaving,\n}: {\n stageOptions: Array<{ id: string; label: string; order: number }>\n currentStageId: string | null\n onStageChange: (stageId: string) => Promise<void> | void\n disabled: boolean\n isSaving: boolean\n}) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n const sortedStages = React.useMemo(\n () =>\n [...stageOptions]\n .filter((stage) => !isTerminalPipelineOutcomeLabel(stage.label))\n .sort((left, right) => left.order - right.order),\n [stageOptions],\n )\n const currentStage = React.useMemo(\n () => sortedStages.find((stage) => stage.id === currentStageId) ?? null,\n [currentStageId, sortedStages],\n )\n\n if (!sortedStages.length) return null\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={disabled || isSaving}\n className=\"h-8 max-w-[13rem] rounded-md px-3\"\n >\n <Workflow className=\"size-4\" />\n <span className=\"truncate\">\n {currentStage?.label ?? t('customers.deals.detail.actions.moveStage', 'Move stage')}\n </span>\n <ChevronDown className=\"size-4\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className=\"w-64 p-1\">\n <div className=\"border-b border-border px-3 py-2\">\n <div className=\"text-xs font-semibold text-foreground\">\n {t('customers.deals.detail.stageMenu.title', 'Update pipeline stage')}\n </div>\n <div className=\"mt-0.5 text-xs text-muted-foreground\">\n {t('customers.deals.detail.stageMenu.description', 'Move the deal forward without opening the form.')}\n </div>\n </div>\n <div className=\"py-1\">\n {sortedStages.map((stage) => {\n const isCurrent = stage.id === currentStageId\n return (\n <Button\n key={stage.id}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n disabled={disabled || isSaving || isCurrent}\n className=\"h-auto w-full justify-between rounded-md px-3 py-2 text-left\"\n onClick={() => {\n setOpen(false)\n if (!isCurrent) {\n void onStageChange(stage.id)\n }\n }}\n >\n <span className=\"min-w-0 truncate text-sm font-medium\">\n {stage.label}\n </span>\n {isCurrent ? <Check className=\"size-4 text-foreground\" /> : null}\n </Button>\n )\n })}\n </div>\n </PopoverContent>\n </Popover>\n )\n}\n\nexport function DealDetailHeader({\n deal,\n owner,\n people,\n companies,\n pipelineName,\n stageOptions = [],\n currentStageId = null,\n onStageChange,\n isStageSaving = false,\n onSave,\n onDelete,\n isDirty,\n isSaving,\n}: DealDetailHeaderProps) {\n const t = useT()\n const amountLabel = React.useMemo(\n () => formatCurrency(deal.valueAmount, deal.valueCurrency),\n [deal.valueAmount, deal.valueCurrency],\n )\n const createdAtLabel = React.useMemo(() => formatDate(deal.createdAt), [deal.createdAt])\n const expectedCloseLabel = React.useMemo(() => formatDate(deal.expectedCloseAt), [deal.expectedCloseAt])\n const { data: statusDictionary } = useCustomerDictionary('deal-statuses', 0, deal.organizationId ?? null)\n const statusEntry = deal.status ? statusDictionary?.map?.[deal.status] : null\n const companyLabel = React.useMemo(() => {\n const primaryCompany = companies[0]\n if (!primaryCompany) return null\n const extraCount = companies.length - 1\n return extraCount > 0 ? `${primaryCompany.label} +${extraCount}` : primaryCompany.label\n }, [companies])\n const timelineLabel = React.useMemo(() => {\n if (createdAtLabel && expectedCloseLabel) {\n return t('customers.deals.detail.header.timeline', 'Created {{created}} \u00B7 Expected close {{expected}}', {\n created: createdAtLabel,\n expected: expectedCloseLabel,\n })\n }\n if (createdAtLabel) {\n return t('customers.deals.detail.header.createdAt', 'Created {{date}}', { date: createdAtLabel })\n }\n if (expectedCloseLabel) {\n return t('customers.deals.detail.header.expectedClose', 'Expected close {{date}}', { date: expectedCloseLabel })\n }\n return null\n }, [createdAtLabel, expectedCloseLabel, t])\n const statusLabel = statusEntry?.label ?? (deal.status ? formatFallbackLabel(deal.status) : null)\n const showStatusChip = statusLabel && (!deal.closureOutcome || !isTerminalPipelineOutcomeLabel(deal.status))\n const pipelineBadgeLabel = pipelineName ?? null\n const canMoveStage = stageOptions.length > 0 && !deal.closureOutcome && typeof onStageChange === 'function'\n const messageSubtitle = React.useMemo(() => (\n [companyLabel, amountLabel].filter(Boolean).join(' \u00B7 ') || statusLabel || undefined\n ), [amountLabel, companyLabel, statusLabel])\n\n return (\n <div className=\"flex flex-col gap-5 xl:flex-row xl:items-start xl:justify-between\">\n <div className=\"flex min-w-0 gap-5\">\n <div className=\"flex size-16 shrink-0 items-center justify-center rounded-lg border border-border bg-muted text-muted-foreground\">\n <FileText className=\"size-7\" />\n </div>\n <div className=\"min-w-0 space-y-3\">\n <div className=\"flex flex-wrap items-baseline gap-2\">\n <h1 className=\"min-w-0 truncate text-2xl font-bold leading-tight text-foreground md:text-2xl\">\n {deal.title || t('customers.deals.detail.untitled', 'Untitled deal')}\n </h1>\n <span className=\"rounded-sm bg-muted px-2 py-1 text-overline font-semibold uppercase tracking-[0.04em] text-muted-foreground\">\n {t('customers.deals.detail.badge.deal', 'Deal')}\n </span>\n </div>\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-2 text-sm text-muted-foreground\">\n {companyLabel ? (\n <span className=\"inline-flex items-center gap-1.5\">\n <Building2 className=\"size-3.5\" />\n {companyLabel}\n </span>\n ) : null}\n {timelineLabel ? (\n <span className=\"inline-flex items-center gap-1.5\">\n <CalendarClock className=\"size-3.5\" />\n {timelineLabel}\n </span>\n ) : null}\n {amountLabel ? (\n <span className=\"font-semibold text-foreground\">{amountLabel}</span>\n ) : null}\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n {showStatusChip ? (\n <HeaderChip variant=\"info\">\n {statusLabel}\n </HeaderChip>\n ) : null}\n {deal.pipelineStage && !isTerminalPipelineOutcomeLabel(deal.pipelineStage) ? (\n <HeaderChip variant=\"warning\">\n {deal.pipelineStage}\n </HeaderChip>\n ) : null}\n {pipelineBadgeLabel ? (\n <HeaderChip icon={<Workflow className=\"size-3.5\" />}>\n {pipelineBadgeLabel}\n </HeaderChip>\n ) : null}\n {deal.closureOutcome ? (\n <HeaderChip variant={deal.closureOutcome === 'won' ? 'success' : 'error'}>\n {deal.closureOutcome === 'won'\n ? t('customers.deals.detail.badge.won', 'Won')\n : t('customers.deals.detail.badge.lost', 'Lost')}\n </HeaderChip>\n ) : null}\n {!companyLabel && people.length > 0 ? (\n <HeaderChip icon={<Users className=\"size-3.5\" />}>\n {people[0]?.label}\n </HeaderChip>\n ) : null}\n {owner && !pipelineBadgeLabel ? (\n <HeaderChip icon={<Workflow className=\"size-3.5\" />}>\n {owner.name}\n </HeaderChip>\n ) : null}\n </div>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2 xl:justify-end\">\n {canMoveStage ? (\n <StageActionButton\n stageOptions={stageOptions}\n currentStageId={currentStageId}\n onStageChange={onStageChange}\n disabled={isDirty || isSaving}\n isSaving={isStageSaving}\n />\n ) : null}\n <SendObjectMessageDialog\n object={{\n entityModule: 'customers',\n entityType: 'deal',\n entityId: deal.id,\n previewData: {\n title: deal.title || t('customers.deals.detail.untitled', 'Untitled deal'),\n subtitle: messageSubtitle,\n },\n }}\n viewHref={`/backend/customers/deals/${deal.id}`}\n buttonVariant=\"outline\"\n buttonSize=\"icon\"\n buttonClassName={HEADER_ICON_BUTTON_CLASS}\n buttonLabel={t('customers.deals.detail.actions.sendMessage', 'Send message')}\n />\n <ObjectHistoryButton\n resourceKind=\"customers.deal\"\n resourceId={deal.id}\n organizationId={deal.organizationId ?? undefined}\n includeRelated\n />\n <IconButton\n variant=\"outline\"\n size=\"sm\"\n type=\"button\"\n aria-label={t('customers.deals.detail.actions.delete', 'Delete')}\n className=\"h-8 w-8 rounded-md\"\n onClick={() => {\n void onDelete()\n }}\n >\n <Trash2 className=\"size-4\" />\n </IconButton>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={onSave}\n disabled={!isDirty || isSaving}\n className=\"h-8 rounded-md px-3\"\n >\n <Save className=\"size-4\" />\n {t('customers.deals.detail.actions.save', 'Save')}\n </Button>\n </div>\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -51,9 +51,9 @@ function DealWonPopup({
51
51
  const t = useT();
52
52
  return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (nextOpen) => {
53
53
  if (!nextOpen) onClose();
54
- }, children: /* @__PURE__ */ jsxs(DialogContent, { className: "overflow-hidden p-0 sm:max-w-[420px]", children: [
54
+ }, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-h-[90vh] overflow-hidden p-0 sm:max-w-[420px]", children: [
55
55
  /* @__PURE__ */ jsx(VisuallyHidden, { children: /* @__PURE__ */ jsx(DialogTitle, { children: t("customers.deals.detail.won.title", "Closed successfully") }) }),
56
- /* @__PURE__ */ jsxs("div", { className: "overflow-hidden rounded-2xl bg-card", children: [
56
+ /* @__PURE__ */ jsxs("div", { className: "max-h-[90vh] overflow-y-auto rounded-2xl bg-card", children: [
57
57
  /* @__PURE__ */ jsx("div", { className: "px-6 pb-5 pt-6", children: /* @__PURE__ */ jsx("div", { className: "flex h-[200px] items-center justify-center rounded-2xl bg-[linear-gradient(135deg,rgba(141,150,244,0.5),rgba(198,203,254,0.95))] text-foreground", children: /* @__PURE__ */ jsx(Trophy, { className: "size-24", strokeWidth: 1.5 }) }) }),
58
58
  /* @__PURE__ */ jsxs("div", { className: "space-y-5 px-7 pb-7 text-center", children: [
59
59
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/components/detail/DealWonPopup.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { VisuallyHidden } from '@radix-ui/react-visually-hidden'\nimport { ChartColumnIncreasing, Clock3, Medal, Trophy } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Dialog, DialogContent, DialogTitle } from '@open-mercato/ui/primitives/dialog'\n\ntype DealStatsPayload = {\n dealValue: number | null\n dealCurrency: string | null\n closureOutcome: 'won' | 'lost'\n closedAt: string\n pipelineName: string | null\n dealsClosedThisPeriod: number\n salesCycleDays: number | null\n dealRankInQuarter: number | null\n lossReason: string | null\n}\n\ntype DealWonPopupProps = {\n open: boolean\n onClose: () => void\n dealTitle: string\n stats: DealStatsPayload | null\n onViewDashboard?: () => void\n onBackToPipeline?: () => void\n}\n\nfunction formatCurrency(value: number | null, currency: string | null): string {\n if (value === null || !Number.isFinite(value)) return '\u2014'\n if (!currency) return value.toLocaleString()\n try {\n return new Intl.NumberFormat(undefined, { style: 'currency', currency }).format(value)\n } catch {\n return `${value.toLocaleString()} ${currency}`\n }\n}\n\nfunction formatClosedDate(value: string, t: ReturnType<typeof useT>): string {\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return t('customers.deals.detail.won.closed', 'Closed')\n return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })\n}\n\nfunction formatSalesCycle(value: number | null): string {\n if (value === null || !Number.isFinite(value)) return '\u2014'\n if (value >= 30) {\n const months = Math.max(1, Math.round(value / 30))\n return `${months} mo`\n }\n return `${value}d`\n}\n\nfunction StatCard({\n icon,\n label,\n value,\n}: {\n icon: React.ReactNode\n label: string\n value: string\n}) {\n return (\n <div className=\"rounded-2xl border bg-background px-4 py-4\">\n <div className=\"mb-2 flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.14em] text-muted-foreground\">\n {icon}\n {label}\n </div>\n <div className=\"text-xl font-semibold text-foreground\">{value}</div>\n </div>\n )\n}\n\nexport function DealWonPopup({\n open,\n onClose,\n dealTitle,\n stats,\n onViewDashboard,\n onBackToPipeline,\n}: DealWonPopupProps) {\n const t = useT()\n\n return (\n <Dialog open={open} onOpenChange={(nextOpen) => { if (!nextOpen) onClose() }}>\n <DialogContent className=\"overflow-hidden p-0 sm:max-w-[420px]\">\n <VisuallyHidden>\n <DialogTitle>{t('customers.deals.detail.won.title', 'Closed successfully')}</DialogTitle>\n </VisuallyHidden>\n <div className=\"overflow-hidden rounded-2xl bg-card\">\n <div className=\"px-6 pb-5 pt-6\">\n {/* TODO(ds-review): decorative gradient \u2014 consider defining a named gradient token if reused */}\n <div className=\"flex h-[200px] items-center justify-center rounded-2xl bg-[linear-gradient(135deg,rgba(141,150,244,0.5),rgba(198,203,254,0.95))] text-foreground\">\n <Trophy className=\"size-24\" strokeWidth={1.5} />\n </div>\n </div>\n\n <div className=\"space-y-5 px-7 pb-7 text-center\">\n <div className=\"space-y-2\">\n <h2 className=\"text-2xl font-bold leading-tight text-foreground\">\n {t('customers.deals.detail.won.title', 'Closed successfully')}\n </h2>\n <p className=\"text-sm font-medium text-muted-foreground\">\n {t('customers.deals.detail.won.subtitle', 'You are a sales machine!')}\n </p>\n </div>\n\n <div className=\"rounded-xl border bg-muted/20 px-4 py-4\">\n <p className=\"text-overline font-bold uppercase tracking-[0.16em] text-muted-foreground\">\n {dealTitle}\n </p>\n <p className=\"mt-2 text-2xl font-bold text-primary\">\n {stats ? formatCurrency(stats.dealValue, stats.dealCurrency) : '\u2014'}\n </p>\n <p className=\"mt-1 text-xs text-muted-foreground\">\n {t('customers.deals.detail.won.closed', 'Won')} \u00B7 {stats ? formatClosedDate(stats.closedAt, t) : '\u2014'}\n </p>\n </div>\n\n <div className=\"grid grid-cols-3 gap-2\">\n <StatCard\n icon={<ChartColumnIncreasing className=\"size-4\" />}\n label={t('customers.deals.detail.won.dealsThisWeek', 'Deals this week')}\n value={stats ? String(stats.dealsClosedThisPeriod) : '\u2014'}\n />\n <StatCard\n icon={<Clock3 className=\"size-4\" />}\n label={t('customers.deals.detail.won.salesCycle', 'Sales cycle')}\n value={formatSalesCycle(stats?.salesCycleDays ?? null)}\n />\n <StatCard\n icon={<Medal className=\"size-4\" />}\n label={t('customers.deals.detail.won.rank', 'Quarter rank')}\n value={stats?.dealRankInQuarter !== null && stats?.dealRankInQuarter !== undefined ? `#${stats.dealRankInQuarter}` : '\u2014'}\n />\n </div>\n\n <div className=\"rounded-2xl border bg-muted/30 px-4 py-4\">\n <div className=\"text-xs font-semibold uppercase tracking-[0.14em] text-muted-foreground\">\n {t('customers.deals.detail.won.pipeline', 'Pipeline')}\n </div>\n <div className=\"mt-2 text-sm text-foreground\">\n {stats?.pipelineName ?? t('customers.deals.detail.won.pipelineFallback', 'Current pipeline')}\n </div>\n </div>\n\n <div className=\"space-y-2\">\n {onViewDashboard ? (\n <Button type=\"button\" className=\"w-full\" onClick={onViewDashboard}>\n {t('customers.deals.detail.won.primaryAction', 'View sales report')}\n </Button>\n ) : null}\n <Button type=\"button\" variant={onViewDashboard ? 'outline' : 'default'} className=\"w-full\" onClick={onBackToPipeline ?? onClose}>\n {t('customers.deals.detail.won.secondaryAction', 'Back to pipeline')}\n </Button>\n </div>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { VisuallyHidden } from '@radix-ui/react-visually-hidden'\nimport { ChartColumnIncreasing, Clock3, Medal, Trophy } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Dialog, DialogContent, DialogTitle } from '@open-mercato/ui/primitives/dialog'\n\ntype DealStatsPayload = {\n dealValue: number | null\n dealCurrency: string | null\n closureOutcome: 'won' | 'lost'\n closedAt: string\n pipelineName: string | null\n dealsClosedThisPeriod: number\n salesCycleDays: number | null\n dealRankInQuarter: number | null\n lossReason: string | null\n}\n\ntype DealWonPopupProps = {\n open: boolean\n onClose: () => void\n dealTitle: string\n stats: DealStatsPayload | null\n onViewDashboard?: () => void\n onBackToPipeline?: () => void\n}\n\nfunction formatCurrency(value: number | null, currency: string | null): string {\n if (value === null || !Number.isFinite(value)) return '\u2014'\n if (!currency) return value.toLocaleString()\n try {\n return new Intl.NumberFormat(undefined, { style: 'currency', currency }).format(value)\n } catch {\n return `${value.toLocaleString()} ${currency}`\n }\n}\n\nfunction formatClosedDate(value: string, t: ReturnType<typeof useT>): string {\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return t('customers.deals.detail.won.closed', 'Closed')\n return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })\n}\n\nfunction formatSalesCycle(value: number | null): string {\n if (value === null || !Number.isFinite(value)) return '\u2014'\n if (value >= 30) {\n const months = Math.max(1, Math.round(value / 30))\n return `${months} mo`\n }\n return `${value}d`\n}\n\nfunction StatCard({\n icon,\n label,\n value,\n}: {\n icon: React.ReactNode\n label: string\n value: string\n}) {\n return (\n <div className=\"rounded-2xl border bg-background px-4 py-4\">\n <div className=\"mb-2 flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.14em] text-muted-foreground\">\n {icon}\n {label}\n </div>\n <div className=\"text-xl font-semibold text-foreground\">{value}</div>\n </div>\n )\n}\n\nexport function DealWonPopup({\n open,\n onClose,\n dealTitle,\n stats,\n onViewDashboard,\n onBackToPipeline,\n}: DealWonPopupProps) {\n const t = useT()\n\n return (\n <Dialog open={open} onOpenChange={(nextOpen) => { if (!nextOpen) onClose() }}>\n <DialogContent className=\"max-h-[90vh] overflow-hidden p-0 sm:max-w-[420px]\">\n <VisuallyHidden>\n <DialogTitle>{t('customers.deals.detail.won.title', 'Closed successfully')}</DialogTitle>\n </VisuallyHidden>\n <div className=\"max-h-[90vh] overflow-y-auto rounded-2xl bg-card\">\n <div className=\"px-6 pb-5 pt-6\">\n {/* TODO(ds-review): decorative gradient \u2014 consider defining a named gradient token if reused */}\n <div className=\"flex h-[200px] items-center justify-center rounded-2xl bg-[linear-gradient(135deg,rgba(141,150,244,0.5),rgba(198,203,254,0.95))] text-foreground\">\n <Trophy className=\"size-24\" strokeWidth={1.5} />\n </div>\n </div>\n\n <div className=\"space-y-5 px-7 pb-7 text-center\">\n <div className=\"space-y-2\">\n <h2 className=\"text-2xl font-bold leading-tight text-foreground\">\n {t('customers.deals.detail.won.title', 'Closed successfully')}\n </h2>\n <p className=\"text-sm font-medium text-muted-foreground\">\n {t('customers.deals.detail.won.subtitle', 'You are a sales machine!')}\n </p>\n </div>\n\n <div className=\"rounded-xl border bg-muted/20 px-4 py-4\">\n <p className=\"text-overline font-bold uppercase tracking-[0.16em] text-muted-foreground\">\n {dealTitle}\n </p>\n <p className=\"mt-2 text-2xl font-bold text-primary\">\n {stats ? formatCurrency(stats.dealValue, stats.dealCurrency) : '\u2014'}\n </p>\n <p className=\"mt-1 text-xs text-muted-foreground\">\n {t('customers.deals.detail.won.closed', 'Won')} \u00B7 {stats ? formatClosedDate(stats.closedAt, t) : '\u2014'}\n </p>\n </div>\n\n <div className=\"grid grid-cols-3 gap-2\">\n <StatCard\n icon={<ChartColumnIncreasing className=\"size-4\" />}\n label={t('customers.deals.detail.won.dealsThisWeek', 'Deals this week')}\n value={stats ? String(stats.dealsClosedThisPeriod) : '\u2014'}\n />\n <StatCard\n icon={<Clock3 className=\"size-4\" />}\n label={t('customers.deals.detail.won.salesCycle', 'Sales cycle')}\n value={formatSalesCycle(stats?.salesCycleDays ?? null)}\n />\n <StatCard\n icon={<Medal className=\"size-4\" />}\n label={t('customers.deals.detail.won.rank', 'Quarter rank')}\n value={stats?.dealRankInQuarter !== null && stats?.dealRankInQuarter !== undefined ? `#${stats.dealRankInQuarter}` : '\u2014'}\n />\n </div>\n\n <div className=\"rounded-2xl border bg-muted/30 px-4 py-4\">\n <div className=\"text-xs font-semibold uppercase tracking-[0.14em] text-muted-foreground\">\n {t('customers.deals.detail.won.pipeline', 'Pipeline')}\n </div>\n <div className=\"mt-2 text-sm text-foreground\">\n {stats?.pipelineName ?? t('customers.deals.detail.won.pipelineFallback', 'Current pipeline')}\n </div>\n </div>\n\n <div className=\"space-y-2\">\n {onViewDashboard ? (\n <Button type=\"button\" className=\"w-full\" onClick={onViewDashboard}>\n {t('customers.deals.detail.won.primaryAction', 'View sales report')}\n </Button>\n ) : null}\n <Button type=\"button\" variant={onViewDashboard ? 'outline' : 'default'} className=\"w-full\" onClick={onBackToPipeline ?? onClose}>\n {t('customers.deals.detail.won.secondaryAction', 'Back to pipeline')}\n </Button>\n </div>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,12 +1,13 @@
1
1
  "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
- import { SquarePen, Calendar, Check } from "lucide-react";
4
+ import { SquarePen, Calendar, Check, ChevronDown, ChevronUp } from "lucide-react";
5
5
  import { z } from "zod";
6
6
  import { useT } from "@open-mercato/shared/lib/i18n/context";
7
7
  import { apiCallOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
8
8
  import { flash } from "@open-mercato/ui/backend/FlashMessages";
9
9
  import { Button } from "@open-mercato/ui/primitives/button";
10
+ import { usePersistedBooleanFlag } from "@open-mercato/ui/backend/crud/usePersistedBooleanFlag";
10
11
  import { ActivityTypeSelector } from "./ActivityTypeSelector.js";
11
12
  import { MiniWeekCalendar } from "./MiniWeekCalendar.js";
12
13
  const composerSchema = z.object({
@@ -40,6 +41,21 @@ function InlineActivityComposer({
40
41
  const [saving, setSaving] = React.useState(false);
41
42
  const [errors, setErrors] = React.useState({});
42
43
  const descriptionRef = React.useRef(null);
44
+ const weekPreviewStorageKey = `om:inline-composer:week-preview:${entityType}`;
45
+ const { value: weekPreviewHidden, toggle: toggleWeekPreview } = usePersistedBooleanFlag(
46
+ weekPreviewStorageKey,
47
+ false
48
+ );
49
+ const resizeDescription = React.useCallback(() => {
50
+ const el = descriptionRef.current;
51
+ if (!el) return;
52
+ el.style.height = "auto";
53
+ const maxHeight = 200;
54
+ el.style.height = `${Math.min(el.scrollHeight, maxHeight)}px`;
55
+ }, []);
56
+ React.useEffect(() => {
57
+ resizeDescription();
58
+ }, [description, resizeDescription]);
43
59
  const handleTypeSelect = React.useCallback((type) => {
44
60
  setSelectedType((previous) => previous === type ? null : type);
45
61
  setErrors({});
@@ -149,20 +165,29 @@ function InlineActivityComposer({
149
165
  /* @__PURE__ */ jsx(ActivityTypeSelector, { selectedType, onSelect: handleTypeSelect }),
150
166
  /* @__PURE__ */ jsxs("div", { className: "mt-4 flex items-start gap-3", children: [
151
167
  /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
168
+ /* @__PURE__ */ jsx(
169
+ "label",
170
+ {
171
+ htmlFor: "inline-activity-composer-description",
172
+ className: "mb-1 block text-xs font-medium text-muted-foreground",
173
+ children: t("customers.activityComposer.descriptionLabel", "Description")
174
+ }
175
+ ),
152
176
  /* @__PURE__ */ jsx(
153
177
  "textarea",
154
178
  {
155
179
  ref: descriptionRef,
180
+ id: "inline-activity-composer-description",
156
181
  value: description,
157
182
  onChange: (event) => setDescription(event.target.value),
158
183
  placeholder: t("customers.activityComposer.descriptionPlaceholder", "What happened?"),
159
- className: "min-h-[44px] w-full resize-none rounded-lg border bg-background px-3 py-2.5 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring",
160
- rows: 1
184
+ className: "min-h-[72px] w-full resize-none rounded-lg border bg-background px-3 py-2.5 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring",
185
+ rows: 3
161
186
  }
162
187
  ),
163
188
  errors.description ? /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-destructive", children: errors.description }) : null
164
189
  ] }),
165
- /* @__PURE__ */ jsxs("label", { className: "flex shrink-0 cursor-pointer items-center gap-2 rounded-lg border bg-background px-3 py-2.5 text-sm text-muted-foreground", children: [
190
+ /* @__PURE__ */ jsxs("label", { className: "mt-[22px] flex shrink-0 cursor-pointer items-center gap-2 rounded-lg border bg-background px-3 py-2.5 text-sm text-muted-foreground", children: [
166
191
  /* @__PURE__ */ jsx(Calendar, { className: "size-4" }),
167
192
  /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: formatDateBadge(occurredAt, t) }),
168
193
  /* @__PURE__ */ jsx(
@@ -176,7 +201,38 @@ function InlineActivityComposer({
176
201
  )
177
202
  ] })
178
203
  ] }),
179
- /* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(MiniWeekCalendar, { entityId, useCanonicalInteractions, refreshRef: calendarRefreshRef }) }),
204
+ /* @__PURE__ */ jsxs("div", { className: "mt-4", children: [
205
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
206
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: t("customers.activityComposer.weekPreviewTitle", "This week") }),
207
+ /* @__PURE__ */ jsx(
208
+ Button,
209
+ {
210
+ type: "button",
211
+ variant: "ghost",
212
+ size: "sm",
213
+ onClick: toggleWeekPreview,
214
+ "aria-expanded": !weekPreviewHidden,
215
+ "aria-controls": "inline-activity-composer-week-preview",
216
+ className: "h-auto px-2 py-1 text-xs text-muted-foreground hover:text-foreground",
217
+ children: weekPreviewHidden ? /* @__PURE__ */ jsxs(Fragment, { children: [
218
+ /* @__PURE__ */ jsx(ChevronDown, { className: "mr-1 size-3" }),
219
+ t("customers.activityComposer.showWeekPreview", "Show week preview")
220
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
221
+ /* @__PURE__ */ jsx(ChevronUp, { className: "mr-1 size-3" }),
222
+ t("customers.activityComposer.hideWeekPreview", "Hide week preview")
223
+ ] })
224
+ }
225
+ )
226
+ ] }),
227
+ !weekPreviewHidden ? /* @__PURE__ */ jsx("div", { id: "inline-activity-composer-week-preview", children: /* @__PURE__ */ jsx(
228
+ MiniWeekCalendar,
229
+ {
230
+ entityId,
231
+ useCanonicalInteractions,
232
+ refreshRef: calendarRefreshRef
233
+ }
234
+ ) }) : null
235
+ ] }),
180
236
  scheduledAt || false ? /* @__PURE__ */ jsxs("div", { className: "mt-3 flex items-center gap-2", children: [
181
237
  /* @__PURE__ */ jsx("label", { className: "text-xs text-muted-foreground", children: t("customers.activityComposer.scheduledLabel", "Scheduled for") }),
182
238
  /* @__PURE__ */ jsx(
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/components/detail/InlineActivityComposer.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { SquarePen, Calendar, Check } from 'lucide-react'\nimport { z } from 'zod'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { ActivityTypeSelector, type ActivityType } from './ActivityTypeSelector'\nimport { MiniWeekCalendar } from './MiniWeekCalendar'\n\ntype GuardedMutationRunner = <T,>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n) => Promise<T>\n\nconst composerSchema = z.object({\n description: z.string().trim().min(1, 'customers.activityComposer.validation.descriptionRequired'),\n occurredAt: z.string().min(1),\n})\n\ntype TranslateFn = (key: string, fallback?: string, params?: Record<string, string>) => string\n\nfunction formatDateBadge(isoLocal: string, t: TranslateFn): string {\n if (!isoLocal) return ''\n const now = new Date()\n const date = new Date(isoLocal.replace('T', ' '))\n const time = isoLocal.slice(11, 16)\n const isToday =\n date.getFullYear() === now.getFullYear() &&\n date.getMonth() === now.getMonth() &&\n date.getDate() === now.getDate()\n const dayLabel = isToday\n ? t('customers.activityComposer.today', 'Today')\n : date.toLocaleDateString(undefined, { day: 'numeric', month: 'short' })\n return `${dayLabel} \u00B7 ${time}`\n}\n\ninterface InlineActivityComposerProps {\n entityType: 'company' | 'person' | 'deal'\n entityId: string\n dealId?: string | null\n onActivityCreated?: () => void\n runGuardedMutation?: GuardedMutationRunner\n onScheduleRequested?: () => void\n useCanonicalInteractions?: boolean\n}\n\nexport function InlineActivityComposer({\n entityType,\n entityId,\n dealId = null,\n onActivityCreated,\n runGuardedMutation,\n onScheduleRequested,\n useCanonicalInteractions,\n}: InlineActivityComposerProps) {\n const t = useT()\n const calendarRefreshRef = React.useRef<(() => void) | null>(null)\n const [selectedType, setSelectedType] = React.useState<ActivityType | null>('call')\n const [description, setDescription] = React.useState('')\n const [occurredAt, setOccurredAt] = React.useState(() => new Date().toISOString().slice(0, 16))\n const [scheduledAt, setScheduledAt] = React.useState('')\n const [saving, setSaving] = React.useState(false)\n const [errors, setErrors] = React.useState<Record<string, string>>({})\n const descriptionRef = React.useRef<HTMLTextAreaElement>(null)\n\n const handleTypeSelect = React.useCallback((type: ActivityType) => {\n setSelectedType((previous) => (previous === type ? null : type))\n setErrors({})\n }, [])\n\n const handleReset = React.useCallback(() => {\n setDescription('')\n setOccurredAt(new Date().toISOString().slice(0, 16))\n setScheduledAt('')\n setErrors({})\n }, [])\n\n const handleSave = React.useCallback(async () => {\n if (!selectedType) {\n setErrors({ type: t('customers.activityComposer.validation.typeRequired', 'Select an activity type') })\n return\n }\n\n const result = composerSchema.safeParse({ description, occurredAt })\n if (!result.success) {\n const fieldErrors: Record<string, string> = {}\n for (const issue of result.error.issues) {\n const field = issue.path[0]\n if (field) fieldErrors[String(field)] = t(issue.message)\n }\n setErrors(fieldErrors)\n return\n }\n\n setSaving(true)\n setErrors({})\n\n try {\n const mutationPayload = {\n entityId,\n dealId,\n interactionType: selectedType,\n body: description.trim(),\n status: scheduledAt ? 'planned' : 'done',\n occurredAt: scheduledAt ? null : new Date(occurredAt).toISOString(),\n scheduledAt: scheduledAt ? new Date(scheduledAt).toISOString() : null,\n }\n const operation = () =>\n apiCallOrThrow('/api/customers/interactions', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(mutationPayload),\n })\n\n if (runGuardedMutation) {\n await runGuardedMutation(operation, mutationPayload)\n } else {\n await operation()\n }\n\n flash(\n t('customers.activityComposer.saved', {\n type: t(`customers.activityComposer.types.${selectedType}`),\n }),\n 'success',\n )\n handleReset()\n calendarRefreshRef.current?.()\n onActivityCreated?.()\n } catch (error) {\n console.error('customers.inlineActivityComposer.save failed', error)\n flash(t('customers.activityComposer.error', 'Failed to save activity'), 'error')\n } finally {\n setSaving(false)\n }\n }, [\n description,\n dealId,\n entityId,\n handleReset,\n occurredAt,\n onActivityCreated,\n runGuardedMutation,\n scheduledAt,\n selectedType,\n t,\n ])\n\n const handleKeyDown = React.useCallback((event: React.KeyboardEvent) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n handleSave()\n }\n }, [handleSave])\n\n return (\n <div className=\"rounded-2xl border border-border/70 bg-card p-5\" onKeyDown={handleKeyDown}>\n {/* Header: title + save button */}\n <div className=\"mb-4 flex items-center justify-between\">\n <h3 className=\"flex items-center gap-2 text-base font-semibold text-foreground\">\n <SquarePen className=\"size-4\" />\n {t('customers.activityComposer.title', 'Log activity')}\n </h3>\n <div className=\"flex items-center gap-2\">\n {onScheduleRequested ? (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={onScheduleRequested} disabled={saving}>\n <Calendar className=\"size-4\" />\n {t('customers.activityComposer.schedule', 'Schedule')}\n </Button>\n ) : null}\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={handleSave}\n disabled={saving || !selectedType}\n >\n <Check className=\"size-4\" />\n {saving\n ? t('customers.activityComposer.saving', 'Saving...')\n : t('customers.activityComposer.saveActivity', 'Save activity')}\n </Button>\n </div>\n </div>\n\n {/* Activity type selector \u2014 4 equal-width buttons */}\n <ActivityTypeSelector selectedType={selectedType} onSelect={handleTypeSelect} />\n\n {/* Description + date row */}\n <div className=\"mt-4 flex items-start gap-3\">\n <div className=\"flex-1\">\n <textarea\n ref={descriptionRef}\n value={description}\n onChange={(event) => setDescription(event.target.value)}\n placeholder={t('customers.activityComposer.descriptionPlaceholder', 'What happened?')}\n className=\"min-h-[44px] w-full resize-none rounded-lg border bg-background px-3 py-2.5 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring\"\n rows={1}\n />\n {errors.description ? (\n <p className=\"mt-1 text-xs text-destructive\">{errors.description}</p>\n ) : null}\n </div>\n <label className=\"flex shrink-0 cursor-pointer items-center gap-2 rounded-lg border bg-background px-3 py-2.5 text-sm text-muted-foreground\">\n <Calendar className=\"size-4\" />\n <span className=\"text-sm font-medium text-foreground\">{formatDateBadge(occurredAt, t)}</span>\n <input\n type=\"datetime-local\"\n value={occurredAt}\n onChange={(event) => setOccurredAt(event.target.value)}\n className=\"sr-only\"\n />\n </label>\n </div>\n\n {/* Mini calendar preview */}\n <div className=\"mt-4\">\n <MiniWeekCalendar entityId={entityId} useCanonicalInteractions={useCanonicalInteractions} refreshRef={calendarRefreshRef} />\n </div>\n\n {/* Scheduled for (optional) */}\n {scheduledAt || false ? (\n <div className=\"mt-3 flex items-center gap-2\">\n <label className=\"text-xs text-muted-foreground\">\n {t('customers.activityComposer.scheduledLabel', 'Scheduled for')}\n </label>\n <input\n type=\"datetime-local\"\n value={scheduledAt}\n onChange={(event) => setScheduledAt(event.target.value)}\n className=\"rounded-md border bg-background px-3 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-ring\"\n />\n </div>\n ) : null}\n </div>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { SquarePen, Calendar, Check, ChevronDown, ChevronUp } from 'lucide-react'\nimport { z } from 'zod'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { usePersistedBooleanFlag } from '@open-mercato/ui/backend/crud/usePersistedBooleanFlag'\nimport { ActivityTypeSelector, type ActivityType } from './ActivityTypeSelector'\nimport { MiniWeekCalendar } from './MiniWeekCalendar'\n\ntype GuardedMutationRunner = <T,>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n) => Promise<T>\n\nconst composerSchema = z.object({\n description: z.string().trim().min(1, 'customers.activityComposer.validation.descriptionRequired'),\n occurredAt: z.string().min(1),\n})\n\ntype TranslateFn = (key: string, fallback?: string, params?: Record<string, string>) => string\n\nfunction formatDateBadge(isoLocal: string, t: TranslateFn): string {\n if (!isoLocal) return ''\n const now = new Date()\n const date = new Date(isoLocal.replace('T', ' '))\n const time = isoLocal.slice(11, 16)\n const isToday =\n date.getFullYear() === now.getFullYear() &&\n date.getMonth() === now.getMonth() &&\n date.getDate() === now.getDate()\n const dayLabel = isToday\n ? t('customers.activityComposer.today', 'Today')\n : date.toLocaleDateString(undefined, { day: 'numeric', month: 'short' })\n return `${dayLabel} \u00B7 ${time}`\n}\n\ninterface InlineActivityComposerProps {\n entityType: 'company' | 'person' | 'deal'\n entityId: string\n dealId?: string | null\n onActivityCreated?: () => void\n runGuardedMutation?: GuardedMutationRunner\n onScheduleRequested?: () => void\n useCanonicalInteractions?: boolean\n}\n\nexport function InlineActivityComposer({\n entityType,\n entityId,\n dealId = null,\n onActivityCreated,\n runGuardedMutation,\n onScheduleRequested,\n useCanonicalInteractions,\n}: InlineActivityComposerProps) {\n const t = useT()\n const calendarRefreshRef = React.useRef<(() => void) | null>(null)\n const [selectedType, setSelectedType] = React.useState<ActivityType | null>('call')\n const [description, setDescription] = React.useState('')\n const [occurredAt, setOccurredAt] = React.useState(() => new Date().toISOString().slice(0, 16))\n const [scheduledAt, setScheduledAt] = React.useState('')\n const [saving, setSaving] = React.useState(false)\n const [errors, setErrors] = React.useState<Record<string, string>>({})\n const descriptionRef = React.useRef<HTMLTextAreaElement>(null)\n\n const weekPreviewStorageKey = `om:inline-composer:week-preview:${entityType}`\n const { value: weekPreviewHidden, toggle: toggleWeekPreview } = usePersistedBooleanFlag(\n weekPreviewStorageKey,\n false,\n )\n\n const resizeDescription = React.useCallback(() => {\n const el = descriptionRef.current\n if (!el) return\n el.style.height = 'auto'\n const maxHeight = 200\n el.style.height = `${Math.min(el.scrollHeight, maxHeight)}px`\n }, [])\n\n React.useEffect(() => {\n resizeDescription()\n }, [description, resizeDescription])\n\n const handleTypeSelect = React.useCallback((type: ActivityType) => {\n setSelectedType((previous) => (previous === type ? null : type))\n setErrors({})\n }, [])\n\n const handleReset = React.useCallback(() => {\n setDescription('')\n setOccurredAt(new Date().toISOString().slice(0, 16))\n setScheduledAt('')\n setErrors({})\n }, [])\n\n const handleSave = React.useCallback(async () => {\n if (!selectedType) {\n setErrors({ type: t('customers.activityComposer.validation.typeRequired', 'Select an activity type') })\n return\n }\n\n const result = composerSchema.safeParse({ description, occurredAt })\n if (!result.success) {\n const fieldErrors: Record<string, string> = {}\n for (const issue of result.error.issues) {\n const field = issue.path[0]\n if (field) fieldErrors[String(field)] = t(issue.message)\n }\n setErrors(fieldErrors)\n return\n }\n\n setSaving(true)\n setErrors({})\n\n try {\n const mutationPayload = {\n entityId,\n dealId,\n interactionType: selectedType,\n body: description.trim(),\n status: scheduledAt ? 'planned' : 'done',\n occurredAt: scheduledAt ? null : new Date(occurredAt).toISOString(),\n scheduledAt: scheduledAt ? new Date(scheduledAt).toISOString() : null,\n }\n const operation = () =>\n apiCallOrThrow('/api/customers/interactions', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(mutationPayload),\n })\n\n if (runGuardedMutation) {\n await runGuardedMutation(operation, mutationPayload)\n } else {\n await operation()\n }\n\n flash(\n t('customers.activityComposer.saved', {\n type: t(`customers.activityComposer.types.${selectedType}`),\n }),\n 'success',\n )\n handleReset()\n calendarRefreshRef.current?.()\n onActivityCreated?.()\n } catch (error) {\n console.error('customers.inlineActivityComposer.save failed', error)\n flash(t('customers.activityComposer.error', 'Failed to save activity'), 'error')\n } finally {\n setSaving(false)\n }\n }, [\n description,\n dealId,\n entityId,\n handleReset,\n occurredAt,\n onActivityCreated,\n runGuardedMutation,\n scheduledAt,\n selectedType,\n t,\n ])\n\n const handleKeyDown = React.useCallback((event: React.KeyboardEvent) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n handleSave()\n }\n }, [handleSave])\n\n return (\n <div className=\"rounded-2xl border border-border/70 bg-card p-5\" onKeyDown={handleKeyDown}>\n {/* Header: title + save button */}\n <div className=\"mb-4 flex items-center justify-between\">\n <h3 className=\"flex items-center gap-2 text-base font-semibold text-foreground\">\n <SquarePen className=\"size-4\" />\n {t('customers.activityComposer.title', 'Log activity')}\n </h3>\n <div className=\"flex items-center gap-2\">\n {onScheduleRequested ? (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={onScheduleRequested} disabled={saving}>\n <Calendar className=\"size-4\" />\n {t('customers.activityComposer.schedule', 'Schedule')}\n </Button>\n ) : null}\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={handleSave}\n disabled={saving || !selectedType}\n >\n <Check className=\"size-4\" />\n {saving\n ? t('customers.activityComposer.saving', 'Saving...')\n : t('customers.activityComposer.saveActivity', 'Save activity')}\n </Button>\n </div>\n </div>\n\n {/* Activity type selector \u2014 4 equal-width buttons */}\n <ActivityTypeSelector selectedType={selectedType} onSelect={handleTypeSelect} />\n\n {/* Description + date row */}\n <div className=\"mt-4 flex items-start gap-3\">\n <div className=\"flex-1\">\n <label\n htmlFor=\"inline-activity-composer-description\"\n className=\"mb-1 block text-xs font-medium text-muted-foreground\"\n >\n {t('customers.activityComposer.descriptionLabel', 'Description')}\n </label>\n <textarea\n ref={descriptionRef}\n id=\"inline-activity-composer-description\"\n value={description}\n onChange={(event) => setDescription(event.target.value)}\n placeholder={t('customers.activityComposer.descriptionPlaceholder', 'What happened?')}\n className=\"min-h-[72px] w-full resize-none rounded-lg border bg-background px-3 py-2.5 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring\"\n rows={3}\n />\n {errors.description ? (\n <p className=\"mt-1 text-xs text-destructive\">{errors.description}</p>\n ) : null}\n </div>\n <label className=\"mt-[22px] flex shrink-0 cursor-pointer items-center gap-2 rounded-lg border bg-background px-3 py-2.5 text-sm text-muted-foreground\">\n <Calendar className=\"size-4\" />\n <span className=\"text-sm font-medium text-foreground\">{formatDateBadge(occurredAt, t)}</span>\n <input\n type=\"datetime-local\"\n value={occurredAt}\n onChange={(event) => setOccurredAt(event.target.value)}\n className=\"sr-only\"\n />\n </label>\n </div>\n\n <div className=\"mt-4\">\n <div className=\"mb-2 flex items-center justify-between\">\n <span className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {t('customers.activityComposer.weekPreviewTitle', 'This week')}\n </span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={toggleWeekPreview}\n aria-expanded={!weekPreviewHidden}\n aria-controls=\"inline-activity-composer-week-preview\"\n className=\"h-auto px-2 py-1 text-xs text-muted-foreground hover:text-foreground\"\n >\n {weekPreviewHidden ? (\n <>\n <ChevronDown className=\"mr-1 size-3\" />\n {t('customers.activityComposer.showWeekPreview', 'Show week preview')}\n </>\n ) : (\n <>\n <ChevronUp className=\"mr-1 size-3\" />\n {t('customers.activityComposer.hideWeekPreview', 'Hide week preview')}\n </>\n )}\n </Button>\n </div>\n {!weekPreviewHidden ? (\n <div id=\"inline-activity-composer-week-preview\">\n <MiniWeekCalendar\n entityId={entityId}\n useCanonicalInteractions={useCanonicalInteractions}\n refreshRef={calendarRefreshRef}\n />\n </div>\n ) : null}\n </div>\n\n {/* Scheduled for (optional) */}\n {scheduledAt || false ? (\n <div className=\"mt-3 flex items-center gap-2\">\n <label className=\"text-xs text-muted-foreground\">\n {t('customers.activityComposer.scheduledLabel', 'Scheduled for')}\n </label>\n <input\n type=\"datetime-local\"\n value={scheduledAt}\n onChange={(event) => setScheduledAt(event.target.value)}\n className=\"rounded-md border bg-background px-3 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-ring\"\n />\n </div>\n ) : null}\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,39 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { VersionHistoryAction } from "@open-mercato/ui/backend/version-history";
5
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
6
+ const OUTLINE_ICON_BUTTON_CLASSES = "size-8 rounded-md border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50";
7
+ function ObjectHistoryButton({
8
+ resourceKind,
9
+ resourceId,
10
+ resourceIdFallback,
11
+ organizationId,
12
+ includeRelated
13
+ }) {
14
+ const t = useT();
15
+ const config = React.useMemo(
16
+ () => ({
17
+ resourceKind,
18
+ resourceId,
19
+ resourceIdFallback,
20
+ organizationId,
21
+ includeRelated
22
+ }),
23
+ [resourceKind, resourceId, resourceIdFallback, organizationId, includeRelated]
24
+ );
25
+ return /* @__PURE__ */ jsx(
26
+ VersionHistoryAction,
27
+ {
28
+ config,
29
+ t,
30
+ buttonClassName: OUTLINE_ICON_BUTTON_CLASSES
31
+ }
32
+ );
33
+ }
34
+ var ObjectHistoryButton_default = ObjectHistoryButton;
35
+ export {
36
+ ObjectHistoryButton,
37
+ ObjectHistoryButton_default as default
38
+ };
39
+ //# sourceMappingURL=ObjectHistoryButton.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/customers/components/detail/ObjectHistoryButton.tsx"],
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { VersionHistoryAction } from '@open-mercato/ui/backend/version-history'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { VersionHistoryConfig } from '@open-mercato/ui/backend/version-history'\n\nexport type ObjectHistoryButtonProps = {\n resourceKind: VersionHistoryConfig['resourceKind']\n resourceId: VersionHistoryConfig['resourceId']\n resourceIdFallback?: VersionHistoryConfig['resourceIdFallback']\n organizationId?: VersionHistoryConfig['organizationId']\n includeRelated?: VersionHistoryConfig['includeRelated']\n}\n\nconst OUTLINE_ICON_BUTTON_CLASSES =\n 'size-8 rounded-md border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50'\n\nexport function ObjectHistoryButton({\n resourceKind,\n resourceId,\n resourceIdFallback,\n organizationId,\n includeRelated,\n}: ObjectHistoryButtonProps) {\n const t = useT()\n const config = React.useMemo<VersionHistoryConfig>(\n () => ({\n resourceKind,\n resourceId,\n resourceIdFallback,\n organizationId,\n includeRelated,\n }),\n [resourceKind, resourceId, resourceIdFallback, organizationId, includeRelated],\n )\n\n return (\n <VersionHistoryAction\n config={config}\n t={t}\n buttonClassName={OUTLINE_ICON_BUTTON_CLASSES}\n />\n )\n}\n\nexport default ObjectHistoryButton\n"],
5
+ "mappings": ";AAsCI;AApCJ,YAAY,WAAW;AACvB,SAAS,4BAA4B;AACrC,SAAS,YAAY;AAWrB,MAAM,8BACJ;AAEK,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA6B;AAC3B,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,MAAM;AAAA,IACnB,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,CAAC,cAAc,YAAY,oBAAoB,gBAAgB,cAAc;AAAA,EAC/E;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,iBAAiB;AAAA;AAAA,EACnB;AAEJ;AAEA,IAAO,8BAAQ;",
6
+ "names": []
7
+ }
@@ -8,11 +8,14 @@ import { useT } from "@open-mercato/shared/lib/i18n/context";
8
8
  import { Button } from "@open-mercato/ui/primitives/button";
9
9
  import { IconButton } from "@open-mercato/ui/primitives/icon-button";
10
10
  import { Badge } from "@open-mercato/ui/primitives/badge";
11
+ import { SendObjectMessageDialog } from "@open-mercato/ui/backend/messages";
11
12
  import { useQueryClient } from "@tanstack/react-query";
13
+ import { ObjectHistoryButton } from "./ObjectHistoryButton.js";
12
14
  import { PersonTagsDialog } from "./PersonTagsDialog.js";
13
15
  import { useCustomerDictionary, invalidateCustomerDictionary } from "./hooks/useCustomerDictionary.js";
14
16
  import { renderDictionaryIcon } from "../../../dictionaries/components/dictionaryAppearance.js";
15
17
  import { getInitials, formatFallbackLabel } from "./utils.js";
18
+ const HEADER_ICON_BUTTON_CLASS = "size-8 rounded-md";
16
19
  function DictionaryBadge({ value, map, categoryIcon, className }) {
17
20
  const entry = map?.[value];
18
21
  const color = entry?.color ?? null;
@@ -160,6 +163,33 @@ function PersonDetailHeader({
160
163
  ] })
161
164
  ] }),
162
165
  /* @__PURE__ */ jsxs("div", { className: "flex w-full shrink-0 items-center justify-start gap-2 sm:w-auto sm:justify-end", children: [
166
+ /* @__PURE__ */ jsx(
167
+ SendObjectMessageDialog,
168
+ {
169
+ object: {
170
+ entityModule: "customers",
171
+ entityType: "person",
172
+ entityId: person.id,
173
+ previewData: {
174
+ title: displayName,
175
+ subtitle: person.primaryEmail ?? companyName ?? void 0
176
+ }
177
+ },
178
+ viewHref: `/backend/customers/people-v2/${person.id}`,
179
+ buttonVariant: "outline",
180
+ buttonSize: "icon",
181
+ buttonClassName: HEADER_ICON_BUTTON_CLASS,
182
+ buttonLabel: t("customers.people.detail.actions.sendMessage", "Send message")
183
+ }
184
+ ),
185
+ /* @__PURE__ */ jsx(
186
+ ObjectHistoryButton,
187
+ {
188
+ resourceKind: "customers.person",
189
+ resourceId: person.id,
190
+ organizationId: person.organizationId ?? void 0
191
+ }
192
+ ),
163
193
  /* @__PURE__ */ jsx(
164
194
  IconButton,
165
195
  {