@open-mercato/core 0.5.1-develop.3043.1a796c3920 → 0.6.0

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 (106) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +21 -1
  3. package/dist/modules/api_keys/api/keys/route.js +9 -0
  4. package/dist/modules/api_keys/api/keys/route.js.map +2 -2
  5. package/dist/modules/audit_logs/services/accessLogService.js +13 -0
  6. package/dist/modules/audit_logs/services/accessLogService.js.map +3 -3
  7. package/dist/modules/audit_logs/services/actionLogService.js +6 -5
  8. package/dist/modules/audit_logs/services/actionLogService.js.map +2 -2
  9. package/dist/modules/auth/api/roles/acl/route.js +27 -37
  10. package/dist/modules/auth/api/roles/acl/route.js.map +2 -2
  11. package/dist/modules/auth/api/users/route.js +41 -28
  12. package/dist/modules/auth/api/users/route.js.map +3 -3
  13. package/dist/modules/auth/lib/grantChecks.js +160 -0
  14. package/dist/modules/auth/lib/grantChecks.js.map +7 -0
  15. package/dist/modules/configs/cli.js +11 -0
  16. package/dist/modules/configs/cli.js.map +2 -2
  17. package/dist/modules/configs/lib/touchGeneratedBarrels.js +46 -0
  18. package/dist/modules/configs/lib/touchGeneratedBarrels.js.map +7 -0
  19. package/dist/modules/customers/api/activities/route.js +1 -52
  20. package/dist/modules/customers/api/activities/route.js.map +2 -2
  21. package/dist/modules/customers/api/interactions/counts/route.js +2 -1
  22. package/dist/modules/customers/api/interactions/counts/route.js.map +2 -2
  23. package/dist/modules/customers/api/interactions/route.js +21 -1
  24. package/dist/modules/customers/api/interactions/route.js.map +2 -2
  25. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +7 -3
  26. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
  27. package/dist/modules/customers/backend/customers/deals/[id]/page.js +5 -1
  28. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  29. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +7 -3
  30. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
  31. package/dist/modules/customers/components/detail/ActivitiesCard.js +62 -6
  32. package/dist/modules/customers/components/detail/ActivitiesCard.js.map +2 -2
  33. package/dist/modules/customers/components/detail/ActivitiesDayStrip.js +21 -6
  34. package/dist/modules/customers/components/detail/ActivitiesDayStrip.js.map +2 -2
  35. package/dist/modules/customers/components/detail/ActivitiesSection.js +37 -5
  36. package/dist/modules/customers/components/detail/ActivitiesSection.js.map +2 -2
  37. package/dist/modules/customers/components/detail/ActivityCard.js +69 -17
  38. package/dist/modules/customers/components/detail/ActivityCard.js.map +2 -2
  39. package/dist/modules/customers/components/detail/ActivityHistorySection.js +94 -34
  40. package/dist/modules/customers/components/detail/ActivityHistorySection.js.map +2 -2
  41. package/dist/modules/customers/components/detail/ActivityLogTab.js +3 -1
  42. package/dist/modules/customers/components/detail/ActivityLogTab.js.map +2 -2
  43. package/dist/modules/customers/components/detail/ActivityTimeline.js +41 -8
  44. package/dist/modules/customers/components/detail/ActivityTimeline.js.map +2 -2
  45. package/dist/modules/customers/components/detail/ActivityTimelineFilters.js +19 -6
  46. package/dist/modules/customers/components/detail/ActivityTimelineFilters.js.map +2 -2
  47. package/dist/modules/customers/components/detail/ActivityTypeSelector.js +4 -3
  48. package/dist/modules/customers/components/detail/ActivityTypeSelector.js.map +2 -2
  49. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +80 -12
  50. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
  51. package/dist/modules/customers/components/detail/schedule/DateTimeFields.js +65 -10
  52. package/dist/modules/customers/components/detail/schedule/DateTimeFields.js.map +2 -2
  53. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js +10 -5
  54. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
  55. package/dist/modules/customers/data/validators.js +74 -2
  56. package/dist/modules/customers/data/validators.js.map +2 -2
  57. package/dist/modules/customers/lib/legacyActivityBridge.js +61 -0
  58. package/dist/modules/customers/lib/legacyActivityBridge.js.map +7 -0
  59. package/dist/modules/integrations/data/validators.js +2 -2
  60. package/dist/modules/integrations/data/validators.js.map +2 -2
  61. package/dist/modules/integrations/lib/credentials-service.js +12 -1
  62. package/dist/modules/integrations/lib/credentials-service.js.map +2 -2
  63. package/dist/modules/messages/commands/actions.js +29 -14
  64. package/dist/modules/messages/commands/actions.js.map +2 -2
  65. package/dist/modules/messages/lib/actions.js +24 -4
  66. package/dist/modules/messages/lib/actions.js.map +2 -2
  67. package/dist/modules/sales/api/documents/factory.js +49 -36
  68. package/dist/modules/sales/api/documents/factory.js.map +2 -2
  69. package/package.json +9 -10
  70. package/src/modules/api_keys/api/keys/route.ts +9 -0
  71. package/src/modules/audit_logs/services/accessLogService.ts +20 -0
  72. package/src/modules/audit_logs/services/actionLogService.ts +13 -5
  73. package/src/modules/auth/api/roles/acl/route.ts +32 -46
  74. package/src/modules/auth/api/users/route.ts +48 -33
  75. package/src/modules/auth/lib/grantChecks.ts +234 -0
  76. package/src/modules/configs/cli.ts +11 -0
  77. package/src/modules/configs/lib/touchGeneratedBarrels.ts +61 -0
  78. package/src/modules/customers/api/activities/route.ts +1 -76
  79. package/src/modules/customers/api/interactions/counts/route.ts +2 -1
  80. package/src/modules/customers/api/interactions/route.ts +28 -1
  81. package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +13 -3
  82. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +14 -2
  83. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +13 -3
  84. package/src/modules/customers/components/detail/ActivitiesCard.tsx +92 -5
  85. package/src/modules/customers/components/detail/ActivitiesDayStrip.tsx +38 -6
  86. package/src/modules/customers/components/detail/ActivitiesSection.tsx +37 -3
  87. package/src/modules/customers/components/detail/ActivityCard.tsx +79 -14
  88. package/src/modules/customers/components/detail/ActivityHistorySection.tsx +102 -33
  89. package/src/modules/customers/components/detail/ActivityLogTab.tsx +7 -1
  90. package/src/modules/customers/components/detail/ActivityTimeline.tsx +39 -5
  91. package/src/modules/customers/components/detail/ActivityTimelineFilters.tsx +29 -7
  92. package/src/modules/customers/components/detail/ActivityTypeSelector.tsx +3 -2
  93. package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +96 -13
  94. package/src/modules/customers/components/detail/schedule/DateTimeFields.tsx +50 -4
  95. package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +21 -5
  96. package/src/modules/customers/data/validators.ts +85 -2
  97. package/src/modules/customers/i18n/de.json +11 -0
  98. package/src/modules/customers/i18n/en.json +11 -0
  99. package/src/modules/customers/i18n/es.json +11 -0
  100. package/src/modules/customers/i18n/pl.json +11 -0
  101. package/src/modules/customers/lib/legacyActivityBridge.ts +106 -0
  102. package/src/modules/integrations/data/validators.ts +8 -6
  103. package/src/modules/integrations/lib/credentials-service.ts +15 -1
  104. package/src/modules/messages/commands/actions.ts +28 -13
  105. package/src/modules/messages/lib/actions.ts +34 -3
  106. package/src/modules/sales/api/documents/factory.ts +55 -38
@@ -1,7 +1,11 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
- import { Calendar, ExternalLink, Mail, MoreHorizontal, Phone, StickyNote, Users } from "lucide-react";
3
+ import * as React from "react";
4
+ import { Calendar, Check, ExternalLink, ListTodo, Mail, MoreHorizontal, Phone, StickyNote, Users } from "lucide-react";
5
+ import { Button } from "@open-mercato/ui/primitives/button";
4
6
  import { IconButton } from "@open-mercato/ui/primitives/icon-button";
7
+ import { flash } from "@open-mercato/ui/backend/FlashMessages";
8
+ import { apiCallOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
5
9
  import { useT } from "@open-mercato/shared/lib/i18n/context";
6
10
  import { cn } from "@open-mercato/shared/lib/utils";
7
11
  import { ActivityAiActions } from "./ActivityAiActions.js";
@@ -10,7 +14,8 @@ const TYPE_ICONS = {
10
14
  call: Phone,
11
15
  email: Mail,
12
16
  meeting: Users,
13
- note: StickyNote
17
+ note: StickyNote,
18
+ task: ListTodo
14
19
  };
15
20
  function formatDayLabel(value, t) {
16
21
  const date = new Date(value);
@@ -41,7 +46,7 @@ function resolveTarget(activity) {
41
46
  if (activity.customer?.displayName) return activity.customer.displayName;
42
47
  return null;
43
48
  }
44
- function ActivityCard({ activity, onOpen }) {
49
+ function ActivityCard({ activity, onOpen, onChanged, runMutation }) {
45
50
  const t = useT();
46
51
  const timestamp = activity.occurredAt ?? activity.scheduledAt ?? activity.createdAt;
47
52
  const TypeIcon = TYPE_ICONS[activity.interactionType] ?? StickyNote;
@@ -52,6 +57,34 @@ function ActivityCard({ activity, onOpen }) {
52
57
  const target = resolveTarget(activity);
53
58
  const direction = activity.interactionType === "email" ? t("customers.activityLog.direction.to", "to") : activity.interactionType === "call" || activity.interactionType === "meeting" ? t("customers.activityLog.direction.with", "with") : "";
54
59
  const showExternalLink = Boolean(activity._integrations && Object.keys(activity._integrations).length > 0);
60
+ const [markingDone, setMarkingDone] = React.useState(false);
61
+ const handleMarkDone = React.useCallback(async () => {
62
+ if (markingDone) return;
63
+ setMarkingDone(true);
64
+ try {
65
+ const operation = () => apiCallOrThrow("/api/customers/interactions/complete", {
66
+ method: "POST",
67
+ headers: { "content-type": "application/json" },
68
+ body: JSON.stringify({ id: activity.id, occurredAt: (/* @__PURE__ */ new Date()).toISOString() })
69
+ });
70
+ if (runMutation) {
71
+ await runMutation(operation, {
72
+ id: activity.id,
73
+ status: "done",
74
+ operation: "completeActivity"
75
+ });
76
+ } else {
77
+ await operation();
78
+ }
79
+ flash(t("customers.activities.actions.markDoneSuccess", "Activity marked done"), "success");
80
+ onChanged?.();
81
+ } catch (err) {
82
+ console.warn("[customers.activityCard] mark done failed", activity.id, err);
83
+ flash(t("customers.activities.actions.markDoneError", "Could not mark activity as done"), "error");
84
+ } finally {
85
+ setMarkingDone(false);
86
+ }
87
+ }, [activity.id, markingDone, onChanged, runMutation, t]);
55
88
  return /* @__PURE__ */ jsxs(
56
89
  "div",
57
90
  {
@@ -87,20 +120,39 @@ function ActivityCard({ activity, onOpen }) {
87
120
  /* @__PURE__ */ jsx("span", { className: "truncate", children: activity.location })
88
121
  ] }) : null
89
122
  ] }),
90
- /* @__PURE__ */ jsx(
91
- IconButton,
92
- {
93
- type: "button",
94
- variant: "ghost",
95
- size: "sm",
96
- "aria-label": t("customers.timeline.more", "More"),
97
- onClick: (event) => {
98
- event.stopPropagation();
99
- onOpen?.(activity);
100
- },
101
- children: /* @__PURE__ */ jsx(MoreHorizontal, { className: "size-4" })
102
- }
103
- )
123
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
124
+ activity.status === "planned" ? /* @__PURE__ */ jsxs(
125
+ Button,
126
+ {
127
+ type: "button",
128
+ variant: "default",
129
+ size: "sm",
130
+ disabled: markingDone,
131
+ onClick: (event) => {
132
+ event.stopPropagation();
133
+ void handleMarkDone();
134
+ },
135
+ children: [
136
+ /* @__PURE__ */ jsx(Check, { className: "size-3.5" }),
137
+ t("customers.activities.actions.markDone", "Mark done")
138
+ ]
139
+ }
140
+ ) : null,
141
+ /* @__PURE__ */ jsx(
142
+ IconButton,
143
+ {
144
+ type: "button",
145
+ variant: "ghost",
146
+ size: "sm",
147
+ "aria-label": t("customers.timeline.more", "More"),
148
+ onClick: (event) => {
149
+ event.stopPropagation();
150
+ onOpen?.(activity);
151
+ },
152
+ children: /* @__PURE__ */ jsx(MoreHorizontal, { className: "size-4" })
153
+ }
154
+ )
155
+ ] })
104
156
  ] }),
105
157
  /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx(ActivityAiActions, { activityType: activity.interactionType }) }),
106
158
  snippet ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: snippet }) : null,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/components/detail/ActivityCard.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { Calendar, ExternalLink, Mail, MoreHorizontal, Phone, StickyNote, Users } from 'lucide-react'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport type { InteractionSummary } from './types'\nimport { ActivityAiActions } from './ActivityAiActions'\nimport { getInitials } from './utils'\n\ntype ActivityCardProps = {\n activity: InteractionSummary\n onOpen?: (activity: InteractionSummary) => void\n}\n\nconst TYPE_ICONS: Record<string, React.ComponentType<{ className?: string }>> = {\n call: Phone,\n email: Mail,\n meeting: Users,\n note: StickyNote,\n}\n\nfunction formatDayLabel(value: string, t: ReturnType<typeof useT>): string {\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return ''\n const now = new Date()\n const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime()\n const day = new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime()\n const diffDays = Math.round((today - day) / 86400000)\n if (diffDays === 0) return t('customers.timeline.date.today', 'today')\n if (diffDays === 1) return t('customers.timeline.date.yesterday', 'yesterday')\n return date.toLocaleDateString(undefined, { day: 'numeric', month: 'short' })\n}\n\nfunction formatTimeLabel(value: string): string {\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return ''\n return date.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' })\n}\n\nfunction trimSnippet(value: string | null | undefined): string | null {\n const normalized = value?.trim()\n if (!normalized) return null\n if (normalized.length <= 200) return normalized\n return `${normalized.slice(0, 197)}...`\n}\n\nfunction resolveTarget(activity: InteractionSummary): string | null {\n const participant = activity.participants?.find((item) => item.name || item.email)\n if (participant?.name) return participant.name\n if (participant?.email) return participant.email\n if (activity.customer?.displayName) return activity.customer.displayName\n return null\n}\n\nexport function ActivityCard({ activity, onOpen }: ActivityCardProps) {\n const t = useT()\n const timestamp = activity.occurredAt ?? activity.scheduledAt ?? activity.createdAt\n const TypeIcon = TYPE_ICONS[activity.interactionType] ?? StickyNote\n const titleBase = activity.title ?? activity.body ?? activity.interactionType\n const title = activity.duration ? `${titleBase} (${activity.duration} min)` : titleBase\n const snippet = trimSnippet(activity.body && activity.title ? activity.body : activity.body ?? null)\n const actorLabel = activity.authorName ?? activity.authorEmail ?? t('customers.changelog.user.system', 'System')\n const target = resolveTarget(activity)\n const direction = activity.interactionType === 'email'\n ? t('customers.activityLog.direction.to', 'to')\n : activity.interactionType === 'call' || activity.interactionType === 'meeting'\n ? t('customers.activityLog.direction.with', 'with')\n : ''\n const showExternalLink = Boolean(activity._integrations && Object.keys(activity._integrations).length > 0)\n\n return (\n <div\n className={cn(\n 'grid gap-3',\n onOpen ? 'cursor-pointer' : '',\n )}\n style={{ gridTemplateColumns: '64px 36px minmax(0,1fr)' }}\n onClick={() => onOpen?.(activity)}\n role={onOpen ? 'button' : undefined}\n tabIndex={onOpen ? 0 : undefined}\n onKeyDown={onOpen ? (event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n onOpen(activity)\n }\n } : undefined}\n >\n <div className=\"pt-1 text-xs text-muted-foreground\">\n <div className=\"font-semibold text-foreground\">{formatDayLabel(timestamp, t)}</div>\n <div>{formatTimeLabel(timestamp)}</div>\n </div>\n\n <div className=\"flex size-9 items-center justify-center rounded-lg bg-muted/80\">\n <TypeIcon className=\"size-4 text-muted-foreground\" />\n </div>\n\n <div className=\"rounded-xl border bg-card px-4 py-3 shadow-sm\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-1.5\">\n <h4 className=\"truncate text-sm font-semibold text-foreground\">{title}</h4>\n {showExternalLink ? <ExternalLink className=\"size-3.5 text-muted-foreground\" /> : null}\n </div>\n {activity.location ? (\n <div className=\"mt-1 flex items-center gap-1 text-xs text-muted-foreground\">\n <Calendar className=\"size-3.5\" />\n <span className=\"truncate\">{activity.location}</span>\n </div>\n ) : null}\n </div>\n\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('customers.timeline.more', 'More')}\n onClick={(event) => {\n event.stopPropagation()\n onOpen?.(activity)\n }}\n >\n <MoreHorizontal className=\"size-4\" />\n </IconButton>\n </div>\n\n <div className=\"mt-2\">\n <ActivityAiActions activityType={activity.interactionType} />\n </div>\n\n {snippet ? (\n <p className=\"mt-2 text-sm text-muted-foreground\">{snippet}</p>\n ) : null}\n\n <div className=\"mt-3 flex flex-wrap items-center gap-1.5 text-xs text-muted-foreground\">\n <span className=\"inline-flex size-5 items-center justify-center rounded-full bg-muted text-xs font-semibold text-foreground\">\n {getInitials(actorLabel)}\n </span>\n <span className=\"font-medium text-foreground\">{actorLabel}</span>\n {target && direction ? (\n <>\n <span>\u00B7</span>\n <span>{direction}</span>\n <span className=\"text-foreground\">{target}</span>\n </>\n ) : null}\n </div>\n </div>\n </div>\n )\n}\n\nexport default ActivityCard\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { Calendar, Check, ExternalLink, ListTodo, Mail, MoreHorizontal, Phone, StickyNote, Users } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport type { InteractionSummary } from './types'\nimport { ActivityAiActions } from './ActivityAiActions'\nimport { getInitials } from './utils'\n\ntype GuardedMutationRunner = <T,>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n) => Promise<T>\n\ntype ActivityCardProps = {\n activity: InteractionSummary\n onOpen?: (activity: InteractionSummary) => void\n /** Called after a successful mark-done so the parent can refresh the timeline. */\n onChanged?: () => void\n /**\n * Optional guarded-mutation runner. When provided, mutations route through the parent's\n * `useGuardedMutation` so retry-last-mutation and the global injection contract apply.\n * When omitted, mutations run directly via `apiCallOrThrow` (e.g. read-only contexts\n * or jest unit tests that don't supply a guarded runner).\n */\n runMutation?: GuardedMutationRunner\n}\n\nconst TYPE_ICONS: Record<string, React.ComponentType<{ className?: string }>> = {\n call: Phone,\n email: Mail,\n meeting: Users,\n note: StickyNote,\n task: ListTodo,\n}\n\nfunction formatDayLabel(value: string, t: ReturnType<typeof useT>): string {\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return ''\n const now = new Date()\n const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime()\n const day = new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime()\n const diffDays = Math.round((today - day) / 86400000)\n if (diffDays === 0) return t('customers.timeline.date.today', 'today')\n if (diffDays === 1) return t('customers.timeline.date.yesterday', 'yesterday')\n return date.toLocaleDateString(undefined, { day: 'numeric', month: 'short' })\n}\n\nfunction formatTimeLabel(value: string): string {\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return ''\n return date.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' })\n}\n\nfunction trimSnippet(value: string | null | undefined): string | null {\n const normalized = value?.trim()\n if (!normalized) return null\n if (normalized.length <= 200) return normalized\n return `${normalized.slice(0, 197)}...`\n}\n\nfunction resolveTarget(activity: InteractionSummary): string | null {\n const participant = activity.participants?.find((item) => item.name || item.email)\n if (participant?.name) return participant.name\n if (participant?.email) return participant.email\n if (activity.customer?.displayName) return activity.customer.displayName\n return null\n}\n\nexport function ActivityCard({ activity, onOpen, onChanged, runMutation }: ActivityCardProps) {\n const t = useT()\n const timestamp = activity.occurredAt ?? activity.scheduledAt ?? activity.createdAt\n const TypeIcon = TYPE_ICONS[activity.interactionType] ?? StickyNote\n const titleBase = activity.title ?? activity.body ?? activity.interactionType\n const title = activity.duration ? `${titleBase} (${activity.duration} min)` : titleBase\n const snippet = trimSnippet(activity.body && activity.title ? activity.body : activity.body ?? null)\n const actorLabel = activity.authorName ?? activity.authorEmail ?? t('customers.changelog.user.system', 'System')\n const target = resolveTarget(activity)\n const direction = activity.interactionType === 'email'\n ? t('customers.activityLog.direction.to', 'to')\n : activity.interactionType === 'call' || activity.interactionType === 'meeting'\n ? t('customers.activityLog.direction.with', 'with')\n : ''\n const showExternalLink = Boolean(activity._integrations && Object.keys(activity._integrations).length > 0)\n const [markingDone, setMarkingDone] = React.useState(false)\n\n const handleMarkDone = React.useCallback(async () => {\n if (markingDone) return\n setMarkingDone(true)\n try {\n const operation = () =>\n apiCallOrThrow('/api/customers/interactions/complete', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: activity.id, occurredAt: new Date().toISOString() }),\n })\n if (runMutation) {\n await runMutation(operation, {\n id: activity.id,\n status: 'done',\n operation: 'completeActivity',\n })\n } else {\n await operation()\n }\n flash(t('customers.activities.actions.markDoneSuccess', 'Activity marked done'), 'success')\n onChanged?.()\n } catch (err) {\n console.warn('[customers.activityCard] mark done failed', activity.id, err)\n flash(t('customers.activities.actions.markDoneError', 'Could not mark activity as done'), 'error')\n } finally {\n setMarkingDone(false)\n }\n }, [activity.id, markingDone, onChanged, runMutation, t])\n\n return (\n <div\n className={cn(\n 'grid gap-3',\n onOpen ? 'cursor-pointer' : '',\n )}\n style={{ gridTemplateColumns: '64px 36px minmax(0,1fr)' }}\n onClick={() => onOpen?.(activity)}\n role={onOpen ? 'button' : undefined}\n tabIndex={onOpen ? 0 : undefined}\n onKeyDown={onOpen ? (event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n onOpen(activity)\n }\n } : undefined}\n >\n <div className=\"pt-1 text-xs text-muted-foreground\">\n <div className=\"font-semibold text-foreground\">{formatDayLabel(timestamp, t)}</div>\n <div>{formatTimeLabel(timestamp)}</div>\n </div>\n\n <div className=\"flex size-9 items-center justify-center rounded-lg bg-muted/80\">\n <TypeIcon className=\"size-4 text-muted-foreground\" />\n </div>\n\n <div className=\"rounded-xl border bg-card px-4 py-3 shadow-sm\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-1.5\">\n <h4 className=\"truncate text-sm font-semibold text-foreground\">{title}</h4>\n {showExternalLink ? <ExternalLink className=\"size-3.5 text-muted-foreground\" /> : null}\n </div>\n {activity.location ? (\n <div className=\"mt-1 flex items-center gap-1 text-xs text-muted-foreground\">\n <Calendar className=\"size-3.5\" />\n <span className=\"truncate\">{activity.location}</span>\n </div>\n ) : null}\n </div>\n\n <div className=\"flex items-center gap-1.5\">\n {activity.status === 'planned' ? (\n <Button\n type=\"button\"\n variant=\"default\"\n size=\"sm\"\n disabled={markingDone}\n onClick={(event) => {\n event.stopPropagation()\n void handleMarkDone()\n }}\n >\n <Check className=\"size-3.5\" />\n {t('customers.activities.actions.markDone', 'Mark done')}\n </Button>\n ) : null}\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('customers.timeline.more', 'More')}\n onClick={(event) => {\n event.stopPropagation()\n onOpen?.(activity)\n }}\n >\n <MoreHorizontal className=\"size-4\" />\n </IconButton>\n </div>\n </div>\n\n <div className=\"mt-2\">\n <ActivityAiActions activityType={activity.interactionType} />\n </div>\n\n {snippet ? (\n <p className=\"mt-2 text-sm text-muted-foreground\">{snippet}</p>\n ) : null}\n\n <div className=\"mt-3 flex flex-wrap items-center gap-1.5 text-xs text-muted-foreground\">\n <span className=\"inline-flex size-5 items-center justify-center rounded-full bg-muted text-xs font-semibold text-foreground\">\n {getInitials(actorLabel)}\n </span>\n <span className=\"font-medium text-foreground\">{actorLabel}</span>\n {target && direction ? (\n <>\n <span>\u00B7</span>\n <span>{direction}</span>\n <span className=\"text-foreground\">{target}</span>\n </>\n ) : null}\n </div>\n </div>\n </div>\n )\n}\n\nexport default ActivityCard\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -5,6 +5,13 @@ import { Clock3, Search } from "lucide-react";
5
5
  import { useT } from "@open-mercato/shared/lib/i18n/context";
6
6
  import { Button } from "@open-mercato/ui/primitives/button";
7
7
  import { Input } from "@open-mercato/ui/primitives/input";
8
+ import {
9
+ Select,
10
+ SelectContent,
11
+ SelectItem,
12
+ SelectTrigger,
13
+ SelectValue
14
+ } from "@open-mercato/ui/primitives/select";
8
15
  import { ErrorMessage, LoadingMessage, TabEmptyState } from "@open-mercato/ui/backend/detail";
9
16
  import { readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
10
17
  import { ActivityCard } from "./ActivityCard.js";
@@ -12,7 +19,8 @@ const TYPE_FILTERS = [
12
19
  { value: "call", labelKey: "customers.timeline.filter.call", fallback: "Call" },
13
20
  { value: "email", labelKey: "customers.timeline.filter.email", fallback: "Email" },
14
21
  { value: "meeting", labelKey: "customers.timeline.filter.meeting", fallback: "Meeting" },
15
- { value: "note", labelKey: "customers.timeline.filter.note", fallback: "Note" }
22
+ { value: "note", labelKey: "customers.timeline.filter.note", fallback: "Note" },
23
+ { value: "task", labelKey: "customers.timeline.filter.task", fallback: "Task" }
16
24
  ];
17
25
  function computeRangeStart(range) {
18
26
  const date = /* @__PURE__ */ new Date();
@@ -92,11 +100,15 @@ function isWithinRange(activity, start) {
92
100
  if (Number.isNaN(timestamp.getTime())) return false;
93
101
  return timestamp >= start;
94
102
  }
103
+ function isAbortError(error) {
104
+ return typeof error === "object" && error !== null && "name" in error && error.name === "AbortError";
105
+ }
95
106
  function ActivityHistorySection({
96
107
  entityId,
97
108
  useCanonicalInteractions = false,
98
109
  refreshKey = 0,
99
- onEditActivity
110
+ onEditActivity,
111
+ runMutation
100
112
  }) {
101
113
  const t = useT();
102
114
  const [searchInput, setSearchInput] = React.useState("");
@@ -107,9 +119,14 @@ function ActivityHistorySection({
107
119
  const [activities, setActivities] = React.useState([]);
108
120
  const [loading, setLoading] = React.useState(true);
109
121
  const [error, setError] = React.useState(null);
110
- const [counts, setCounts] = React.useState({ call: 0, email: 0, meeting: 0, note: 0, total: 0 });
122
+ const [counts, setCounts] = React.useState({ call: 0, email: 0, meeting: 0, note: 0, task: 0, total: 0 });
111
123
  const [hasMore, setHasMore] = React.useState(false);
112
124
  const [loadedPages, setLoadedPages] = React.useState(1);
125
+ const [localRefreshKey, setLocalRefreshKey] = React.useState(0);
126
+ const historyRequestSeqRef = React.useRef(0);
127
+ const handleActivityChanged = React.useCallback(() => {
128
+ setLocalRefreshKey((current) => current + 1);
129
+ }, []);
113
130
  React.useEffect(() => {
114
131
  const timeout = window.setTimeout(() => setSearch(searchInput.trim()), 300);
115
132
  return () => window.clearTimeout(timeout);
@@ -119,7 +136,7 @@ function ActivityHistorySection({
119
136
  void (async () => {
120
137
  try {
121
138
  const payload = await readApiResultOrThrow(
122
- `/api/customers/interactions/counts?entityId=${encodeURIComponent(entityId)}&status=done`,
139
+ `/api/customers/interactions/counts?entityId=${encodeURIComponent(entityId)}`,
123
140
  { signal: controller.signal }
124
141
  );
125
142
  const result = payload.result ?? payload;
@@ -128,15 +145,18 @@ function ActivityHistorySection({
128
145
  email: result.email ?? 0,
129
146
  meeting: result.meeting ?? 0,
130
147
  note: result.note ?? 0,
148
+ task: result.task ?? 0,
131
149
  total: result.total ?? 0
132
150
  });
133
151
  } catch {
134
- setCounts({ call: 0, email: 0, meeting: 0, note: 0, total: 0 });
152
+ setCounts({ call: 0, email: 0, meeting: 0, note: 0, task: 0, total: 0 });
135
153
  }
136
154
  })();
137
155
  return () => controller.abort();
138
- }, [entityId, refreshKey]);
139
- const loadHistory = React.useCallback(async () => {
156
+ }, [entityId, refreshKey, localRefreshKey]);
157
+ const loadHistory = React.useCallback(async (options) => {
158
+ const { signal, requestSeq } = options;
159
+ const isStale = () => signal.aborted || requestSeq !== historyRequestSeqRef.current;
140
160
  setLoading(true);
141
161
  setError(null);
142
162
  try {
@@ -146,14 +166,14 @@ function ActivityHistorySection({
146
166
  let nextCursor;
147
167
  let firstPageHasMore = false;
148
168
  let pagesLoaded = 0;
169
+ const taskFilterActive = activeTypes.includes("task");
149
170
  do {
150
171
  const params = new URLSearchParams({
151
172
  entityId,
152
- status: "done",
153
- excludeInteractionType: "task",
154
173
  limit: String(pageSize),
155
174
  from: rangeStart
156
175
  });
176
+ if (!taskFilterActive) params.set("excludeInteractionType", "task");
157
177
  if (activeTypes.length > 0) params.set("type", activeTypes.join(","));
158
178
  if (search) params.set("search", search);
159
179
  if (sortMode === "recent") {
@@ -165,8 +185,10 @@ function ActivityHistorySection({
165
185
  }
166
186
  if (nextCursor) params.set("cursor", nextCursor);
167
187
  const response = await readApiResultOrThrow(
168
- `/api/customers/interactions?${params.toString()}`
188
+ `/api/customers/interactions?${params.toString()}`,
189
+ { signal }
169
190
  );
191
+ if (isStale()) return;
170
192
  const pageItems = Array.isArray(response.items) ? response.items : [];
171
193
  canonicalItems.push(...pageItems);
172
194
  nextCursor = response.nextCursor;
@@ -179,8 +201,10 @@ function ActivityHistorySection({
179
201
  let legacyTotalPages = 1;
180
202
  for (let legacyPage = 1; legacyPage <= loadedPages; legacyPage += 1) {
181
203
  const legacyPayload = await readApiResultOrThrow(
182
- `/api/customers/activities?entityId=${encodeURIComponent(entityId)}&page=${legacyPage}&pageSize=20&sortField=occurredAt&sortDir=desc`
204
+ `/api/customers/activities?entityId=${encodeURIComponent(entityId)}&page=${legacyPage}&pageSize=20&sortField=occurredAt&sortDir=desc`,
205
+ { signal }
183
206
  ).catch(() => ({ items: [], totalPages: 1 }));
207
+ if (isStale()) return;
184
208
  legacyItems.push(...Array.isArray(legacyPayload.items) ? legacyPayload.items.map(normalizeLegacyActivity) : []);
185
209
  legacyTotalPages = typeof legacyPayload.totalPages === "number" ? legacyPayload.totalPages : legacyTotalPages;
186
210
  if (legacyPage >= legacyTotalPages) break;
@@ -198,19 +222,27 @@ function ActivityHistorySection({
198
222
  combined = Array.from(deduped.values());
199
223
  firstPageHasMore = firstPageHasMore || legacyTotalPages > loadedPages;
200
224
  }
201
- setActivities(sortActivities(combined, sortMode));
202
- setHasMore(firstPageHasMore);
225
+ if (!isStale()) {
226
+ setActivities(sortActivities(combined, sortMode));
227
+ setHasMore(firstPageHasMore);
228
+ }
203
229
  } catch (loadError) {
204
- setActivities([]);
205
- setHasMore(false);
206
- setError(t("customers.activityLog.error", "Failed to load activity history"));
230
+ if (!isStale() && !isAbortError(loadError)) {
231
+ setActivities([]);
232
+ setHasMore(false);
233
+ setError(t("customers.activityLog.error", "Failed to load activity history"));
234
+ }
207
235
  } finally {
208
- setLoading(false);
236
+ if (!isStale()) setLoading(false);
209
237
  }
210
238
  }, [activeTypes, dateRange, entityId, loadedPages, search, sortMode, t, useCanonicalInteractions]);
211
239
  React.useEffect(() => {
212
- void loadHistory();
213
- }, [loadHistory, refreshKey]);
240
+ const controller = new AbortController();
241
+ const requestSeq = historyRequestSeqRef.current + 1;
242
+ historyRequestSeqRef.current = requestSeq;
243
+ void loadHistory({ signal: controller.signal, requestSeq });
244
+ return () => controller.abort();
245
+ }, [loadHistory, refreshKey, localRefreshKey]);
214
246
  React.useEffect(() => {
215
247
  setLoadedPages(1);
216
248
  }, [activeTypes, dateRange, entityId, search, sortMode, useCanonicalInteractions]);
@@ -272,32 +304,52 @@ function ActivityHistorySection({
272
304
  );
273
305
  }),
274
306
  /* @__PURE__ */ jsxs(
275
- "select",
307
+ Select,
276
308
  {
277
309
  value: dateRange,
278
- onChange: (event) => {
279
- setDateRange(event.target.value);
310
+ onValueChange: (value) => {
311
+ setDateRange(value);
280
312
  },
281
- className: "h-8 rounded-lg border bg-background px-3 text-xs outline-none ring-offset-background focus:ring-2 focus:ring-ring",
282
313
  children: [
283
- /* @__PURE__ */ jsx("option", { value: "7d", children: t("customers.changelog.last7days", "Last 7 days") }),
284
- /* @__PURE__ */ jsx("option", { value: "30d", children: t("customers.changelog.last30days", "Last 30 days") }),
285
- /* @__PURE__ */ jsx("option", { value: "90d", children: t("customers.changelog.last90days", "Last 90 days") })
314
+ /* @__PURE__ */ jsx(
315
+ SelectTrigger,
316
+ {
317
+ size: "sm",
318
+ "aria-label": t("customers.activityLog.filters.dateRangeLabel", "Date range"),
319
+ className: "w-auto",
320
+ children: /* @__PURE__ */ jsx(SelectValue, {})
321
+ }
322
+ ),
323
+ /* @__PURE__ */ jsxs(SelectContent, { children: [
324
+ /* @__PURE__ */ jsx(SelectItem, { value: "7d", children: t("customers.changelog.last7days", "Last 7 days") }),
325
+ /* @__PURE__ */ jsx(SelectItem, { value: "30d", children: t("customers.changelog.last30days", "Last 30 days") }),
326
+ /* @__PURE__ */ jsx(SelectItem, { value: "90d", children: t("customers.changelog.last90days", "Last 90 days") })
327
+ ] })
286
328
  ]
287
329
  }
288
330
  ),
289
331
  /* @__PURE__ */ jsxs(
290
- "select",
332
+ Select,
291
333
  {
292
334
  value: sortMode,
293
- onChange: (event) => {
294
- setSortMode(event.target.value);
335
+ onValueChange: (value) => {
336
+ setSortMode(value);
295
337
  },
296
- className: "h-8 rounded-lg border bg-background px-3 text-xs outline-none ring-offset-background focus:ring-2 focus:ring-ring",
297
338
  children: [
298
- /* @__PURE__ */ jsx("option", { value: "recent", children: t("customers.activityLog.sort.recent", "Sort: newest") }),
299
- /* @__PURE__ */ jsx("option", { value: "title-asc", children: t("customers.activityLog.sort.titleAsc", "Sort: Name A-Z") }),
300
- /* @__PURE__ */ jsx("option", { value: "title-desc", children: t("customers.activityLog.sort.titleDesc", "Sort: Name Z-A") })
339
+ /* @__PURE__ */ jsx(
340
+ SelectTrigger,
341
+ {
342
+ size: "sm",
343
+ "aria-label": t("customers.activityLog.filters.sortLabel", "Sort order"),
344
+ className: "w-auto",
345
+ children: /* @__PURE__ */ jsx(SelectValue, {})
346
+ }
347
+ ),
348
+ /* @__PURE__ */ jsxs(SelectContent, { children: [
349
+ /* @__PURE__ */ jsx(SelectItem, { value: "recent", children: t("customers.activityLog.sort.recent", "Sort: newest") }),
350
+ /* @__PURE__ */ jsx(SelectItem, { value: "title-asc", children: t("customers.activityLog.sort.titleAsc", "Sort: Name A-Z") }),
351
+ /* @__PURE__ */ jsx(SelectItem, { value: "title-desc", children: t("customers.activityLog.sort.titleDesc", "Sort: Name Z-A") })
352
+ ] })
301
353
  ]
302
354
  }
303
355
  )
@@ -324,7 +376,15 @@ function ActivityHistorySection({
324
376
  ] }),
325
377
  /* @__PURE__ */ jsx("div", { className: "h-px flex-1 bg-border" })
326
378
  ] }) : null,
327
- /* @__PURE__ */ jsx(ActivityCard, { activity, onOpen: onEditActivity })
379
+ /* @__PURE__ */ jsx(
380
+ ActivityCard,
381
+ {
382
+ activity,
383
+ onOpen: onEditActivity,
384
+ onChanged: handleActivityChanged,
385
+ runMutation
386
+ }
387
+ )
328
388
  ] }, activity.id);
329
389
  }),
330
390
  hasMore ? /* @__PURE__ */ jsx("div", { className: "pt-2 text-center", children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "link", size: "sm", onClick: handleLoadMore, className: "text-sm", children: t("customers.activities.loadMore", "Load more") }) }) : null
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/components/detail/ActivityHistorySection.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { Clock3, Search } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { ErrorMessage, LoadingMessage, TabEmptyState } from '@open-mercato/ui/backend/detail'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport type { ActivitySummary, InteractionSummary } from './types'\nimport { ActivityCard } from './ActivityCard'\n\ntype ActivityHistorySectionProps = {\n entityId: string\n useCanonicalInteractions?: boolean\n refreshKey?: number\n onEditActivity?: (activity: InteractionSummary) => void\n}\n\ntype InteractionListResponse = {\n items?: InteractionSummary[]\n nextCursor?: string\n}\n\ntype InteractionCountsResponse = {\n ok?: boolean\n result?: {\n call: number\n email: number\n meeting: number\n note: number\n total: number\n }\n call?: number\n email?: number\n meeting?: number\n note?: number\n total?: number\n}\n\nconst TYPE_FILTERS = [\n { value: 'call', labelKey: 'customers.timeline.filter.call', fallback: 'Call' },\n { value: 'email', labelKey: 'customers.timeline.filter.email', fallback: 'Email' },\n { value: 'meeting', labelKey: 'customers.timeline.filter.meeting', fallback: 'Meeting' },\n { value: 'note', labelKey: 'customers.timeline.filter.note', fallback: 'Note' },\n] as const\n\nfunction computeRangeStart(range: '7d' | '30d' | '90d'): Date {\n const date = new Date()\n date.setHours(0, 0, 0, 0)\n const days = Number.parseInt(range.replace('d', ''), 10) || 30\n date.setDate(date.getDate() - days)\n return date\n}\n\nfunction toTimelineTimestamp(activity: InteractionSummary): string {\n return activity.occurredAt ?? activity.scheduledAt ?? activity.createdAt\n}\n\nfunction normalizeLegacyActivity(activity: ActivitySummary): InteractionSummary {\n return {\n id: activity.id,\n interactionType: activity.activityType,\n title: activity.subject ?? null,\n body: activity.body ?? null,\n status: 'done',\n scheduledAt: null,\n occurredAt: activity.occurredAt ?? null,\n priority: null,\n authorUserId: activity.authorUserId ?? null,\n ownerUserId: null,\n appearanceIcon: activity.appearanceIcon ?? null,\n appearanceColor: activity.appearanceColor ?? null,\n source: 'legacy-activity',\n entityId: activity.entityId ?? null,\n dealId: activity.dealId ?? null,\n organizationId: null,\n tenantId: null,\n authorName: activity.authorName ?? null,\n authorEmail: activity.authorEmail ?? null,\n dealTitle: activity.dealTitle ?? null,\n customValues: activity.customValues ?? null,\n createdAt: activity.createdAt,\n updatedAt: activity.createdAt,\n }\n}\n\nfunction sortTimelineActivities(items: InteractionSummary[]): InteractionSummary[] {\n const now = Date.now()\n return [...items].sort((left, right) => {\n const leftScheduled = left.scheduledAt ? new Date(left.scheduledAt).getTime() : Number.NaN\n const rightScheduled = right.scheduledAt ? new Date(right.scheduledAt).getTime() : Number.NaN\n const leftIsPlanned = left.status === 'planned' && Number.isFinite(leftScheduled)\n const rightIsPlanned = right.status === 'planned' && Number.isFinite(rightScheduled)\n const leftIsUpcoming = leftIsPlanned && leftScheduled >= now\n const rightIsUpcoming = rightIsPlanned && rightScheduled >= now\n\n if (leftIsUpcoming !== rightIsUpcoming) return leftIsUpcoming ? -1 : 1\n if (leftIsUpcoming && rightIsUpcoming) return leftScheduled - rightScheduled\n\n const compare = toTimelineTimestamp(right).localeCompare(toTimelineTimestamp(left))\n if (compare !== 0) return compare\n return right.id.localeCompare(left.id)\n })\n}\n\nfunction sortActivities(items: InteractionSummary[], sortMode: 'recent' | 'title-asc' | 'title-desc') {\n if (sortMode === 'recent') return sortTimelineActivities(items)\n return [...items].sort((left, right) => {\n const leftTitle = (left.title ?? left.body ?? left.interactionType ?? '').toLowerCase()\n const rightTitle = (right.title ?? right.body ?? right.interactionType ?? '').toLowerCase()\n return sortMode === 'title-asc'\n ? leftTitle.localeCompare(rightTitle)\n : rightTitle.localeCompare(leftTitle)\n })\n}\n\nfunction matchesSearch(activity: InteractionSummary, query: string): boolean {\n const normalized = query.trim().toLowerCase()\n if (!normalized) return true\n const haystack = [\n activity.title,\n activity.body,\n activity.authorName,\n activity.authorEmail,\n activity.customer?.displayName,\n ]\n .filter(Boolean)\n .join(' ')\n .toLowerCase()\n return haystack.includes(normalized)\n}\n\nfunction isWithinRange(activity: InteractionSummary, start: Date): boolean {\n const timestamp = new Date(toTimelineTimestamp(activity))\n if (Number.isNaN(timestamp.getTime())) return false\n return timestamp >= start\n}\n\nexport function ActivityHistorySection({\n entityId,\n useCanonicalInteractions = false,\n refreshKey = 0,\n onEditActivity,\n}: ActivityHistorySectionProps) {\n const t = useT()\n const [searchInput, setSearchInput] = React.useState('')\n const [search, setSearch] = React.useState('')\n const [activeTypes, setActiveTypes] = React.useState<string[]>([])\n const [dateRange, setDateRange] = React.useState<'7d' | '30d' | '90d'>('90d')\n const [sortMode, setSortMode] = React.useState<'recent' | 'title-asc' | 'title-desc'>('recent')\n const [activities, setActivities] = React.useState<InteractionSummary[]>([])\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [counts, setCounts] = React.useState<Record<string, number>>({ call: 0, email: 0, meeting: 0, note: 0, total: 0 })\n const [hasMore, setHasMore] = React.useState(false)\n const [loadedPages, setLoadedPages] = React.useState(1)\n\n React.useEffect(() => {\n const timeout = window.setTimeout(() => setSearch(searchInput.trim()), 300)\n return () => window.clearTimeout(timeout)\n }, [searchInput])\n\n React.useEffect(() => {\n const controller = new AbortController()\n void (async () => {\n try {\n const payload = await readApiResultOrThrow<InteractionCountsResponse>(\n `/api/customers/interactions/counts?entityId=${encodeURIComponent(entityId)}&status=done`,\n { signal: controller.signal },\n )\n const result = payload.result ?? payload\n setCounts({\n call: result.call ?? 0,\n email: result.email ?? 0,\n meeting: result.meeting ?? 0,\n note: result.note ?? 0,\n total: result.total ?? 0,\n })\n } catch {\n setCounts({ call: 0, email: 0, meeting: 0, note: 0, total: 0 })\n }\n })()\n return () => controller.abort()\n }, [entityId, refreshKey])\n\n const loadHistory = React.useCallback(async () => {\n setLoading(true)\n setError(null)\n try {\n const rangeStart = computeRangeStart(dateRange).toISOString()\n const pageSize = 20\n const canonicalItems: InteractionSummary[] = []\n let nextCursor: string | undefined\n let firstPageHasMore = false\n let pagesLoaded = 0\n\n do {\n const params = new URLSearchParams({\n entityId,\n status: 'done',\n excludeInteractionType: 'task',\n limit: String(pageSize),\n from: rangeStart,\n })\n if (activeTypes.length > 0) params.set('type', activeTypes.join(','))\n if (search) params.set('search', search)\n if (sortMode === 'recent') {\n params.set('sortField', 'occurredAt')\n params.set('sortDir', 'desc')\n } else {\n params.set('sortField', 'title')\n params.set('sortDir', sortMode === 'title-asc' ? 'asc' : 'desc')\n }\n if (nextCursor) params.set('cursor', nextCursor)\n\n const response = await readApiResultOrThrow<InteractionListResponse>(\n `/api/customers/interactions?${params.toString()}`,\n )\n const pageItems = Array.isArray(response.items) ? response.items : []\n canonicalItems.push(...pageItems)\n nextCursor = response.nextCursor\n if (!firstPageHasMore) firstPageHasMore = Boolean(response.nextCursor)\n pagesLoaded += 1\n } while (nextCursor && pagesLoaded < loadedPages)\n\n let combined = canonicalItems\n\n if (!useCanonicalInteractions) {\n const legacyItems: InteractionSummary[] = []\n let legacyTotalPages = 1\n for (let legacyPage = 1; legacyPage <= loadedPages; legacyPage += 1) {\n const legacyPayload = await readApiResultOrThrow<{ items?: ActivitySummary[]; totalPages?: number }>(\n `/api/customers/activities?entityId=${encodeURIComponent(entityId)}&page=${legacyPage}&pageSize=20&sortField=occurredAt&sortDir=desc`,\n ).catch(() => ({ items: [] as ActivitySummary[], totalPages: 1 }))\n legacyItems.push(...(Array.isArray(legacyPayload.items) ? legacyPayload.items.map(normalizeLegacyActivity) : []))\n legacyTotalPages = typeof legacyPayload.totalPages === 'number' ? legacyPayload.totalPages : legacyTotalPages\n if (legacyPage >= legacyTotalPages) break\n }\n const rangeStartDate = computeRangeStart(dateRange)\n const filteredLegacy = legacyItems.filter((item) => {\n if (activeTypes.length > 0 && !activeTypes.includes(item.interactionType)) return false\n if (!matchesSearch(item, search)) return false\n return isWithinRange(item, rangeStartDate)\n })\n const deduped = new Map<string, InteractionSummary>()\n ;[...canonicalItems, ...filteredLegacy].forEach((item) => {\n if (!deduped.has(item.id)) deduped.set(item.id, item)\n })\n combined = Array.from(deduped.values())\n firstPageHasMore = firstPageHasMore || legacyTotalPages > loadedPages\n }\n\n setActivities(sortActivities(combined, sortMode))\n setHasMore(firstPageHasMore)\n } catch (loadError) {\n setActivities([])\n setHasMore(false)\n setError(t('customers.activityLog.error', 'Failed to load activity history'))\n } finally {\n setLoading(false)\n }\n }, [activeTypes, dateRange, entityId, loadedPages, search, sortMode, t, useCanonicalInteractions])\n\n React.useEffect(() => {\n void loadHistory()\n }, [loadHistory, refreshKey])\n\n React.useEffect(() => {\n setLoadedPages(1)\n }, [activeTypes, dateRange, entityId, search, sortMode, useCanonicalInteractions])\n\n const filteredLabel = activeTypes.length > 0\n ? activeTypes.map((type) => t(`customers.timeline.filter.${type}`, type)).join(', ')\n : t('customers.timeline.filter.all', 'All')\n\n const handleTypeToggle = React.useCallback((type: string) => {\n setActiveTypes((current) => (\n current.includes(type)\n ? current.filter((entry) => entry !== type)\n : [...current, type]\n ))\n }, [])\n\n const handleLoadMore = React.useCallback(() => {\n setLoadedPages((current) => current + 1)\n }, [])\n\n return (\n <div className=\"rounded-xl border bg-card\">\n <div className=\"flex items-center gap-2 border-b px-5 py-4\">\n <Clock3 className=\"size-4 text-muted-foreground\" />\n <div className=\"min-w-0\">\n <h3 className=\"text-base font-semibold text-foreground\">\n {t('customers.activityLog.title', 'Activity history')}\n </h3>\n <p className=\"text-xs text-muted-foreground\">\n {t('customers.timeline.history.filtered', 'filtered: {{types}} \u00B7 {{count}} results', {\n types: filteredLabel,\n count: activities.length,\n })}\n </p>\n </div>\n </div>\n\n <div className=\"space-y-4 px-5 py-4\">\n <div className=\"flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between\">\n <div className=\"relative w-full max-w-md\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground\" />\n <Input\n value={searchInput}\n onChange={(event) => {\n setSearchInput(event.target.value)\n }}\n placeholder={t('customers.activityLog.searchPlaceholder', 'Search by title, note, or author')}\n className=\"h-9 pl-9\"\n />\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-overline font-semibold uppercase tracking-[0.12em] text-muted-foreground\">\n {t('customers.changelog.filter', 'Filter')}:\n </span>\n {TYPE_FILTERS.map((filter) => {\n const isActive = activeTypes.includes(filter.value)\n return (\n <Button\n key={filter.value}\n type=\"button\"\n variant={isActive ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => handleTypeToggle(filter.value)}\n className=\"h-auto rounded-full px-2.5 py-1 text-xs\"\n >\n {t(filter.labelKey, filter.fallback)}\n <span className={isActive ? 'ml-1 text-primary-foreground/80' : 'ml-1 text-muted-foreground'}>\n {counts[filter.value] ?? 0}\n </span>\n </Button>\n )\n })}\n\n <select\n value={dateRange}\n onChange={(event) => {\n setDateRange(event.target.value as '7d' | '30d' | '90d')\n }}\n className=\"h-8 rounded-lg border bg-background px-3 text-xs outline-none ring-offset-background focus:ring-2 focus:ring-ring\"\n >\n <option value=\"7d\">{t('customers.changelog.last7days', 'Last 7 days')}</option>\n <option value=\"30d\">{t('customers.changelog.last30days', 'Last 30 days')}</option>\n <option value=\"90d\">{t('customers.changelog.last90days', 'Last 90 days')}</option>\n </select>\n\n <select\n value={sortMode}\n onChange={(event) => {\n setSortMode(event.target.value as 'recent' | 'title-asc' | 'title-desc')\n }}\n className=\"h-8 rounded-lg border bg-background px-3 text-xs outline-none ring-offset-background focus:ring-2 focus:ring-ring\"\n >\n <option value=\"recent\">{t('customers.activityLog.sort.recent', 'Sort: newest')}</option>\n <option value=\"title-asc\">{t('customers.activityLog.sort.titleAsc', 'Sort: Name A-Z')}</option>\n <option value=\"title-desc\">{t('customers.activityLog.sort.titleDesc', 'Sort: Name Z-A')}</option>\n </select>\n </div>\n </div>\n\n {loading && activities.length === 0 ? (\n <LoadingMessage label={t('customers.people.detail.activities.loading', 'Loading activities\u2026')} className=\"min-h-[220px] justify-center\" />\n ) : error ? (\n <ErrorMessage label={error} />\n ) : activities.length === 0 ? (\n <TabEmptyState\n title={t('customers.timeline.empty', 'No activities match the current filters.')}\n description={t('customers.activityLog.emptyDescription', 'Try broadening the date range or removing some filters.')}\n />\n ) : (\n <div className=\"space-y-4\">\n {activities.map((activity, index) => {\n const currentYear = new Date(toTimelineTimestamp(activity)).getFullYear()\n const previousYear = index > 0 ? new Date(toTimelineTimestamp(activities[index - 1])).getFullYear() : null\n const showYearSeparator = previousYear !== null && currentYear !== previousYear\n return (\n <React.Fragment key={activity.id}>\n {showYearSeparator ? (\n <div className=\"flex items-center gap-3 py-1\">\n <div className=\"h-px flex-1 bg-border\" />\n <span className=\"text-xs font-semibold text-muted-foreground\">\u00B7 {currentYear} \u00B7</span>\n <div className=\"h-px flex-1 bg-border\" />\n </div>\n ) : null}\n <ActivityCard activity={activity} onOpen={onEditActivity} />\n </React.Fragment>\n )\n })}\n\n {hasMore ? (\n <div className=\"pt-2 text-center\">\n <Button type=\"button\" variant=\"link\" size=\"sm\" onClick={handleLoadMore} className=\"text-sm\">\n {t('customers.activities.loadMore', 'Load more')}\n </Button>\n </div>\n ) : null}\n </div>\n )}\n </div>\n </div>\n )\n}\n\nexport default ActivityHistorySection\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { Clock3, Search } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { ErrorMessage, LoadingMessage, TabEmptyState } from '@open-mercato/ui/backend/detail'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport type { ActivitySummary, InteractionSummary } from './types'\nimport { ActivityCard } from './ActivityCard'\n\ntype GuardedMutationRunner = <T,>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n) => Promise<T>\n\ntype ActivityHistorySectionProps = {\n entityId: string\n useCanonicalInteractions?: boolean\n refreshKey?: number\n onEditActivity?: (activity: InteractionSummary) => void\n /** Optional guarded-mutation runner so per-row mutations route through the parent's\n * `useGuardedMutation` and emit retry-last-mutation context. */\n runMutation?: GuardedMutationRunner\n}\n\ntype InteractionListResponse = {\n items?: InteractionSummary[]\n nextCursor?: string\n}\n\ntype InteractionCountsResponse = {\n ok?: boolean\n result?: {\n call: number\n email: number\n meeting: number\n note: number\n task: number\n total: number\n }\n call?: number\n email?: number\n meeting?: number\n note?: number\n task?: number\n total?: number\n}\n\nconst TYPE_FILTERS = [\n { value: 'call', labelKey: 'customers.timeline.filter.call', fallback: 'Call' },\n { value: 'email', labelKey: 'customers.timeline.filter.email', fallback: 'Email' },\n { value: 'meeting', labelKey: 'customers.timeline.filter.meeting', fallback: 'Meeting' },\n { value: 'note', labelKey: 'customers.timeline.filter.note', fallback: 'Note' },\n { value: 'task', labelKey: 'customers.timeline.filter.task', fallback: 'Task' },\n] as const\n\nfunction computeRangeStart(range: '7d' | '30d' | '90d'): Date {\n const date = new Date()\n date.setHours(0, 0, 0, 0)\n const days = Number.parseInt(range.replace('d', ''), 10) || 30\n date.setDate(date.getDate() - days)\n return date\n}\n\nfunction toTimelineTimestamp(activity: InteractionSummary): string {\n return activity.occurredAt ?? activity.scheduledAt ?? activity.createdAt\n}\n\nfunction normalizeLegacyActivity(activity: ActivitySummary): InteractionSummary {\n return {\n id: activity.id,\n interactionType: activity.activityType,\n title: activity.subject ?? null,\n body: activity.body ?? null,\n status: 'done',\n scheduledAt: null,\n occurredAt: activity.occurredAt ?? null,\n priority: null,\n authorUserId: activity.authorUserId ?? null,\n ownerUserId: null,\n appearanceIcon: activity.appearanceIcon ?? null,\n appearanceColor: activity.appearanceColor ?? null,\n source: 'legacy-activity',\n entityId: activity.entityId ?? null,\n dealId: activity.dealId ?? null,\n organizationId: null,\n tenantId: null,\n authorName: activity.authorName ?? null,\n authorEmail: activity.authorEmail ?? null,\n dealTitle: activity.dealTitle ?? null,\n customValues: activity.customValues ?? null,\n createdAt: activity.createdAt,\n updatedAt: activity.createdAt,\n }\n}\n\nfunction sortTimelineActivities(items: InteractionSummary[]): InteractionSummary[] {\n const now = Date.now()\n return [...items].sort((left, right) => {\n const leftScheduled = left.scheduledAt ? new Date(left.scheduledAt).getTime() : Number.NaN\n const rightScheduled = right.scheduledAt ? new Date(right.scheduledAt).getTime() : Number.NaN\n const leftIsPlanned = left.status === 'planned' && Number.isFinite(leftScheduled)\n const rightIsPlanned = right.status === 'planned' && Number.isFinite(rightScheduled)\n const leftIsUpcoming = leftIsPlanned && leftScheduled >= now\n const rightIsUpcoming = rightIsPlanned && rightScheduled >= now\n\n if (leftIsUpcoming !== rightIsUpcoming) return leftIsUpcoming ? -1 : 1\n if (leftIsUpcoming && rightIsUpcoming) return leftScheduled - rightScheduled\n\n const compare = toTimelineTimestamp(right).localeCompare(toTimelineTimestamp(left))\n if (compare !== 0) return compare\n return right.id.localeCompare(left.id)\n })\n}\n\nfunction sortActivities(items: InteractionSummary[], sortMode: 'recent' | 'title-asc' | 'title-desc') {\n if (sortMode === 'recent') return sortTimelineActivities(items)\n return [...items].sort((left, right) => {\n const leftTitle = (left.title ?? left.body ?? left.interactionType ?? '').toLowerCase()\n const rightTitle = (right.title ?? right.body ?? right.interactionType ?? '').toLowerCase()\n return sortMode === 'title-asc'\n ? leftTitle.localeCompare(rightTitle)\n : rightTitle.localeCompare(leftTitle)\n })\n}\n\nfunction matchesSearch(activity: InteractionSummary, query: string): boolean {\n const normalized = query.trim().toLowerCase()\n if (!normalized) return true\n const haystack = [\n activity.title,\n activity.body,\n activity.authorName,\n activity.authorEmail,\n activity.customer?.displayName,\n ]\n .filter(Boolean)\n .join(' ')\n .toLowerCase()\n return haystack.includes(normalized)\n}\n\nfunction isWithinRange(activity: InteractionSummary, start: Date): boolean {\n const timestamp = new Date(toTimelineTimestamp(activity))\n if (Number.isNaN(timestamp.getTime())) return false\n return timestamp >= start\n}\n\nfunction isAbortError(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'name' in error &&\n (error as { name?: unknown }).name === 'AbortError'\n )\n}\n\nexport function ActivityHistorySection({\n entityId,\n useCanonicalInteractions = false,\n refreshKey = 0,\n onEditActivity,\n runMutation,\n}: ActivityHistorySectionProps) {\n const t = useT()\n const [searchInput, setSearchInput] = React.useState('')\n const [search, setSearch] = React.useState('')\n const [activeTypes, setActiveTypes] = React.useState<string[]>([])\n const [dateRange, setDateRange] = React.useState<'7d' | '30d' | '90d'>('90d')\n const [sortMode, setSortMode] = React.useState<'recent' | 'title-asc' | 'title-desc'>('recent')\n const [activities, setActivities] = React.useState<InteractionSummary[]>([])\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [counts, setCounts] = React.useState<Record<string, number>>({ call: 0, email: 0, meeting: 0, note: 0, task: 0, total: 0 })\n const [hasMore, setHasMore] = React.useState(false)\n const [loadedPages, setLoadedPages] = React.useState(1)\n const [localRefreshKey, setLocalRefreshKey] = React.useState(0)\n const historyRequestSeqRef = React.useRef(0)\n const handleActivityChanged = React.useCallback(() => {\n setLocalRefreshKey((current) => current + 1)\n }, [])\n\n React.useEffect(() => {\n const timeout = window.setTimeout(() => setSearch(searchInput.trim()), 300)\n return () => window.clearTimeout(timeout)\n }, [searchInput])\n\n React.useEffect(() => {\n const controller = new AbortController()\n void (async () => {\n try {\n const payload = await readApiResultOrThrow<InteractionCountsResponse>(\n `/api/customers/interactions/counts?entityId=${encodeURIComponent(entityId)}`,\n { signal: controller.signal },\n )\n const result = payload.result ?? payload\n setCounts({\n call: result.call ?? 0,\n email: result.email ?? 0,\n meeting: result.meeting ?? 0,\n note: result.note ?? 0,\n task: result.task ?? 0,\n total: result.total ?? 0,\n })\n } catch {\n setCounts({ call: 0, email: 0, meeting: 0, note: 0, task: 0, total: 0 })\n }\n })()\n return () => controller.abort()\n }, [entityId, refreshKey, localRefreshKey])\n\n const loadHistory = React.useCallback(async (options: { signal: AbortSignal; requestSeq: number }) => {\n const { signal, requestSeq } = options\n const isStale = () => signal.aborted || requestSeq !== historyRequestSeqRef.current\n setLoading(true)\n setError(null)\n try {\n const rangeStart = computeRangeStart(dateRange).toISOString()\n const pageSize = 20\n const canonicalItems: InteractionSummary[] = []\n let nextCursor: string | undefined\n let firstPageHasMore = false\n let pagesLoaded = 0\n\n const taskFilterActive = activeTypes.includes('task')\n do {\n const params = new URLSearchParams({\n entityId,\n limit: String(pageSize),\n from: rangeStart,\n })\n if (!taskFilterActive) params.set('excludeInteractionType', 'task')\n if (activeTypes.length > 0) params.set('type', activeTypes.join(','))\n if (search) params.set('search', search)\n if (sortMode === 'recent') {\n params.set('sortField', 'occurredAt')\n params.set('sortDir', 'desc')\n } else {\n params.set('sortField', 'title')\n params.set('sortDir', sortMode === 'title-asc' ? 'asc' : 'desc')\n }\n if (nextCursor) params.set('cursor', nextCursor)\n\n const response = await readApiResultOrThrow<InteractionListResponse>(\n `/api/customers/interactions?${params.toString()}`,\n { signal },\n )\n if (isStale()) return\n const pageItems = Array.isArray(response.items) ? response.items : []\n canonicalItems.push(...pageItems)\n nextCursor = response.nextCursor\n if (!firstPageHasMore) firstPageHasMore = Boolean(response.nextCursor)\n pagesLoaded += 1\n } while (nextCursor && pagesLoaded < loadedPages)\n\n let combined = canonicalItems\n\n if (!useCanonicalInteractions) {\n const legacyItems: InteractionSummary[] = []\n let legacyTotalPages = 1\n for (let legacyPage = 1; legacyPage <= loadedPages; legacyPage += 1) {\n const legacyPayload = await readApiResultOrThrow<{ items?: ActivitySummary[]; totalPages?: number }>(\n `/api/customers/activities?entityId=${encodeURIComponent(entityId)}&page=${legacyPage}&pageSize=20&sortField=occurredAt&sortDir=desc`,\n { signal },\n ).catch(() => ({ items: [] as ActivitySummary[], totalPages: 1 }))\n if (isStale()) return\n legacyItems.push(...(Array.isArray(legacyPayload.items) ? legacyPayload.items.map(normalizeLegacyActivity) : []))\n legacyTotalPages = typeof legacyPayload.totalPages === 'number' ? legacyPayload.totalPages : legacyTotalPages\n if (legacyPage >= legacyTotalPages) break\n }\n const rangeStartDate = computeRangeStart(dateRange)\n const filteredLegacy = legacyItems.filter((item) => {\n if (activeTypes.length > 0 && !activeTypes.includes(item.interactionType)) return false\n if (!matchesSearch(item, search)) return false\n return isWithinRange(item, rangeStartDate)\n })\n const deduped = new Map<string, InteractionSummary>()\n ;[...canonicalItems, ...filteredLegacy].forEach((item) => {\n if (!deduped.has(item.id)) deduped.set(item.id, item)\n })\n combined = Array.from(deduped.values())\n firstPageHasMore = firstPageHasMore || legacyTotalPages > loadedPages\n }\n\n if (!isStale()) {\n setActivities(sortActivities(combined, sortMode))\n setHasMore(firstPageHasMore)\n }\n } catch (loadError) {\n if (!isStale() && !isAbortError(loadError)) {\n setActivities([])\n setHasMore(false)\n setError(t('customers.activityLog.error', 'Failed to load activity history'))\n }\n } finally {\n if (!isStale()) setLoading(false)\n }\n }, [activeTypes, dateRange, entityId, loadedPages, search, sortMode, t, useCanonicalInteractions])\n\n React.useEffect(() => {\n const controller = new AbortController()\n const requestSeq = historyRequestSeqRef.current + 1\n historyRequestSeqRef.current = requestSeq\n void loadHistory({ signal: controller.signal, requestSeq })\n return () => controller.abort()\n }, [loadHistory, refreshKey, localRefreshKey])\n\n React.useEffect(() => {\n setLoadedPages(1)\n }, [activeTypes, dateRange, entityId, search, sortMode, useCanonicalInteractions])\n\n const filteredLabel = activeTypes.length > 0\n ? activeTypes.map((type) => t(`customers.timeline.filter.${type}`, type)).join(', ')\n : t('customers.timeline.filter.all', 'All')\n\n const handleTypeToggle = React.useCallback((type: string) => {\n setActiveTypes((current) => (\n current.includes(type)\n ? current.filter((entry) => entry !== type)\n : [...current, type]\n ))\n }, [])\n\n const handleLoadMore = React.useCallback(() => {\n setLoadedPages((current) => current + 1)\n }, [])\n\n return (\n <div className=\"rounded-xl border bg-card\">\n <div className=\"flex items-center gap-2 border-b px-5 py-4\">\n <Clock3 className=\"size-4 text-muted-foreground\" />\n <div className=\"min-w-0\">\n <h3 className=\"text-base font-semibold text-foreground\">\n {t('customers.activityLog.title', 'Activity history')}\n </h3>\n <p className=\"text-xs text-muted-foreground\">\n {t('customers.timeline.history.filtered', 'filtered: {{types}} \u00B7 {{count}} results', {\n types: filteredLabel,\n count: activities.length,\n })}\n </p>\n </div>\n </div>\n\n <div className=\"space-y-4 px-5 py-4\">\n <div className=\"flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between\">\n <div className=\"relative w-full max-w-md\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground\" />\n <Input\n value={searchInput}\n onChange={(event) => {\n setSearchInput(event.target.value)\n }}\n placeholder={t('customers.activityLog.searchPlaceholder', 'Search by title, note, or author')}\n className=\"h-9 pl-9\"\n />\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-overline font-semibold uppercase tracking-[0.12em] text-muted-foreground\">\n {t('customers.changelog.filter', 'Filter')}:\n </span>\n {TYPE_FILTERS.map((filter) => {\n const isActive = activeTypes.includes(filter.value)\n return (\n <Button\n key={filter.value}\n type=\"button\"\n variant={isActive ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => handleTypeToggle(filter.value)}\n className=\"h-auto rounded-full px-2.5 py-1 text-xs\"\n >\n {t(filter.labelKey, filter.fallback)}\n <span className={isActive ? 'ml-1 text-primary-foreground/80' : 'ml-1 text-muted-foreground'}>\n {counts[filter.value] ?? 0}\n </span>\n </Button>\n )\n })}\n\n <Select\n value={dateRange}\n onValueChange={(value) => {\n setDateRange(value as '7d' | '30d' | '90d')\n }}\n >\n <SelectTrigger\n size=\"sm\"\n aria-label={t('customers.activityLog.filters.dateRangeLabel', 'Date range')}\n className=\"w-auto\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"7d\">{t('customers.changelog.last7days', 'Last 7 days')}</SelectItem>\n <SelectItem value=\"30d\">{t('customers.changelog.last30days', 'Last 30 days')}</SelectItem>\n <SelectItem value=\"90d\">{t('customers.changelog.last90days', 'Last 90 days')}</SelectItem>\n </SelectContent>\n </Select>\n\n <Select\n value={sortMode}\n onValueChange={(value) => {\n setSortMode(value as 'recent' | 'title-asc' | 'title-desc')\n }}\n >\n <SelectTrigger\n size=\"sm\"\n aria-label={t('customers.activityLog.filters.sortLabel', 'Sort order')}\n className=\"w-auto\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"recent\">{t('customers.activityLog.sort.recent', 'Sort: newest')}</SelectItem>\n <SelectItem value=\"title-asc\">{t('customers.activityLog.sort.titleAsc', 'Sort: Name A-Z')}</SelectItem>\n <SelectItem value=\"title-desc\">{t('customers.activityLog.sort.titleDesc', 'Sort: Name Z-A')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n </div>\n\n {loading && activities.length === 0 ? (\n <LoadingMessage label={t('customers.people.detail.activities.loading', 'Loading activities\u2026')} className=\"min-h-[220px] justify-center\" />\n ) : error ? (\n <ErrorMessage label={error} />\n ) : activities.length === 0 ? (\n <TabEmptyState\n title={t('customers.timeline.empty', 'No activities match the current filters.')}\n description={t('customers.activityLog.emptyDescription', 'Try broadening the date range or removing some filters.')}\n />\n ) : (\n <div className=\"space-y-4\">\n {activities.map((activity, index) => {\n const currentYear = new Date(toTimelineTimestamp(activity)).getFullYear()\n const previousYear = index > 0 ? new Date(toTimelineTimestamp(activities[index - 1])).getFullYear() : null\n const showYearSeparator = previousYear !== null && currentYear !== previousYear\n return (\n <React.Fragment key={activity.id}>\n {showYearSeparator ? (\n <div className=\"flex items-center gap-3 py-1\">\n <div className=\"h-px flex-1 bg-border\" />\n <span className=\"text-xs font-semibold text-muted-foreground\">\u00B7 {currentYear} \u00B7</span>\n <div className=\"h-px flex-1 bg-border\" />\n </div>\n ) : null}\n <ActivityCard\n activity={activity}\n onOpen={onEditActivity}\n onChanged={handleActivityChanged}\n runMutation={runMutation}\n />\n </React.Fragment>\n )\n })}\n\n {hasMore ? (\n <div className=\"pt-2 text-center\">\n <Button type=\"button\" variant=\"link\" size=\"sm\" onClick={handleLoadMore} className=\"text-sm\">\n {t('customers.activities.loadMore', 'Load more')}\n </Button>\n </div>\n ) : null}\n </div>\n )}\n </div>\n </div>\n )\n}\n\nexport default ActivityHistorySection\n"],
5
+ "mappings": ";AAmVQ,cACA,YADA;AAjVR,YAAY,WAAW;AACvB,SAAS,QAAQ,cAAc;AAC/B,SAAS,YAAY;AACrB,SAAS,cAAc;AACvB,SAAS,aAAa;AACtB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,cAAc,gBAAgB,qBAAqB;AAC5D,SAAS,4BAA4B;AAErC,SAAS,oBAAoB;AAwC7B,MAAM,eAAe;AAAA,EACnB,EAAE,OAAO,QAAQ,UAAU,kCAAkC,UAAU,OAAO;AAAA,EAC9E,EAAE,OAAO,SAAS,UAAU,mCAAmC,UAAU,QAAQ;AAAA,EACjF,EAAE,OAAO,WAAW,UAAU,qCAAqC,UAAU,UAAU;AAAA,EACvF,EAAE,OAAO,QAAQ,UAAU,kCAAkC,UAAU,OAAO;AAAA,EAC9E,EAAE,OAAO,QAAQ,UAAU,kCAAkC,UAAU,OAAO;AAChF;AAEA,SAAS,kBAAkB,OAAmC;AAC5D,QAAM,OAAO,oBAAI,KAAK;AACtB,OAAK,SAAS,GAAG,GAAG,GAAG,CAAC;AACxB,QAAM,OAAO,OAAO,SAAS,MAAM,QAAQ,KAAK,EAAE,GAAG,EAAE,KAAK;AAC5D,OAAK,QAAQ,KAAK,QAAQ,IAAI,IAAI;AAClC,SAAO;AACT;AAEA,SAAS,oBAAoB,UAAsC;AACjE,SAAO,SAAS,cAAc,SAAS,eAAe,SAAS;AACjE;AAEA,SAAS,wBAAwB,UAA+C;AAC9E,SAAO;AAAA,IACL,IAAI,SAAS;AAAA,IACb,iBAAiB,SAAS;AAAA,IAC1B,OAAO,SAAS,WAAW;AAAA,IAC3B,MAAM,SAAS,QAAQ;AAAA,IACvB,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,YAAY,SAAS,cAAc;AAAA,IACnC,UAAU;AAAA,IACV,cAAc,SAAS,gBAAgB;AAAA,IACvC,aAAa;AAAA,IACb,gBAAgB,SAAS,kBAAkB;AAAA,IAC3C,iBAAiB,SAAS,mBAAmB;AAAA,IAC7C,QAAQ;AAAA,IACR,UAAU,SAAS,YAAY;AAAA,IAC/B,QAAQ,SAAS,UAAU;AAAA,IAC3B,gBAAgB;AAAA,IAChB,UAAU;AAAA,IACV,YAAY,SAAS,cAAc;AAAA,IACnC,aAAa,SAAS,eAAe;AAAA,IACrC,WAAW,SAAS,aAAa;AAAA,IACjC,cAAc,SAAS,gBAAgB;AAAA,IACvC,WAAW,SAAS;AAAA,IACpB,WAAW,SAAS;AAAA,EACtB;AACF;AAEA,SAAS,uBAAuB,OAAmD;AACjF,QAAM,MAAM,KAAK,IAAI;AACrB,SAAO,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM,UAAU;AACtC,UAAM,gBAAgB,KAAK,cAAc,IAAI,KAAK,KAAK,WAAW,EAAE,QAAQ,IAAI,OAAO;AACvF,UAAM,iBAAiB,MAAM,cAAc,IAAI,KAAK,MAAM,WAAW,EAAE,QAAQ,IAAI,OAAO;AAC1F,UAAM,gBAAgB,KAAK,WAAW,aAAa,OAAO,SAAS,aAAa;AAChF,UAAM,iBAAiB,MAAM,WAAW,aAAa,OAAO,SAAS,cAAc;AACnF,UAAM,iBAAiB,iBAAiB,iBAAiB;AACzD,UAAM,kBAAkB,kBAAkB,kBAAkB;AAE5D,QAAI,mBAAmB,gBAAiB,QAAO,iBAAiB,KAAK;AACrE,QAAI,kBAAkB,gBAAiB,QAAO,gBAAgB;AAE9D,UAAM,UAAU,oBAAoB,KAAK,EAAE,cAAc,oBAAoB,IAAI,CAAC;AAClF,QAAI,YAAY,EAAG,QAAO;AAC1B,WAAO,MAAM,GAAG,cAAc,KAAK,EAAE;AAAA,EACvC,CAAC;AACH;AAEA,SAAS,eAAe,OAA6B,UAAiD;AACpG,MAAI,aAAa,SAAU,QAAO,uBAAuB,KAAK;AAC9D,SAAO,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM,UAAU;AACtC,UAAM,aAAa,KAAK,SAAS,KAAK,QAAQ,KAAK,mBAAmB,IAAI,YAAY;AACtF,UAAM,cAAc,MAAM,SAAS,MAAM,QAAQ,MAAM,mBAAmB,IAAI,YAAY;AAC1F,WAAO,aAAa,cAChB,UAAU,cAAc,UAAU,IAClC,WAAW,cAAc,SAAS;AAAA,EACxC,CAAC;AACH;AAEA,SAAS,cAAc,UAA8B,OAAwB;AAC3E,QAAM,aAAa,MAAM,KAAK,EAAE,YAAY;AAC5C,MAAI,CAAC,WAAY,QAAO;AACxB,QAAM,WAAW;AAAA,IACf,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS,UAAU;AAAA,EACrB,EACG,OAAO,OAAO,EACd,KAAK,GAAG,EACR,YAAY;AACf,SAAO,SAAS,SAAS,UAAU;AACrC;AAEA,SAAS,cAAc,UAA8B,OAAsB;AACzE,QAAM,YAAY,IAAI,KAAK,oBAAoB,QAAQ,CAAC;AACxD,MAAI,OAAO,MAAM,UAAU,QAAQ,CAAC,EAAG,QAAO;AAC9C,SAAO,aAAa;AACtB;AAEA,SAAS,aAAa,OAAyB;AAC7C,SACE,OAAO,UAAU,YACjB,UAAU,QACV,UAAU,SACT,MAA6B,SAAS;AAE3C;AAEO,SAAS,uBAAuB;AAAA,EACrC;AAAA,EACA,2BAA2B;AAAA,EAC3B,aAAa;AAAA,EACb;AAAA,EACA;AACF,GAAgC;AAC9B,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAS,EAAE;AACvD,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAS,EAAE;AAC7C,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAmB,CAAC,CAAC;AACjE,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAA+B,KAAK;AAC5E,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAgD,QAAQ;AAC9F,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAA+B,CAAC,CAAC;AAC3E,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,IAAI;AACjD,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAwB,IAAI;AAC5D,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAiC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,CAAC;AAChI,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAClD,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAS,CAAC;AACtD,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,MAAM,SAAS,CAAC;AAC9D,QAAM,uBAAuB,MAAM,OAAO,CAAC;AAC3C,QAAM,wBAAwB,MAAM,YAAY,MAAM;AACpD,uBAAmB,CAAC,YAAY,UAAU,CAAC;AAAA,EAC7C,GAAG,CAAC,CAAC;AAEL,QAAM,UAAU,MAAM;AACpB,UAAM,UAAU,OAAO,WAAW,MAAM,UAAU,YAAY,KAAK,CAAC,GAAG,GAAG;AAC1E,WAAO,MAAM,OAAO,aAAa,OAAO;AAAA,EAC1C,GAAG,CAAC,WAAW,CAAC;AAEhB,QAAM,UAAU,MAAM;AACpB,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,YAAY;AAChB,UAAI;AACF,cAAM,UAAU,MAAM;AAAA,UACpB,+CAA+C,mBAAmB,QAAQ,CAAC;AAAA,UAC3E,EAAE,QAAQ,WAAW,OAAO;AAAA,QAC9B;AACA,cAAM,SAAS,QAAQ,UAAU;AACjC,kBAAU;AAAA,UACR,MAAM,OAAO,QAAQ;AAAA,UACrB,OAAO,OAAO,SAAS;AAAA,UACvB,SAAS,OAAO,WAAW;AAAA,UAC3B,MAAM,OAAO,QAAQ;AAAA,UACrB,MAAM,OAAO,QAAQ;AAAA,UACrB,OAAO,OAAO,SAAS;AAAA,QACzB,CAAC;AAAA,MACH,QAAQ;AACN,kBAAU,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,CAAC;AAAA,MACzE;AAAA,IACF,GAAG;AACH,WAAO,MAAM,WAAW,MAAM;AAAA,EAChC,GAAG,CAAC,UAAU,YAAY,eAAe,CAAC;AAE1C,QAAM,cAAc,MAAM,YAAY,OAAO,YAAyD;AACpG,UAAM,EAAE,QAAQ,WAAW,IAAI;AAC/B,UAAM,UAAU,MAAM,OAAO,WAAW,eAAe,qBAAqB;AAC5E,eAAW,IAAI;AACf,aAAS,IAAI;AACb,QAAI;AACF,YAAM,aAAa,kBAAkB,SAAS,EAAE,YAAY;AAC5D,YAAM,WAAW;AACjB,YAAM,iBAAuC,CAAC;AAC9C,UAAI;AACJ,UAAI,mBAAmB;AACvB,UAAI,cAAc;AAElB,YAAM,mBAAmB,YAAY,SAAS,MAAM;AACpD,SAAG;AACD,cAAM,SAAS,IAAI,gBAAgB;AAAA,UACjC;AAAA,UACA,OAAO,OAAO,QAAQ;AAAA,UACtB,MAAM;AAAA,QACR,CAAC;AACD,YAAI,CAAC,iBAAkB,QAAO,IAAI,0BAA0B,MAAM;AAClE,YAAI,YAAY,SAAS,EAAG,QAAO,IAAI,QAAQ,YAAY,KAAK,GAAG,CAAC;AACpE,YAAI,OAAQ,QAAO,IAAI,UAAU,MAAM;AACvC,YAAI,aAAa,UAAU;AACzB,iBAAO,IAAI,aAAa,YAAY;AACpC,iBAAO,IAAI,WAAW,MAAM;AAAA,QAC9B,OAAO;AACL,iBAAO,IAAI,aAAa,OAAO;AAC/B,iBAAO,IAAI,WAAW,aAAa,cAAc,QAAQ,MAAM;AAAA,QACjE;AACA,YAAI,WAAY,QAAO,IAAI,UAAU,UAAU;AAE/C,cAAM,WAAW,MAAM;AAAA,UACrB,+BAA+B,OAAO,SAAS,CAAC;AAAA,UAChD,EAAE,OAAO;AAAA,QACX;AACA,YAAI,QAAQ,EAAG;AACf,cAAM,YAAY,MAAM,QAAQ,SAAS,KAAK,IAAI,SAAS,QAAQ,CAAC;AACpE,uBAAe,KAAK,GAAG,SAAS;AAChC,qBAAa,SAAS;AACtB,YAAI,CAAC,iBAAkB,oBAAmB,QAAQ,SAAS,UAAU;AACrE,uBAAe;AAAA,MACjB,SAAS,cAAc,cAAc;AAErC,UAAI,WAAW;AAEf,UAAI,CAAC,0BAA0B;AAC7B,cAAM,cAAoC,CAAC;AAC3C,YAAI,mBAAmB;AACvB,iBAAS,aAAa,GAAG,cAAc,aAAa,cAAc,GAAG;AACnE,gBAAM,gBAAgB,MAAM;AAAA,YAC1B,sCAAsC,mBAAmB,QAAQ,CAAC,SAAS,UAAU;AAAA,YACrF,EAAE,OAAO;AAAA,UACX,EAAE,MAAM,OAAO,EAAE,OAAO,CAAC,GAAwB,YAAY,EAAE,EAAE;AACjE,cAAI,QAAQ,EAAG;AACf,sBAAY,KAAK,GAAI,MAAM,QAAQ,cAAc,KAAK,IAAI,cAAc,MAAM,IAAI,uBAAuB,IAAI,CAAC,CAAE;AAChH,6BAAmB,OAAO,cAAc,eAAe,WAAW,cAAc,aAAa;AAC7F,cAAI,cAAc,iBAAkB;AAAA,QACtC;AACA,cAAM,iBAAiB,kBAAkB,SAAS;AAClD,cAAM,iBAAiB,YAAY,OAAO,CAAC,SAAS;AAClD,cAAI,YAAY,SAAS,KAAK,CAAC,YAAY,SAAS,KAAK,eAAe,EAAG,QAAO;AAClF,cAAI,CAAC,cAAc,MAAM,MAAM,EAAG,QAAO;AACzC,iBAAO,cAAc,MAAM,cAAc;AAAA,QAC3C,CAAC;AACD,cAAM,UAAU,oBAAI,IAAgC;AACnD,SAAC,GAAG,gBAAgB,GAAG,cAAc,EAAE,QAAQ,CAAC,SAAS;AACxD,cAAI,CAAC,QAAQ,IAAI,KAAK,EAAE,EAAG,SAAQ,IAAI,KAAK,IAAI,IAAI;AAAA,QACtD,CAAC;AACD,mBAAW,MAAM,KAAK,QAAQ,OAAO,CAAC;AACtC,2BAAmB,oBAAoB,mBAAmB;AAAA,MAC5D;AAEA,UAAI,CAAC,QAAQ,GAAG;AACd,sBAAc,eAAe,UAAU,QAAQ,CAAC;AAChD,mBAAW,gBAAgB;AAAA,MAC7B;AAAA,IACF,SAAS,WAAW;AAClB,UAAI,CAAC,QAAQ,KAAK,CAAC,aAAa,SAAS,GAAG;AAC1C,sBAAc,CAAC,CAAC;AAChB,mBAAW,KAAK;AAChB,iBAAS,EAAE,+BAA+B,iCAAiC,CAAC;AAAA,MAC9E;AAAA,IACF,UAAE;AACA,UAAI,CAAC,QAAQ,EAAG,YAAW,KAAK;AAAA,IAClC;AAAA,EACF,GAAG,CAAC,aAAa,WAAW,UAAU,aAAa,QAAQ,UAAU,GAAG,wBAAwB,CAAC;AAEjG,QAAM,UAAU,MAAM;AACpB,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,aAAa,qBAAqB,UAAU;AAClD,yBAAqB,UAAU;AAC/B,SAAK,YAAY,EAAE,QAAQ,WAAW,QAAQ,WAAW,CAAC;AAC1D,WAAO,MAAM,WAAW,MAAM;AAAA,EAChC,GAAG,CAAC,aAAa,YAAY,eAAe,CAAC;AAE7C,QAAM,UAAU,MAAM;AACpB,mBAAe,CAAC;AAAA,EAClB,GAAG,CAAC,aAAa,WAAW,UAAU,QAAQ,UAAU,wBAAwB,CAAC;AAEjF,QAAM,gBAAgB,YAAY,SAAS,IACvC,YAAY,IAAI,CAAC,SAAS,EAAE,6BAA6B,IAAI,IAAI,IAAI,CAAC,EAAE,KAAK,IAAI,IACjF,EAAE,iCAAiC,KAAK;AAE5C,QAAM,mBAAmB,MAAM,YAAY,CAAC,SAAiB;AAC3D,mBAAe,CAAC,YACd,QAAQ,SAAS,IAAI,IACjB,QAAQ,OAAO,CAAC,UAAU,UAAU,IAAI,IACxC,CAAC,GAAG,SAAS,IAAI,CACtB;AAAA,EACH,GAAG,CAAC,CAAC;AAEL,QAAM,iBAAiB,MAAM,YAAY,MAAM;AAC7C,mBAAe,CAAC,YAAY,UAAU,CAAC;AAAA,EACzC,GAAG,CAAC,CAAC;AAEL,SACE,qBAAC,SAAI,WAAU,6BACb;AAAA,yBAAC,SAAI,WAAU,8CACb;AAAA,0BAAC,UAAO,WAAU,gCAA+B;AAAA,MACjD,qBAAC,SAAI,WAAU,WACb;AAAA,4BAAC,QAAG,WAAU,2CACX,YAAE,+BAA+B,kBAAkB,GACtD;AAAA,QACA,oBAAC,OAAE,WAAU,iCACV,YAAE,uCAAuC,8CAA2C;AAAA,UACnF,OAAO;AAAA,UACP,OAAO,WAAW;AAAA,QACpB,CAAC,GACH;AAAA,SACF;AAAA,OACF;AAAA,IAEA,qBAAC,SAAI,WAAU,uBACb;AAAA,2BAAC,SAAI,WAAU,sEACb;AAAA,6BAAC,SAAI,WAAU,4BACb;AAAA,8BAAC,UAAO,WAAU,6FAA4F;AAAA,UAC9G;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU,CAAC,UAAU;AACnB,+BAAe,MAAM,OAAO,KAAK;AAAA,cACnC;AAAA,cACA,aAAa,EAAE,2CAA2C,kCAAkC;AAAA,cAC5F,WAAU;AAAA;AAAA,UACZ;AAAA,WACF;AAAA,QAEA,qBAAC,SAAI,WAAU,qCACb;AAAA,+BAAC,UAAK,WAAU,iFACb;AAAA,cAAE,8BAA8B,QAAQ;AAAA,YAAE;AAAA,aAC7C;AAAA,UACC,aAAa,IAAI,CAAC,WAAW;AAC5B,kBAAM,WAAW,YAAY,SAAS,OAAO,KAAK;AAClD,mBACE;AAAA,cAAC;AAAA;AAAA,gBAEC,MAAK;AAAA,gBACL,SAAS,WAAW,YAAY;AAAA,gBAChC,MAAK;AAAA,gBACL,SAAS,MAAM,iBAAiB,OAAO,KAAK;AAAA,gBAC5C,WAAU;AAAA,gBAET;AAAA,oBAAE,OAAO,UAAU,OAAO,QAAQ;AAAA,kBACnC,oBAAC,UAAK,WAAW,WAAW,oCAAoC,8BAC7D,iBAAO,OAAO,KAAK,KAAK,GAC3B;AAAA;AAAA;AAAA,cAVK,OAAO;AAAA,YAWd;AAAA,UAEJ,CAAC;AAAA,UAED;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,eAAe,CAAC,UAAU;AACxB,6BAAa,KAA6B;AAAA,cAC5C;AAAA,cAEA;AAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,cAAY,EAAE,gDAAgD,YAAY;AAAA,oBAC1E,WAAU;AAAA,oBAEV,8BAAC,eAAY;AAAA;AAAA,gBACf;AAAA,gBACA,qBAAC,iBACC;AAAA,sCAAC,cAAW,OAAM,MAAM,YAAE,iCAAiC,aAAa,GAAE;AAAA,kBAC1E,oBAAC,cAAW,OAAM,OAAO,YAAE,kCAAkC,cAAc,GAAE;AAAA,kBAC7E,oBAAC,cAAW,OAAM,OAAO,YAAE,kCAAkC,cAAc,GAAE;AAAA,mBAC/E;AAAA;AAAA;AAAA,UACF;AAAA,UAEA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,eAAe,CAAC,UAAU;AACxB,4BAAY,KAA8C;AAAA,cAC5D;AAAA,cAEA;AAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,cAAY,EAAE,2CAA2C,YAAY;AAAA,oBACrE,WAAU;AAAA,oBAEV,8BAAC,eAAY;AAAA;AAAA,gBACf;AAAA,gBACA,qBAAC,iBACC;AAAA,sCAAC,cAAW,OAAM,UAAU,YAAE,qCAAqC,cAAc,GAAE;AAAA,kBACnF,oBAAC,cAAW,OAAM,aAAa,YAAE,uCAAuC,gBAAgB,GAAE;AAAA,kBAC1F,oBAAC,cAAW,OAAM,cAAc,YAAE,wCAAwC,gBAAgB,GAAE;AAAA,mBAC9F;AAAA;AAAA;AAAA,UACF;AAAA,WACF;AAAA,SACF;AAAA,MAEC,WAAW,WAAW,WAAW,IAChC,oBAAC,kBAAe,OAAO,EAAE,8CAA8C,0BAAqB,GAAG,WAAU,gCAA+B,IACtI,QACF,oBAAC,gBAAa,OAAO,OAAO,IAC1B,WAAW,WAAW,IACxB;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,EAAE,4BAA4B,0CAA0C;AAAA,UAC/E,aAAa,EAAE,0CAA0C,yDAAyD;AAAA;AAAA,MACpH,IAEA,qBAAC,SAAI,WAAU,aACZ;AAAA,mBAAW,IAAI,CAAC,UAAU,UAAU;AACnC,gBAAM,cAAc,IAAI,KAAK,oBAAoB,QAAQ,CAAC,EAAE,YAAY;AACxE,gBAAM,eAAe,QAAQ,IAAI,IAAI,KAAK,oBAAoB,WAAW,QAAQ,CAAC,CAAC,CAAC,EAAE,YAAY,IAAI;AACtG,gBAAM,oBAAoB,iBAAiB,QAAQ,gBAAgB;AACnE,iBACE,qBAAC,MAAM,UAAN,EACE;AAAA,gCACC,qBAAC,SAAI,WAAU,gCACb;AAAA,kCAAC,SAAI,WAAU,yBAAwB;AAAA,cACvC,qBAAC,UAAK,WAAU,+CAA8C;AAAA;AAAA,gBAAG;AAAA,gBAAY;AAAA,iBAAE;AAAA,cAC/E,oBAAC,SAAI,WAAU,yBAAwB;AAAA,eACzC,IACE;AAAA,YACJ;AAAA,cAAC;AAAA;AAAA,gBACC;AAAA,gBACA,QAAQ;AAAA,gBACR,WAAW;AAAA,gBACX;AAAA;AAAA,YACF;AAAA,eAbmB,SAAS,EAc9B;AAAA,QAEJ,CAAC;AAAA,QAEA,UACC,oBAAC,SAAI,WAAU,oBACb,8BAAC,UAAO,MAAK,UAAS,SAAQ,QAAO,MAAK,MAAK,SAAS,gBAAgB,WAAU,WAC/E,YAAE,iCAAiC,WAAW,GACjD,GACF,IACE;AAAA,SACN;AAAA,OAEJ;AAAA,KACF;AAEJ;AAEA,IAAO,iCAAQ;",
6
6
  "names": []
7
7
  }
@@ -8,6 +8,7 @@ function ActivityLogTab({
8
8
  onScheduleRequested,
9
9
  onAddActivity,
10
10
  onEditActivity,
11
+ runGuardedMutation,
11
12
  refreshKey = 0,
12
13
  useCanonicalInteractions = false,
13
14
  entityCompanyName
@@ -34,7 +35,8 @@ function ActivityLogTab({
34
35
  entityId,
35
36
  useCanonicalInteractions,
36
37
  refreshKey,
37
- onEditActivity
38
+ onEditActivity,
39
+ runMutation: runGuardedMutation
38
40
  }
39
41
  )
40
42
  ] });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/components/detail/ActivityLogTab.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport { ActivitiesCard } from './ActivitiesCard'\nimport type { ActivityKind } from './ActivitiesAddNewMenu'\nimport { ActivityHistorySection } from './ActivityHistorySection'\nimport type { InteractionSummary } from './types'\n\ntype GuardedMutationRunner = <T,>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n) => Promise<T>\n\ntype ActivityLogTabProps = {\n entityId: string\n plannedActivities: InteractionSummary[]\n /** @deprecated No longer used after the ActivitiesCard refactor. Kept optional for callers; remove after one minor cycle. */\n onActivityCreated?: () => void\n onScheduleRequested: () => void\n onAddActivity?: (kind: ActivityKind) => void\n /** @deprecated No longer used after the ActivitiesCard refactor. Kept optional for callers; remove after one minor cycle. */\n onMarkDone?: (id: string) => void\n onEditActivity: (activity: InteractionSummary) => void\n /** @deprecated No longer used after the ActivitiesCard refactor. Kept optional for callers; remove after one minor cycle. */\n onCancelActivity?: (id: string) => void\n /** @deprecated No longer used after the ActivitiesCard refactor. Kept optional for callers; remove after one minor cycle. */\n runGuardedMutation?: GuardedMutationRunner\n refreshKey?: number\n useCanonicalInteractions?: boolean\n /** Optional parent-entity company name; surfaces in planned event subtitles when no deal is set. */\n entityCompanyName?: string | null\n}\n\nexport function ActivityLogTab({\n entityId,\n plannedActivities,\n onScheduleRequested,\n onAddActivity,\n onEditActivity,\n refreshKey = 0,\n useCanonicalInteractions = false,\n entityCompanyName,\n}: ActivityLogTabProps) {\n const handleAddNew = (kind: ActivityKind) => {\n if (onAddActivity) onAddActivity(kind)\n else onScheduleRequested()\n }\n\n return (\n <div className=\"space-y-4\">\n <ActivitiesCard\n entityId={entityId}\n plannedActivities={plannedActivities}\n refreshKey={refreshKey}\n onAddNew={handleAddNew}\n onEditActivity={onEditActivity}\n entityCompanyName={entityCompanyName}\n />\n\n <ActivityHistorySection\n entityId={entityId}\n useCanonicalInteractions={useCanonicalInteractions}\n refreshKey={refreshKey}\n onEditActivity={onEditActivity}\n />\n </div>\n )\n}\n\nexport default ActivityLogTab\n"],
5
- "mappings": ";AAgDI,SACE,KADF;AA9CJ,SAAS,sBAAsB;AAE/B,SAAS,8BAA8B;AA4BhC,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,2BAA2B;AAAA,EAC3B;AACF,GAAwB;AACtB,QAAM,eAAe,CAAC,SAAuB;AAC3C,QAAI,cAAe,eAAc,IAAI;AAAA,QAChC,qBAAoB;AAAA,EAC3B;AAEA,SACE,qBAAC,SAAI,WAAU,aACb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA,UAAU;AAAA,QACV;AAAA,QACA;AAAA;AAAA,IACF;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;AAEA,IAAO,yBAAQ;",
4
+ "sourcesContent": ["'use client'\n\nimport { ActivitiesCard } from './ActivitiesCard'\nimport type { ActivityKind } from './ActivitiesAddNewMenu'\nimport { ActivityHistorySection } from './ActivityHistorySection'\nimport type { InteractionSummary } from './types'\n\ntype GuardedMutationRunner = <T,>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n) => Promise<T>\n\ntype ActivityLogTabProps = {\n entityId: string\n plannedActivities: InteractionSummary[]\n /** @deprecated No longer used after the ActivitiesCard refactor. Kept optional for callers; remove after one minor cycle. */\n onActivityCreated?: () => void\n onScheduleRequested: () => void\n onAddActivity?: (kind: ActivityKind) => void\n /** @deprecated No longer used after the ActivitiesCard refactor. Kept optional for callers; remove after one minor cycle. */\n onMarkDone?: (id: string) => void\n onEditActivity: (activity: InteractionSummary) => void\n /** @deprecated No longer used after the ActivitiesCard refactor. Kept optional for callers; remove after one minor cycle. */\n onCancelActivity?: (id: string) => void\n /**\n * Guarded-mutation runner from the parent page. When provided, per-row mutations\n * (e.g. ActivityCard \"Mark done\") route through `useGuardedMutation` so the global\n * injection contract and retry-last-mutation context apply.\n */\n runGuardedMutation?: GuardedMutationRunner\n refreshKey?: number\n useCanonicalInteractions?: boolean\n /** Optional parent-entity company name; surfaces in planned event subtitles when no deal is set. */\n entityCompanyName?: string | null\n}\n\nexport function ActivityLogTab({\n entityId,\n plannedActivities,\n onScheduleRequested,\n onAddActivity,\n onEditActivity,\n runGuardedMutation,\n refreshKey = 0,\n useCanonicalInteractions = false,\n entityCompanyName,\n}: ActivityLogTabProps) {\n const handleAddNew = (kind: ActivityKind) => {\n if (onAddActivity) onAddActivity(kind)\n else onScheduleRequested()\n }\n\n return (\n <div className=\"space-y-4\">\n <ActivitiesCard\n entityId={entityId}\n plannedActivities={plannedActivities}\n refreshKey={refreshKey}\n onAddNew={handleAddNew}\n onEditActivity={onEditActivity}\n entityCompanyName={entityCompanyName}\n />\n\n <ActivityHistorySection\n entityId={entityId}\n useCanonicalInteractions={useCanonicalInteractions}\n refreshKey={refreshKey}\n onEditActivity={onEditActivity}\n runMutation={runGuardedMutation}\n />\n </div>\n )\n}\n\nexport default ActivityLogTab\n"],
5
+ "mappings": ";AAqDI,SACE,KADF;AAnDJ,SAAS,sBAAsB;AAE/B,SAAS,8BAA8B;AAgChC,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,2BAA2B;AAAA,EAC3B;AACF,GAAwB;AACtB,QAAM,eAAe,CAAC,SAAuB;AAC3C,QAAI,cAAe,eAAc,IAAI;AAAA,QAChC,qBAAoB;AAAA,EAC3B;AAEA,SACE,qBAAC,SAAI,WAAU,aACb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA,UAAU;AAAA,QACV;AAAA,QACA;AAAA;AAAA,IACF;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,aAAa;AAAA;AAAA,IACf;AAAA,KACF;AAEJ;AAEA,IAAO,yBAAQ;",
6
6
  "names": []
7
7
  }