@open-mercato/core 0.5.1-develop.2996.ce62fd491c → 0.5.1-develop.3036.f02c281f23
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.
- package/.turbo/turbo-build.log +1 -1
- package/dist/modules/auth/api/sidebar/preferences/route.js +2 -2
- package/dist/modules/auth/api/sidebar/preferences/route.js.map +2 -2
- package/dist/modules/auth/api/sidebar/variants/[id]/route.js +2 -2
- package/dist/modules/auth/api/sidebar/variants/[id]/route.js.map +2 -2
- package/dist/modules/auth/api/sidebar/variants/route.js +1 -1
- package/dist/modules/auth/api/sidebar/variants/route.js.map +2 -2
- package/dist/modules/auth/backend/sidebar-customization/page.meta.js +1 -0
- package/dist/modules/auth/backend/sidebar-customization/page.meta.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/route.js +30 -20
- package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/companies/route.js +12 -7
- package/dist/modules/customers/api/companies/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +12 -7
- package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
- package/dist/modules/customers/api/people/route.js +12 -7
- package/dist/modules/customers/api/people/route.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +21 -0
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +27 -30
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/components/detail/ActivitiesAddNewMenu.js +56 -0
- package/dist/modules/customers/components/detail/ActivitiesAddNewMenu.js.map +7 -0
- package/dist/modules/customers/components/detail/ActivitiesCard.js +175 -0
- package/dist/modules/customers/components/detail/ActivitiesCard.js.map +7 -0
- package/dist/modules/customers/components/detail/ActivitiesDayStrip.js +324 -0
- package/dist/modules/customers/components/detail/ActivitiesDayStrip.js.map +7 -0
- package/dist/modules/customers/components/detail/ActivitiesSection.js +62 -13
- package/dist/modules/customers/components/detail/ActivitiesSection.js.map +2 -2
- package/dist/modules/customers/components/detail/ActivityLogTab.js +14 -23
- package/dist/modules/customers/components/detail/ActivityLogTab.js.map +2 -2
- package/dist/modules/customers/components/detail/ActivityTimeline.js +13 -13
- package/dist/modules/customers/components/detail/ActivityTimeline.js.map +2 -2
- package/dist/modules/customers/components/detail/ActivityTimelineFilters.js +35 -22
- package/dist/modules/customers/components/detail/ActivityTimelineFilters.js.map +2 -2
- package/dist/modules/customers/components/detail/AiActionChips.js +15 -22
- package/dist/modules/customers/components/detail/AiActionChips.js.map +2 -2
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +196 -28
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/DateTimeFields.js +2 -2
- package/dist/modules/customers/components/detail/schedule/DateTimeFields.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/FooterFields.js +14 -2
- package/dist/modules/customers/components/detail/schedule/FooterFields.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/LinkedEntitiesField.js +9 -2
- package/dist/modules/customers/components/detail/schedule/LinkedEntitiesField.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/ParticipantsField.js +9 -2
- package/dist/modules/customers/components/detail/schedule/ParticipantsField.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/fieldConfig.js +25 -4
- package/dist/modules/customers/components/detail/schedule/fieldConfig.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js +20 -3
- package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
- package/package.json +3 -3
- package/src/modules/auth/api/sidebar/preferences/route.ts +2 -2
- package/src/modules/auth/api/sidebar/variants/[id]/route.ts +2 -2
- package/src/modules/auth/api/sidebar/variants/route.ts +1 -1
- package/src/modules/auth/backend/sidebar-customization/page.meta.ts +1 -8
- package/src/modules/customers/api/companies/[id]/route.ts +30 -20
- package/src/modules/customers/api/companies/route.ts +12 -7
- package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +12 -7
- package/src/modules/customers/api/people/route.ts +12 -7
- package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +22 -0
- package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +28 -21
- package/src/modules/customers/components/detail/ActivitiesAddNewMenu.tsx +67 -0
- package/src/modules/customers/components/detail/ActivitiesCard.tsx +231 -0
- package/src/modules/customers/components/detail/ActivitiesDayStrip.tsx +390 -0
- package/src/modules/customers/components/detail/ActivitiesSection.tsx +91 -40
- package/src/modules/customers/components/detail/ActivityLogTab.tsx +25 -23
- package/src/modules/customers/components/detail/ActivityTimeline.tsx +15 -19
- package/src/modules/customers/components/detail/ActivityTimelineFilters.tsx +36 -29
- package/src/modules/customers/components/detail/AiActionChips.tsx +17 -23
- package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +233 -41
- package/src/modules/customers/components/detail/schedule/DateTimeFields.tsx +6 -2
- package/src/modules/customers/components/detail/schedule/FooterFields.tsx +22 -2
- package/src/modules/customers/components/detail/schedule/LinkedEntitiesField.tsx +10 -2
- package/src/modules/customers/components/detail/schedule/ParticipantsField.tsx +10 -2
- package/src/modules/customers/components/detail/schedule/fieldConfig.ts +26 -6
- package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +32 -3
- package/src/modules/customers/i18n/de.json +69 -2
- package/src/modules/customers/i18n/en.json +69 -2
- package/src/modules/customers/i18n/es.json +69 -2
- package/src/modules/customers/i18n/pl.json +68 -1
|
@@ -30,6 +30,61 @@ const TYPE_TABS = [
|
|
|
30
30
|
{ type: "task", icon: Check, labelKey: "customers.schedule.types.task", fallback: "Task" },
|
|
31
31
|
{ type: "email", icon: Mail, labelKey: "customers.schedule.types.email", fallback: "Email" }
|
|
32
32
|
];
|
|
33
|
+
const TYPE_CHROME = {
|
|
34
|
+
meeting: {
|
|
35
|
+
titleKey: "customers.schedule.meeting.title",
|
|
36
|
+
titleFallback: "New meeting",
|
|
37
|
+
subtitleKey: "customers.schedule.meeting.subtitle",
|
|
38
|
+
subtitleFallback: "Block time on the calendar with attendees",
|
|
39
|
+
saveKey: "customers.schedule.meeting.save",
|
|
40
|
+
saveFallback: "Save activity",
|
|
41
|
+
saveIcon: Calendar
|
|
42
|
+
},
|
|
43
|
+
call: {
|
|
44
|
+
titleKey: "customers.schedule.call.title",
|
|
45
|
+
titleFallback: "Log call",
|
|
46
|
+
subtitleKey: "customers.schedule.call.subtitle",
|
|
47
|
+
subtitleFallback: "Log a call you just had or schedule one",
|
|
48
|
+
saveKey: "customers.schedule.call.save",
|
|
49
|
+
saveFallback: "Log call",
|
|
50
|
+
saveIcon: Phone
|
|
51
|
+
},
|
|
52
|
+
task: {
|
|
53
|
+
titleKey: "customers.schedule.task.title",
|
|
54
|
+
titleFallback: "New task",
|
|
55
|
+
subtitleKey: "customers.schedule.task.subtitle",
|
|
56
|
+
subtitleFallback: "Capture something to follow up on",
|
|
57
|
+
saveKey: "customers.schedule.task.save",
|
|
58
|
+
saveFallback: "Save task",
|
|
59
|
+
saveIcon: Check
|
|
60
|
+
},
|
|
61
|
+
email: {
|
|
62
|
+
titleKey: "customers.schedule.email.title",
|
|
63
|
+
titleFallback: "Compose email",
|
|
64
|
+
subtitleKey: "customers.schedule.email.subtitle",
|
|
65
|
+
subtitleFallback: "Compose and send a tracked email",
|
|
66
|
+
saveKey: "customers.schedule.email.save",
|
|
67
|
+
saveFallback: "Send email",
|
|
68
|
+
saveIcon: Mail
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
const CALL_DIRECTIONS = [
|
|
72
|
+
{ key: "outbound", labelKey: "customers.schedule.call.direction.outbound", labelFallback: "Outbound", dot: "bg-status-info-icon" },
|
|
73
|
+
{ key: "inbound", labelKey: "customers.schedule.call.direction.inbound", labelFallback: "Inbound", dot: "bg-status-success-icon" }
|
|
74
|
+
];
|
|
75
|
+
const CALL_OUTCOMES = [
|
|
76
|
+
{ key: "connected", labelKey: "customers.schedule.call.outcome.connected", labelFallback: "Connected", dot: "bg-status-success-icon" },
|
|
77
|
+
{ key: "voicemail", labelKey: "customers.schedule.call.outcome.voicemail", labelFallback: "Voicemail", dot: "bg-status-warning-icon" },
|
|
78
|
+
{ key: "noanswer", labelKey: "customers.schedule.call.outcome.noAnswer", labelFallback: "No answer", dot: "bg-muted-foreground" },
|
|
79
|
+
{ key: "busy", labelKey: "customers.schedule.call.outcome.busy", labelFallback: "Busy", dot: "bg-status-warning-icon" },
|
|
80
|
+
{ key: "badnumber", labelKey: "customers.schedule.call.outcome.badNumber", labelFallback: "Bad number", dot: "bg-status-error-icon" }
|
|
81
|
+
];
|
|
82
|
+
const TASK_PRIORITIES = [
|
|
83
|
+
{ key: "low", labelKey: "customers.schedule.task.priority.low", labelFallback: "Low", dot: "bg-muted-foreground" },
|
|
84
|
+
{ key: "medium", labelKey: "customers.schedule.task.priority.medium", labelFallback: "Medium", dot: "bg-status-info-icon" },
|
|
85
|
+
{ key: "high", labelKey: "customers.schedule.task.priority.high", labelFallback: "High", dot: "bg-status-warning-icon" },
|
|
86
|
+
{ key: "urgent", labelKey: "customers.schedule.task.priority.urgent", labelFallback: "Urgent", dot: "bg-status-error-icon" }
|
|
87
|
+
];
|
|
33
88
|
function ScheduleActivityDialog({
|
|
34
89
|
open,
|
|
35
90
|
onClose,
|
|
@@ -45,6 +100,29 @@ function ScheduleActivityDialog({
|
|
|
45
100
|
const state = useScheduleFormState({ open, editData: editData ?? null });
|
|
46
101
|
const visibleFields = FIELD_VISIBILITY[state.activityType];
|
|
47
102
|
const { confirm, ConfirmDialogElement } = useConfirmDialog();
|
|
103
|
+
const isEditing = Boolean(editData?.id);
|
|
104
|
+
const chrome = TYPE_CHROME[state.activityType];
|
|
105
|
+
const SaveIcon = chrome.saveIcon;
|
|
106
|
+
const [callDirection, setCallDirection] = React.useState("outbound");
|
|
107
|
+
const [callOutcome, setCallOutcome] = React.useState(null);
|
|
108
|
+
const [callPhoneNumber, setCallPhoneNumber] = React.useState("");
|
|
109
|
+
const [taskPriority, setTaskPriority] = React.useState("medium");
|
|
110
|
+
React.useEffect(() => {
|
|
111
|
+
if (!open) return;
|
|
112
|
+
const raw = editData;
|
|
113
|
+
const cv = raw?.customValues && typeof raw.customValues === "object" ? raw.customValues : null;
|
|
114
|
+
setCallDirection(typeof cv?.callDirection === "string" && cv.callDirection === "inbound" ? "inbound" : "outbound");
|
|
115
|
+
setCallOutcome(typeof cv?.callOutcome === "string" ? cv.callOutcome : null);
|
|
116
|
+
setCallPhoneNumber(typeof cv?.callPhoneNumber === "string" ? cv.callPhoneNumber : "");
|
|
117
|
+
setTaskPriority(typeof cv?.taskPriority === "string" ? cv.taskPriority : "medium");
|
|
118
|
+
}, [open, editData]);
|
|
119
|
+
React.useEffect(() => {
|
|
120
|
+
if (!open || isEditing) return;
|
|
121
|
+
setCallDirection("outbound");
|
|
122
|
+
setCallOutcome(null);
|
|
123
|
+
setCallPhoneNumber("");
|
|
124
|
+
setTaskPriority("medium");
|
|
125
|
+
}, [state.activityType, open, isEditing]);
|
|
48
126
|
const formSnapshot = React.useMemo(() => JSON.stringify({
|
|
49
127
|
activityType: state.activityType,
|
|
50
128
|
title: state.title,
|
|
@@ -192,9 +270,18 @@ function ScheduleActivityDialog({
|
|
|
192
270
|
try {
|
|
193
271
|
const scheduledAt = state.allDay ? (/* @__PURE__ */ new Date(`${state.date}T00:00:00`)).toISOString() : (/* @__PURE__ */ new Date(`${state.date}T${state.startTime}:00`)).toISOString();
|
|
194
272
|
const recurrenceRule = state.recurrenceEnabled ? buildRecurrenceRule(state.recurrenceDays, state.recurrenceEndType, state.recurrenceCount, state.recurrenceEndDate) : null;
|
|
195
|
-
const
|
|
273
|
+
const isSaveEdit = Boolean(editData?.id);
|
|
274
|
+
const customValues = {};
|
|
275
|
+
if (state.activityType === "call") {
|
|
276
|
+
customValues.callDirection = callDirection;
|
|
277
|
+
if (callOutcome) customValues.callOutcome = callOutcome;
|
|
278
|
+
if (callPhoneNumber.trim()) customValues.callPhoneNumber = callPhoneNumber.trim();
|
|
279
|
+
}
|
|
280
|
+
if (state.activityType === "task") {
|
|
281
|
+
customValues.taskPriority = taskPriority;
|
|
282
|
+
}
|
|
196
283
|
const payload = {
|
|
197
|
-
...
|
|
284
|
+
...isSaveEdit ? { id: editData.id } : {},
|
|
198
285
|
entityId,
|
|
199
286
|
dealId,
|
|
200
287
|
interactionType: state.activityType,
|
|
@@ -211,16 +298,17 @@ function ScheduleActivityDialog({
|
|
|
211
298
|
guestPermissions: visibleFields.has("participants") && state.participants.length > 0 ? state.guestPermissions : null,
|
|
212
299
|
linkedEntities: state.linkedEntities.length > 0 ? state.linkedEntities.map((e) => ({ id: e.id, type: e.type, label: e.label })) : null,
|
|
213
300
|
reminderMinutes: visibleFields.has("reminder") ? state.reminderMinutes : null,
|
|
214
|
-
visibility: visibleFields.has("visibility") ? state.visibility : null
|
|
301
|
+
visibility: visibleFields.has("visibility") ? state.visibility : null,
|
|
302
|
+
...Object.keys(customValues).length > 0 ? { customValues } : {}
|
|
215
303
|
};
|
|
216
304
|
await runGuardedMutation(
|
|
217
305
|
() => apiCallOrThrow("/api/customers/interactions", {
|
|
218
|
-
method:
|
|
306
|
+
method: isSaveEdit ? "PUT" : "POST",
|
|
219
307
|
headers: { "content-type": "application/json" },
|
|
220
308
|
body: JSON.stringify(payload)
|
|
221
309
|
}),
|
|
222
310
|
{
|
|
223
|
-
operation:
|
|
311
|
+
operation: isSaveEdit ? "updateActivity" : "createActivity",
|
|
224
312
|
interactionId: editData?.id ?? null,
|
|
225
313
|
interactionType: state.activityType
|
|
226
314
|
}
|
|
@@ -246,18 +334,16 @@ function ScheduleActivityDialog({
|
|
|
246
334
|
if (!o) void guardedClose();
|
|
247
335
|
}, children: [
|
|
248
336
|
ConfirmDialogElement,
|
|
249
|
-
/* @__PURE__ */ jsxs(DialogContent, { className: "flex max-h-[90vh] flex-col overflow-hidden border-border p-0 shadow-xl sm:max-w-[
|
|
250
|
-
/* @__PURE__ */ jsx(VisuallyHidden, { children: /* @__PURE__ */ jsx(DialogTitle, { children:
|
|
337
|
+
/* @__PURE__ */ jsxs(DialogContent, { className: "flex max-h-[90vh] flex-col overflow-hidden border-border p-0 shadow-xl sm:max-w-[760px] sm:rounded-xl [&>[data-dialog-close]]:hidden", onKeyDown: handleKeyDown, "aria-describedby": void 0, children: [
|
|
338
|
+
/* @__PURE__ */ jsx(VisuallyHidden, { children: /* @__PURE__ */ jsx(DialogTitle, { children: isEditing ? t("customers.schedule.editTitle", "Edit activity") : t(chrome.titleKey, chrome.titleFallback) }) }),
|
|
251
339
|
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-start justify-between gap-3 border-b border-border bg-background px-6 py-5", children: [
|
|
252
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1
|
|
253
|
-
/* @__PURE__ */ jsx("h2", { className: "text-lg font-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
] })
|
|
260
|
-
] })
|
|
340
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
341
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold leading-tight tracking-tight text-foreground", children: isEditing ? t("customers.schedule.editTitle", "Edit activity") : t(chrome.titleKey, chrome.titleFallback) }),
|
|
342
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t(chrome.subtitleKey, chrome.subtitleFallback) }),
|
|
343
|
+
entityName ? /* @__PURE__ */ jsxs("p", { className: "mt-0.5 text-xs text-muted-foreground/80", children: [
|
|
344
|
+
t("customers.schedule.context", "On timeline: {{name}}", { name: entityName }),
|
|
345
|
+
companyName ? ` \xB7 ${companyName}` : ""
|
|
346
|
+
] }) : null
|
|
261
347
|
] }),
|
|
262
348
|
/* @__PURE__ */ jsx(IconButton, { type: "button", variant: "ghost", size: "sm", onClick: () => {
|
|
263
349
|
void guardedClose();
|
|
@@ -269,21 +355,20 @@ function ScheduleActivityDialog({
|
|
|
269
355
|
/* @__PURE__ */ jsx(AlertTitle, { children: t("customers.schedule.conflict.title", "Calendar conflict") }),
|
|
270
356
|
/* @__PURE__ */ jsx(AlertDescription, { children: state.conflict })
|
|
271
357
|
] }),
|
|
272
|
-
/* @__PURE__ */ jsx("div", { className: "
|
|
358
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 gap-2", children: TYPE_TABS.map(({ type, icon: Icon, labelKey, fallback }) => {
|
|
273
359
|
const isActive = state.activityType === type;
|
|
274
360
|
return /* @__PURE__ */ jsxs(
|
|
275
|
-
|
|
361
|
+
"button",
|
|
276
362
|
{
|
|
277
363
|
type: "button",
|
|
278
|
-
variant: "ghost",
|
|
279
|
-
size: "sm",
|
|
280
364
|
onClick: () => state.setActivityType(type),
|
|
365
|
+
"aria-pressed": isActive,
|
|
281
366
|
className: cn(
|
|
282
|
-
"h-
|
|
283
|
-
isActive ? "
|
|
367
|
+
"flex h-[80px] flex-col items-center justify-center gap-2 rounded-md border text-[14px] font-semibold transition-colors",
|
|
368
|
+
isActive ? "border-transparent bg-foreground text-background" : "border-border bg-card text-muted-foreground hover:border-foreground/40 hover:text-foreground"
|
|
284
369
|
),
|
|
285
370
|
children: [
|
|
286
|
-
/* @__PURE__ */ jsx(Icon, { className: "size-
|
|
371
|
+
/* @__PURE__ */ jsx(Icon, { className: "size-[18px]" }),
|
|
287
372
|
t(labelKey, fallback)
|
|
288
373
|
]
|
|
289
374
|
},
|
|
@@ -298,7 +383,7 @@ function ScheduleActivityDialog({
|
|
|
298
383
|
type: "text",
|
|
299
384
|
value: state.title,
|
|
300
385
|
onChange: (e) => state.setTitle(e.target.value),
|
|
301
|
-
placeholder: t("customers.schedule.titlePlaceholder", "Activity title..."),
|
|
386
|
+
placeholder: state.activityType === "email" ? t("customers.schedule.subjectPlaceholder", "Subject...") : t("customers.schedule.titlePlaceholder", "Activity title..."),
|
|
302
387
|
className: "w-full rounded-md border border-border bg-background px-3 py-2.5 text-sm text-foreground outline-none focus:border-foreground",
|
|
303
388
|
autoFocus: true
|
|
304
389
|
}
|
|
@@ -329,6 +414,77 @@ function ScheduleActivityDialog({
|
|
|
329
414
|
setRecurrenceEndDate: state.setRecurrenceEndDate
|
|
330
415
|
}
|
|
331
416
|
),
|
|
417
|
+
state.activityType === "call" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
418
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
419
|
+
/* @__PURE__ */ jsx("label", { className: "text-overline font-semibold uppercase text-muted-foreground tracking-wider", children: t("customers.schedule.call.directionLabel", "Direction") }),
|
|
420
|
+
/* @__PURE__ */ jsx("div", { className: "mt-2 flex flex-wrap gap-2", children: CALL_DIRECTIONS.map((opt) => {
|
|
421
|
+
const isActive = callDirection === opt.key;
|
|
422
|
+
return /* @__PURE__ */ jsxs(
|
|
423
|
+
"button",
|
|
424
|
+
{
|
|
425
|
+
type: "button",
|
|
426
|
+
"aria-pressed": isActive,
|
|
427
|
+
onClick: () => setCallDirection(opt.key),
|
|
428
|
+
className: cn(
|
|
429
|
+
"inline-flex items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-sm font-medium transition-colors",
|
|
430
|
+
isActive ? "border-transparent bg-foreground text-background" : "border-border bg-card text-muted-foreground hover:border-foreground/40"
|
|
431
|
+
),
|
|
432
|
+
children: [
|
|
433
|
+
/* @__PURE__ */ jsx("span", { className: cn("inline-block size-1.5 rounded-full", opt.dot), "aria-hidden": true }),
|
|
434
|
+
t(opt.labelKey, opt.labelFallback)
|
|
435
|
+
]
|
|
436
|
+
},
|
|
437
|
+
opt.key
|
|
438
|
+
);
|
|
439
|
+
}) })
|
|
440
|
+
] }),
|
|
441
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
442
|
+
/* @__PURE__ */ jsx("label", { className: "text-overline font-semibold uppercase text-muted-foreground tracking-wider", children: t("customers.schedule.call.outcomeLabel", "Outcome") }),
|
|
443
|
+
/* @__PURE__ */ jsx("div", { className: "mt-2 flex flex-wrap gap-2", children: CALL_OUTCOMES.map((opt) => {
|
|
444
|
+
const isActive = callOutcome === opt.key;
|
|
445
|
+
return /* @__PURE__ */ jsxs(
|
|
446
|
+
"button",
|
|
447
|
+
{
|
|
448
|
+
type: "button",
|
|
449
|
+
"aria-pressed": isActive,
|
|
450
|
+
onClick: () => setCallOutcome(isActive ? null : opt.key),
|
|
451
|
+
className: cn(
|
|
452
|
+
"inline-flex items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-sm font-medium transition-colors",
|
|
453
|
+
isActive ? "border-transparent bg-foreground text-background" : "border-border bg-card text-muted-foreground hover:border-foreground/40"
|
|
454
|
+
),
|
|
455
|
+
children: [
|
|
456
|
+
/* @__PURE__ */ jsx("span", { className: cn("inline-block size-1.5 rounded-full", opt.dot), "aria-hidden": true }),
|
|
457
|
+
t(opt.labelKey, opt.labelFallback)
|
|
458
|
+
]
|
|
459
|
+
},
|
|
460
|
+
opt.key
|
|
461
|
+
);
|
|
462
|
+
}) })
|
|
463
|
+
] })
|
|
464
|
+
] }),
|
|
465
|
+
state.activityType === "task" && /* @__PURE__ */ jsxs("div", { children: [
|
|
466
|
+
/* @__PURE__ */ jsx("label", { className: "text-overline font-semibold uppercase text-muted-foreground tracking-wider", children: t("customers.schedule.task.priorityLabel", "Priority") }),
|
|
467
|
+
/* @__PURE__ */ jsx("div", { className: "mt-2 flex flex-wrap gap-2", children: TASK_PRIORITIES.map((opt) => {
|
|
468
|
+
const isActive = taskPriority === opt.key;
|
|
469
|
+
return /* @__PURE__ */ jsxs(
|
|
470
|
+
"button",
|
|
471
|
+
{
|
|
472
|
+
type: "button",
|
|
473
|
+
"aria-pressed": isActive,
|
|
474
|
+
onClick: () => setTaskPriority(opt.key),
|
|
475
|
+
className: cn(
|
|
476
|
+
"inline-flex items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-sm font-medium transition-colors",
|
|
477
|
+
isActive ? "border-transparent bg-foreground text-background" : "border-border bg-card text-muted-foreground hover:border-foreground/40"
|
|
478
|
+
),
|
|
479
|
+
children: [
|
|
480
|
+
/* @__PURE__ */ jsx("span", { className: cn("inline-block size-1.5 rounded-full", opt.dot), "aria-hidden": true }),
|
|
481
|
+
t(opt.labelKey, opt.labelFallback)
|
|
482
|
+
]
|
|
483
|
+
},
|
|
484
|
+
opt.key
|
|
485
|
+
);
|
|
486
|
+
}) })
|
|
487
|
+
] }),
|
|
332
488
|
/* @__PURE__ */ jsx(
|
|
333
489
|
ParticipantsField,
|
|
334
490
|
{
|
|
@@ -341,7 +497,19 @@ function ScheduleActivityDialog({
|
|
|
341
497
|
setGuestPermissions: state.setGuestPermissions
|
|
342
498
|
}
|
|
343
499
|
),
|
|
344
|
-
/* @__PURE__ */
|
|
500
|
+
state.activityType === "call" ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
501
|
+
/* @__PURE__ */ jsx("label", { className: "text-overline font-semibold uppercase text-muted-foreground tracking-wider", children: t("customers.schedule.call.phoneLabel", "Phone number") }),
|
|
502
|
+
/* @__PURE__ */ jsx(
|
|
503
|
+
"input",
|
|
504
|
+
{
|
|
505
|
+
type: "tel",
|
|
506
|
+
value: callPhoneNumber,
|
|
507
|
+
onChange: (e) => setCallPhoneNumber(e.target.value),
|
|
508
|
+
placeholder: t("customers.schedule.call.phonePlaceholder", "+1 555 000 0000"),
|
|
509
|
+
className: "w-full rounded-md border border-border bg-background px-3 py-2.5 text-sm text-foreground outline-none focus:border-foreground"
|
|
510
|
+
}
|
|
511
|
+
)
|
|
512
|
+
] }) : /* @__PURE__ */ jsx(
|
|
345
513
|
LocationField,
|
|
346
514
|
{
|
|
347
515
|
visible: visibleFields,
|
|
@@ -388,9 +556,9 @@ function ScheduleActivityDialog({
|
|
|
388
556
|
/* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: () => {
|
|
389
557
|
void guardedClose();
|
|
390
558
|
}, className: "rounded-md border border-input bg-background px-5 py-3 text-sm font-semibold text-foreground", children: t("customers.schedule.cancel", "Cancel") }),
|
|
391
|
-
/* @__PURE__ */ jsxs(Button, { type: "button", onClick: handleSave, disabled: state.saving || !state.title.trim(), className: "flex items-center gap-2 rounded-md bg-
|
|
392
|
-
/* @__PURE__ */ jsx(
|
|
393
|
-
state.saving ? t("customers.schedule.saving", "Saving...") :
|
|
559
|
+
/* @__PURE__ */ jsxs(Button, { type: "button", onClick: handleSave, disabled: state.saving || !state.title.trim(), className: "flex items-center gap-2 rounded-md bg-foreground px-5 py-3 text-sm font-semibold text-background hover:bg-foreground/90 disabled:opacity-50", children: [
|
|
560
|
+
/* @__PURE__ */ jsx(SaveIcon, { className: "size-3.5" }),
|
|
561
|
+
state.saving ? t("customers.schedule.saving", "Saving...") : isEditing ? t("customers.schedule.update", "Update activity") : t(chrome.saveKey, chrome.saveFallback)
|
|
394
562
|
] })
|
|
395
563
|
] })
|
|
396
564
|
] })
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/customers/components/detail/ScheduleActivityDialog.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { Users, Phone, Check, Mail, Calendar, AlertTriangle, X } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Dialog, DialogContent, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport { VisuallyHidden } from '@radix-ui/react-visually-hidden'\nimport { SwitchableMarkdownInput } from '@open-mercato/ui/backend/inputs'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport {\n useScheduleFormState,\n FIELD_VISIBILITY,\n getFieldLabel,\n DateTimeFields,\n ParticipantsField,\n LocationField,\n FooterFields,\n LinkedEntitiesField,\n} from './schedule'\nimport type { ActivityType, ScheduleActivityEditData } from './schedule'\n\nconst TYPE_TABS: Array<{ type: ActivityType; icon: React.ComponentType<{ className?: string }>; labelKey: string; fallback: string }> = [\n { type: 'meeting', icon: Users, labelKey: 'customers.schedule.types.meeting', fallback: 'Meeting' },\n { type: 'call', icon: Phone, labelKey: 'customers.schedule.types.call', fallback: 'Call' },\n { type: 'task', icon: Check, labelKey: 'customers.schedule.types.task', fallback: 'Task' },\n { type: 'email', icon: Mail, labelKey: 'customers.schedule.types.email', fallback: 'Email' },\n]\n\ninterface ScheduleActivityDialogProps {\n open: boolean\n onClose: () => void\n entityId: string\n dealId?: string | null\n entityName?: string\n companyName?: string | null\n entityType: 'company' | 'person' | 'deal'\n onActivityCreated?: () => void\n /** When provided, dialog opens in edit mode with pre-filled data */\n editData?: ScheduleActivityEditData | null\n}\n\nexport function ScheduleActivityDialog({\n open,\n onClose,\n entityId,\n dealId = null,\n entityName,\n companyName,\n entityType,\n onActivityCreated,\n editData,\n}: ScheduleActivityDialogProps) {\n const t = useT()\n const state = useScheduleFormState({ open, editData: editData ?? null })\n const visibleFields = FIELD_VISIBILITY[state.activityType]\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n\n const formSnapshot = React.useMemo(() => JSON.stringify({\n activityType: state.activityType,\n title: state.title,\n date: state.date,\n startTime: state.startTime,\n duration: state.duration,\n allDay: state.allDay,\n description: state.description,\n location: state.location,\n reminderMinutes: state.reminderMinutes,\n visibility: state.visibility,\n participants: state.participants,\n linkedEntities: state.linkedEntities,\n recurrenceEnabled: state.recurrenceEnabled,\n recurrenceDays: state.recurrenceDays,\n recurrenceEndType: state.recurrenceEndType,\n recurrenceCount: state.recurrenceCount,\n recurrenceEndDate: state.recurrenceEndDate,\n guestPermissions: state.guestPermissions,\n }), [\n state.activityType, state.title, state.date, state.startTime, state.duration, state.allDay,\n state.description, state.location, state.reminderMinutes, state.visibility, state.participants,\n state.linkedEntities, state.recurrenceEnabled, state.recurrenceDays, state.recurrenceEndType,\n state.recurrenceCount, state.recurrenceEndDate, state.guestPermissions,\n ])\n const initialSnapshotRef = React.useRef<string | null>(null)\n const snapshotOpenKeyRef = React.useRef<string | null>(null)\n const snapshotSettleCountRef = React.useRef(0)\n const openKey = open ? `${editData?.id ?? 'new'}` : null\n React.useEffect(() => {\n if (!open) {\n initialSnapshotRef.current = null\n snapshotOpenKeyRef.current = null\n snapshotSettleCountRef.current = 0\n return\n }\n if (snapshotOpenKeyRef.current !== openKey) {\n snapshotOpenKeyRef.current = openKey\n snapshotSettleCountRef.current = 0\n initialSnapshotRef.current = null\n }\n if (snapshotSettleCountRef.current < 2) {\n initialSnapshotRef.current = formSnapshot\n snapshotSettleCountRef.current += 1\n }\n }, [open, openKey, formSnapshot])\n\n const isDirty = React.useCallback(() => {\n if (initialSnapshotRef.current == null) return false\n return initialSnapshotRef.current !== formSnapshot\n }, [formSnapshot])\n\n const guardedClose = React.useCallback(async () => {\n if (!isDirty()) {\n onClose()\n return\n }\n const ok = await confirm({\n title: t('customers.schedule.discardConfirm.title', 'Discard unsaved changes?'),\n description: t(\n 'customers.schedule.discardConfirm.description',\n 'You have unsaved edits in this activity. Save them first or continue to discard them.',\n ),\n confirmText: t('customers.schedule.discardConfirm.confirm', 'Discard'),\n cancelText: t('customers.schedule.discardConfirm.cancel', 'Keep editing'),\n variant: 'destructive',\n })\n if (ok) onClose()\n }, [confirm, isDirty, onClose, t])\n\n const mutationContextId = React.useMemo(\n () => `customer-activity:${entityType}:${entityId}`,\n [entityId, entityType],\n )\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId: string\n entityType: 'company' | 'person' | 'deal'\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: mutationContextId,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n const mutationContext = React.useMemo(\n () => ({\n formId: mutationContextId,\n resourceKind:\n entityType === 'company'\n ? 'customers.company'\n : entityType === 'person'\n ? 'customers.person'\n : 'customers.deal',\n resourceId: entityId,\n entityType,\n retryLastMutation,\n }),\n [entityId, entityType, mutationContextId, retryLastMutation],\n )\n const runGuardedMutation = React.useCallback(\n async <T,>(operation: () => Promise<T>, mutationPayload: Record<string, unknown>) =>\n runMutation({\n operation,\n mutationPayload,\n context: mutationContext,\n }),\n [mutationContext, runMutation],\n )\n\n // Conflict detection -- debounced check when date/time/duration changes\n React.useEffect(() => {\n if (!open || state.allDay || !state.date || !state.startTime) {\n state.setConflict(null)\n return\n }\n const timer = setTimeout(async () => {\n try {\n const localStart = new Date(`${state.date}T${state.startTime}:00`)\n const params = new URLSearchParams({\n date: state.date,\n startTime: state.startTime,\n duration: String(state.duration),\n })\n if (editData?.id) {\n params.set('excludeId', editData.id)\n }\n if (!Number.isNaN(localStart.getTime())) {\n params.set('timezoneOffsetMinutes', String(-localStart.getTimezoneOffset()))\n }\n const data = await readApiResultOrThrow<{\n hasConflicts: boolean\n conflicts: Array<{ id: string; title: string | null; startTime: string; endTime: string; type: string }>\n }>(`/api/customers/interactions/conflicts?${params.toString()}`)\n if (data?.hasConflicts && Array.isArray(data.conflicts) && data.conflicts.length > 0) {\n const descriptions = data.conflicts\n .map((c) => `${c.startTime}\u2013${c.endTime}: ${c.title ?? c.type}`)\n .join(', ')\n state.setConflict(\n t('customers.schedule.conflict.description', 'Overlaps with: {{items}}', { items: descriptions }),\n )\n } else {\n state.setConflict(null)\n }\n } catch {\n state.setConflict(null)\n }\n }, 500)\n return () => clearTimeout(timer)\n }, [editData?.id, open, state.date, state.startTime, state.duration, state.allDay, t]) // eslint-disable-line react-hooks/exhaustive-deps\n\n const handleSave = React.useCallback(async () => {\n if (!state.title.trim()) return\n state.setSaving(true)\n try {\n const scheduledAt = state.allDay\n ? new Date(`${state.date}T00:00:00`).toISOString()\n : new Date(`${state.date}T${state.startTime}:00`).toISOString()\n\n const recurrenceRule = state.recurrenceEnabled\n ? buildRecurrenceRule(state.recurrenceDays, state.recurrenceEndType, state.recurrenceCount, state.recurrenceEndDate)\n : null\n\n const isEditing = Boolean(editData?.id)\n const payload = {\n ...(isEditing ? { id: editData!.id } : {}),\n entityId,\n dealId,\n interactionType: state.activityType,\n title: state.title.trim(),\n body: state.description.trim() || null,\n status: 'planned',\n scheduledAt,\n durationMinutes: visibleFields.has('duration') && !state.allDay ? state.duration : null,\n location: visibleFields.has('location') ? (state.location.trim() || null) : null,\n allDay: visibleFields.has('allDay') ? state.allDay : null,\n recurrenceRule: visibleFields.has('recurrence') ? recurrenceRule : null,\n recurrenceEnd: visibleFields.has('recurrence') && state.recurrenceEndType === 'date' && state.recurrenceEndDate\n ? new Date(state.recurrenceEndDate).toISOString()\n : null,\n participants: visibleFields.has('participants') && state.participants.length > 0\n ? state.participants.map((p) => ({ userId: p.userId, name: p.name, email: p.email, status: p.status ?? 'pending' }))\n : null,\n guestPermissions: visibleFields.has('participants') && state.participants.length > 0 ? state.guestPermissions : null,\n linkedEntities: state.linkedEntities.length > 0\n ? state.linkedEntities.map((e) => ({ id: e.id, type: e.type, label: e.label }))\n : null,\n reminderMinutes: visibleFields.has('reminder') ? state.reminderMinutes : null,\n visibility: visibleFields.has('visibility') ? state.visibility : null,\n }\n await runGuardedMutation(\n () =>\n apiCallOrThrow('/api/customers/interactions', {\n method: isEditing ? 'PUT' : 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n }),\n {\n operation: isEditing ? 'updateActivity' : 'createActivity',\n interactionId: editData?.id ?? null,\n interactionType: state.activityType,\n },\n )\n flash(t('customers.schedule.saved', 'Activity scheduled'), 'success')\n onClose()\n // Delay data reload so the dialog can unmount cleanly and Radix restores body scroll\n requestAnimationFrame(() => { onActivityCreated?.() })\n } catch {\n flash(t('customers.schedule.error', 'Failed to schedule activity'), 'error')\n } finally {\n state.setSaving(false)\n }\n }, [state.activityType, state.allDay, state.date, state.description, dealId, state.duration, editData, entityId, state.guestPermissions, state.linkedEntities, state.location, onActivityCreated, onClose, state.participants, state.recurrenceCount, state.recurrenceDays, state.recurrenceEnabled, state.recurrenceEndDate, state.recurrenceEndType, state.reminderMinutes, runGuardedMutation, state.startTime, t, state.title, state.visibility, visibleFields]) // eslint-disable-line react-hooks/exhaustive-deps\n\n const handleKeyDown = React.useCallback((e: React.KeyboardEvent) => {\n if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {\n e.preventDefault()\n handleSave()\n }\n }, [handleSave])\n\n return (\n <Dialog open={open} onOpenChange={(o) => { if (!o) void guardedClose() }}>\n {ConfirmDialogElement}\n <DialogContent className=\"flex max-h-[90vh] flex-col overflow-hidden border-border p-0 shadow-xl sm:max-w-[680px] sm:rounded-xl [&>[data-dialog-close]]:hidden\" onKeyDown={handleKeyDown} aria-describedby={undefined}>\n <VisuallyHidden>\n <DialogTitle>{editData ? t('customers.schedule.editTitle', 'Edit activity') : t('customers.schedule.title', 'Schedule activity')}</DialogTitle>\n </VisuallyHidden>\n\n {/* Header */}\n <div className=\"flex shrink-0 items-start justify-between gap-3 border-b border-border bg-background px-6 py-5\">\n <div className=\"flex flex-col gap-1.5\">\n <h2 className=\"text-lg font-bold leading-tight text-foreground\">\n {editData ? t('customers.schedule.editTitle', 'Edit activity') : t('customers.schedule.title', 'Schedule activity')}\n </h2>\n {entityName && (\n <div className=\"flex items-center gap-1.5\">\n <span className=\"inline-block size-3.5 rounded-full bg-status-success-icon shrink-0\" />\n <span className=\"text-xs text-muted-foreground\">\n {t('customers.schedule.context', 'On timeline: {{name}}', { name: entityName })}\n {companyName && ` \u00B7 ${companyName}`}\n </span>\n </div>\n )}\n </div>\n <IconButton type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => { void guardedClose() }} className=\"flex size-9 shrink-0 items-center justify-center rounded-md border border-border bg-background\" aria-label={t('customers.schedule.cancel', 'Cancel')}>\n <X className=\"size-4 text-muted-foreground\" />\n </IconButton>\n </div>\n\n <div className=\"flex-1 overflow-y-auto\">\n <div className=\"flex flex-col gap-4 bg-background p-6\">\n\n {/* Conflict warning */}\n {state.conflict && (\n <Alert variant=\"warning\" className=\"rounded-lg\">\n <AlertTriangle className=\"size-5\" />\n <AlertTitle>\n {t('customers.schedule.conflict.title', 'Calendar conflict')}\n </AlertTitle>\n <AlertDescription>{state.conflict}</AlertDescription>\n </Alert>\n )}\n\n {/* Type tabs */}\n <div className=\"flex gap-0.5 rounded-md border border-border bg-muted p-1\">\n {TYPE_TABS.map(({ type, icon: Icon, labelKey, fallback }) => {\n const isActive = state.activityType === type\n return (\n <Button\n key={type}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => state.setActivityType(type)}\n className={cn(\n 'h-auto flex items-center gap-2 rounded-md px-3.5 py-2 text-sm transition-colors',\n isActive\n ? 'bg-background font-semibold text-foreground shadow-sm'\n : 'bg-transparent font-normal text-muted-foreground',\n )}\n >\n <Icon className=\"size-3.5\" />\n {t(labelKey, fallback)}\n </Button>\n )\n })}\n </div>\n\n {/* Title */}\n <div className=\"flex flex-col gap-1.5\">\n <label className=\"text-overline font-semibold text-muted-foreground tracking-wider\">\n {getFieldLabel(state.activityType, 'title', t, 'customers.schedule.titleLabel', 'Title')}\n </label>\n <input\n type=\"text\"\n value={state.title}\n onChange={(e) => state.setTitle(e.target.value)}\n placeholder={t('customers.schedule.titlePlaceholder', 'Activity title...')}\n className=\"w-full rounded-md border border-border bg-background px-3 py-2.5 text-sm text-foreground outline-none focus:border-foreground\"\n autoFocus\n />\n </div>\n\n {/* Date/Time/Duration */}\n <DateTimeFields\n visible={visibleFields}\n activityType={state.activityType}\n date={state.date}\n setDate={state.setDate}\n startTime={state.startTime}\n setStartTime={state.setStartTime}\n duration={state.duration}\n setDuration={state.setDuration}\n allDay={state.allDay}\n setAllDay={state.setAllDay}\n recurrenceEnabled={state.recurrenceEnabled}\n setRecurrenceEnabled={state.setRecurrenceEnabled}\n recurrenceDays={state.recurrenceDays}\n toggleRecurrenceDay={state.toggleRecurrenceDay}\n recurrenceEndType={state.recurrenceEndType}\n setRecurrenceEndType={state.setRecurrenceEndType}\n recurrenceCount={state.recurrenceCount}\n setRecurrenceCount={state.setRecurrenceCount}\n recurrenceEndDate={state.recurrenceEndDate}\n setRecurrenceEndDate={state.setRecurrenceEndDate}\n />\n\n {/* Participants */}\n <ParticipantsField\n visible={visibleFields}\n activityType={state.activityType}\n participants={state.participants}\n setParticipants={state.setParticipants}\n removeParticipant={state.removeParticipant}\n guestPermissions={state.guestPermissions}\n setGuestPermissions={state.setGuestPermissions}\n />\n\n {/* Location */}\n <LocationField\n visible={visibleFields}\n activityType={state.activityType}\n location={state.location}\n setLocation={state.setLocation}\n />\n\n {/* Linked Entities */}\n <LinkedEntitiesField\n visible={visibleFields}\n activityType={state.activityType}\n linkedEntities={state.linkedEntities}\n setLinkedEntities={state.setLinkedEntities}\n />\n\n {/* Description */}\n <div>\n <label className=\"text-overline font-semibold uppercase text-muted-foreground tracking-wider\">\n {getFieldLabel(state.activityType, 'description', t, 'customers.schedule.description', 'Description')}\n </label>\n <div className=\"mt-[8px]\">\n <SwitchableMarkdownInput\n value={state.description}\n onChange={state.setDescription}\n isMarkdownEnabled={state.markdownEnabled}\n height={120}\n placeholder={t('customers.schedule.descriptionPlaceholder', 'Add details...')}\n />\n </div>\n </div>\n\n {/* Reminder + Visibility */}\n <FooterFields\n visible={visibleFields}\n activityType={state.activityType}\n reminderMinutes={state.reminderMinutes}\n setReminderMinutes={state.setReminderMinutes}\n visibility={state.visibility}\n setVisibility={state.setVisibility}\n />\n\n </div>\n </div>\n\n {/* Footer */}\n <div className=\"flex shrink-0 items-center justify-end gap-2.5 border-t border-border bg-muted/50 px-6 py-4\">\n <Button type=\"button\" variant=\"outline\" onClick={() => { void guardedClose() }} className=\"rounded-md border border-input bg-background px-5 py-3 text-sm font-semibold text-foreground\">\n {t('customers.schedule.cancel', 'Cancel')}\n </Button>\n <Button type=\"button\" onClick={handleSave} disabled={state.saving || !state.title.trim()} className=\"flex items-center gap-2 rounded-md bg-primary px-5 py-3 text-sm font-semibold text-primary-foreground disabled:opacity-50\">\n <Calendar className=\"size-3.5\" />\n {state.saving\n ? t('customers.schedule.saving', 'Saving...')\n : editData\n ? t('customers.schedule.update', 'Update activity')\n : t('customers.schedule.save', 'Save activity')}\n </Button>\n </div>\n </DialogContent>\n </Dialog>\n )\n}\n\nexport type { ScheduleActivityEditData }\n\nfunction buildRecurrenceRule(\n days: boolean[],\n endType: 'never' | 'count' | 'date',\n count: number,\n endDate: string,\n): string {\n const dayNames = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU']\n const selectedDays = days.map((active, i) => (active ? dayNames[i] : null)).filter(Boolean)\n let rule = `FREQ=WEEKLY;BYDAY=${selectedDays.join(',')}`\n if (endType === 'count') rule += `;COUNT=${count}`\n if (endType === 'date' && endDate) rule += `;UNTIL=${endDate.replace(/-/g, '')}T235959Z`\n return rule\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { Users, Phone, Check, Mail, Calendar, AlertTriangle, X } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Dialog, DialogContent, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport { VisuallyHidden } from '@radix-ui/react-visually-hidden'\nimport { SwitchableMarkdownInput } from '@open-mercato/ui/backend/inputs'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport {\n useScheduleFormState,\n FIELD_VISIBILITY,\n getFieldLabel,\n DateTimeFields,\n ParticipantsField,\n LocationField,\n FooterFields,\n LinkedEntitiesField,\n} from './schedule'\nimport type { ActivityType, ScheduleActivityEditData } from './schedule'\n\nconst TYPE_TABS: Array<{ type: ActivityType; icon: React.ComponentType<{ className?: string }>; labelKey: string; fallback: string }> = [\n { type: 'meeting', icon: Users, labelKey: 'customers.schedule.types.meeting', fallback: 'Meeting' },\n { type: 'call', icon: Phone, labelKey: 'customers.schedule.types.call', fallback: 'Call' },\n { type: 'task', icon: Check, labelKey: 'customers.schedule.types.task', fallback: 'Task' },\n { type: 'email', icon: Mail, labelKey: 'customers.schedule.types.email', fallback: 'Email' },\n]\n\ntype DialogChrome = { titleKey: string; titleFallback: string; subtitleKey: string; subtitleFallback: string; saveKey: string; saveFallback: string; saveIcon: React.ComponentType<{ className?: string }> }\n\nconst TYPE_CHROME: Record<ActivityType, DialogChrome> = {\n meeting: {\n titleKey: 'customers.schedule.meeting.title', titleFallback: 'New meeting',\n subtitleKey: 'customers.schedule.meeting.subtitle', subtitleFallback: 'Block time on the calendar with attendees',\n saveKey: 'customers.schedule.meeting.save', saveFallback: 'Save activity', saveIcon: Calendar,\n },\n call: {\n titleKey: 'customers.schedule.call.title', titleFallback: 'Log call',\n subtitleKey: 'customers.schedule.call.subtitle', subtitleFallback: 'Log a call you just had or schedule one',\n saveKey: 'customers.schedule.call.save', saveFallback: 'Log call', saveIcon: Phone,\n },\n task: {\n titleKey: 'customers.schedule.task.title', titleFallback: 'New task',\n subtitleKey: 'customers.schedule.task.subtitle', subtitleFallback: 'Capture something to follow up on',\n saveKey: 'customers.schedule.task.save', saveFallback: 'Save task', saveIcon: Check,\n },\n email: {\n titleKey: 'customers.schedule.email.title', titleFallback: 'Compose email',\n subtitleKey: 'customers.schedule.email.subtitle', subtitleFallback: 'Compose and send a tracked email',\n saveKey: 'customers.schedule.email.save', saveFallback: 'Send email', saveIcon: Mail,\n },\n}\n\nconst CALL_DIRECTIONS: Array<{ key: 'outbound' | 'inbound'; labelKey: string; labelFallback: string; dot: string }> = [\n { key: 'outbound', labelKey: 'customers.schedule.call.direction.outbound', labelFallback: 'Outbound', dot: 'bg-status-info-icon' },\n { key: 'inbound', labelKey: 'customers.schedule.call.direction.inbound', labelFallback: 'Inbound', dot: 'bg-status-success-icon' },\n]\n\nconst CALL_OUTCOMES: Array<{ key: string; labelKey: string; labelFallback: string; dot: string }> = [\n { key: 'connected', labelKey: 'customers.schedule.call.outcome.connected', labelFallback: 'Connected', dot: 'bg-status-success-icon' },\n { key: 'voicemail', labelKey: 'customers.schedule.call.outcome.voicemail', labelFallback: 'Voicemail', dot: 'bg-status-warning-icon' },\n { key: 'noanswer', labelKey: 'customers.schedule.call.outcome.noAnswer', labelFallback: 'No answer', dot: 'bg-muted-foreground' },\n { key: 'busy', labelKey: 'customers.schedule.call.outcome.busy', labelFallback: 'Busy', dot: 'bg-status-warning-icon' },\n { key: 'badnumber', labelKey: 'customers.schedule.call.outcome.badNumber', labelFallback: 'Bad number', dot: 'bg-status-error-icon' },\n]\n\nconst TASK_PRIORITIES: Array<{ key: string; labelKey: string; labelFallback: string; dot: string }> = [\n { key: 'low', labelKey: 'customers.schedule.task.priority.low', labelFallback: 'Low', dot: 'bg-muted-foreground' },\n { key: 'medium', labelKey: 'customers.schedule.task.priority.medium', labelFallback: 'Medium', dot: 'bg-status-info-icon' },\n { key: 'high', labelKey: 'customers.schedule.task.priority.high', labelFallback: 'High', dot: 'bg-status-warning-icon' },\n { key: 'urgent', labelKey: 'customers.schedule.task.priority.urgent', labelFallback: 'Urgent', dot: 'bg-status-error-icon' },\n]\n\ninterface ScheduleActivityDialogProps {\n open: boolean\n onClose: () => void\n entityId: string\n dealId?: string | null\n entityName?: string\n companyName?: string | null\n entityType: 'company' | 'person' | 'deal'\n onActivityCreated?: () => void\n /** When provided, dialog opens in edit mode with pre-filled data */\n editData?: ScheduleActivityEditData | null\n}\n\nexport function ScheduleActivityDialog({\n open,\n onClose,\n entityId,\n dealId = null,\n entityName,\n companyName,\n entityType,\n onActivityCreated,\n editData,\n}: ScheduleActivityDialogProps) {\n const t = useT()\n const state = useScheduleFormState({ open, editData: editData ?? null })\n const visibleFields = FIELD_VISIBILITY[state.activityType]\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const isEditing = Boolean(editData?.id)\n const chrome = TYPE_CHROME[state.activityType]\n const SaveIcon = chrome.saveIcon\n const [callDirection, setCallDirection] = React.useState<'outbound' | 'inbound'>('outbound')\n const [callOutcome, setCallOutcome] = React.useState<string | null>(null)\n const [callPhoneNumber, setCallPhoneNumber] = React.useState('')\n const [taskPriority, setTaskPriority] = React.useState<string>('medium')\n\n React.useEffect(() => {\n if (!open) return\n const raw = editData as (Record<string, unknown> & { customValues?: unknown }) | null | undefined\n const cv = (raw?.customValues && typeof raw.customValues === 'object' ? raw.customValues : null) as Record<string, unknown> | null\n setCallDirection(typeof cv?.callDirection === 'string' && cv.callDirection === 'inbound' ? 'inbound' : 'outbound')\n setCallOutcome(typeof cv?.callOutcome === 'string' ? cv.callOutcome : null)\n setCallPhoneNumber(typeof cv?.callPhoneNumber === 'string' ? cv.callPhoneNumber : '')\n setTaskPriority(typeof cv?.taskPriority === 'string' ? cv.taskPriority : 'medium')\n }, [open, editData])\n\n // Reset per-type chip state when the user switches activity type in create mode.\n // In edit mode, the persisted customValues should win, so we skip the reset.\n React.useEffect(() => {\n if (!open || isEditing) return\n setCallDirection('outbound')\n setCallOutcome(null)\n setCallPhoneNumber('')\n setTaskPriority('medium')\n }, [state.activityType, open, isEditing])\n\n const formSnapshot = React.useMemo(() => JSON.stringify({\n activityType: state.activityType,\n title: state.title,\n date: state.date,\n startTime: state.startTime,\n duration: state.duration,\n allDay: state.allDay,\n description: state.description,\n location: state.location,\n reminderMinutes: state.reminderMinutes,\n visibility: state.visibility,\n participants: state.participants,\n linkedEntities: state.linkedEntities,\n recurrenceEnabled: state.recurrenceEnabled,\n recurrenceDays: state.recurrenceDays,\n recurrenceEndType: state.recurrenceEndType,\n recurrenceCount: state.recurrenceCount,\n recurrenceEndDate: state.recurrenceEndDate,\n guestPermissions: state.guestPermissions,\n }), [\n state.activityType, state.title, state.date, state.startTime, state.duration, state.allDay,\n state.description, state.location, state.reminderMinutes, state.visibility, state.participants,\n state.linkedEntities, state.recurrenceEnabled, state.recurrenceDays, state.recurrenceEndType,\n state.recurrenceCount, state.recurrenceEndDate, state.guestPermissions,\n ])\n const initialSnapshotRef = React.useRef<string | null>(null)\n const snapshotOpenKeyRef = React.useRef<string | null>(null)\n const snapshotSettleCountRef = React.useRef(0)\n const openKey = open ? `${editData?.id ?? 'new'}` : null\n React.useEffect(() => {\n if (!open) {\n initialSnapshotRef.current = null\n snapshotOpenKeyRef.current = null\n snapshotSettleCountRef.current = 0\n return\n }\n if (snapshotOpenKeyRef.current !== openKey) {\n snapshotOpenKeyRef.current = openKey\n snapshotSettleCountRef.current = 0\n initialSnapshotRef.current = null\n }\n if (snapshotSettleCountRef.current < 2) {\n initialSnapshotRef.current = formSnapshot\n snapshotSettleCountRef.current += 1\n }\n }, [open, openKey, formSnapshot])\n\n const isDirty = React.useCallback(() => {\n if (initialSnapshotRef.current == null) return false\n return initialSnapshotRef.current !== formSnapshot\n }, [formSnapshot])\n\n const guardedClose = React.useCallback(async () => {\n if (!isDirty()) {\n onClose()\n return\n }\n const ok = await confirm({\n title: t('customers.schedule.discardConfirm.title', 'Discard unsaved changes?'),\n description: t(\n 'customers.schedule.discardConfirm.description',\n 'You have unsaved edits in this activity. Save them first or continue to discard them.',\n ),\n confirmText: t('customers.schedule.discardConfirm.confirm', 'Discard'),\n cancelText: t('customers.schedule.discardConfirm.cancel', 'Keep editing'),\n variant: 'destructive',\n })\n if (ok) onClose()\n }, [confirm, isDirty, onClose, t])\n\n const mutationContextId = React.useMemo(\n () => `customer-activity:${entityType}:${entityId}`,\n [entityId, entityType],\n )\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId: string\n entityType: 'company' | 'person' | 'deal'\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: mutationContextId,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n const mutationContext = React.useMemo(\n () => ({\n formId: mutationContextId,\n resourceKind:\n entityType === 'company'\n ? 'customers.company'\n : entityType === 'person'\n ? 'customers.person'\n : 'customers.deal',\n resourceId: entityId,\n entityType,\n retryLastMutation,\n }),\n [entityId, entityType, mutationContextId, retryLastMutation],\n )\n const runGuardedMutation = React.useCallback(\n async <T,>(operation: () => Promise<T>, mutationPayload: Record<string, unknown>) =>\n runMutation({\n operation,\n mutationPayload,\n context: mutationContext,\n }),\n [mutationContext, runMutation],\n )\n\n // Conflict detection -- debounced check when date/time/duration changes\n React.useEffect(() => {\n if (!open || state.allDay || !state.date || !state.startTime) {\n state.setConflict(null)\n return\n }\n const timer = setTimeout(async () => {\n try {\n const localStart = new Date(`${state.date}T${state.startTime}:00`)\n const params = new URLSearchParams({\n date: state.date,\n startTime: state.startTime,\n duration: String(state.duration),\n })\n if (editData?.id) {\n params.set('excludeId', editData.id)\n }\n if (!Number.isNaN(localStart.getTime())) {\n params.set('timezoneOffsetMinutes', String(-localStart.getTimezoneOffset()))\n }\n const data = await readApiResultOrThrow<{\n hasConflicts: boolean\n conflicts: Array<{ id: string; title: string | null; startTime: string; endTime: string; type: string }>\n }>(`/api/customers/interactions/conflicts?${params.toString()}`)\n if (data?.hasConflicts && Array.isArray(data.conflicts) && data.conflicts.length > 0) {\n const descriptions = data.conflicts\n .map((c) => `${c.startTime}\u2013${c.endTime}: ${c.title ?? c.type}`)\n .join(', ')\n state.setConflict(\n t('customers.schedule.conflict.description', 'Overlaps with: {{items}}', { items: descriptions }),\n )\n } else {\n state.setConflict(null)\n }\n } catch {\n state.setConflict(null)\n }\n }, 500)\n return () => clearTimeout(timer)\n }, [editData?.id, open, state.date, state.startTime, state.duration, state.allDay, t]) // eslint-disable-line react-hooks/exhaustive-deps\n\n const handleSave = React.useCallback(async () => {\n if (!state.title.trim()) return\n state.setSaving(true)\n try {\n const scheduledAt = state.allDay\n ? new Date(`${state.date}T00:00:00`).toISOString()\n : new Date(`${state.date}T${state.startTime}:00`).toISOString()\n\n const recurrenceRule = state.recurrenceEnabled\n ? buildRecurrenceRule(state.recurrenceDays, state.recurrenceEndType, state.recurrenceCount, state.recurrenceEndDate)\n : null\n\n const isSaveEdit = Boolean(editData?.id)\n const customValues: Record<string, unknown> = {}\n if (state.activityType === 'call') {\n customValues.callDirection = callDirection\n if (callOutcome) customValues.callOutcome = callOutcome\n if (callPhoneNumber.trim()) customValues.callPhoneNumber = callPhoneNumber.trim()\n }\n if (state.activityType === 'task') {\n customValues.taskPriority = taskPriority\n }\n const payload = {\n ...(isSaveEdit ? { id: editData!.id } : {}),\n entityId,\n dealId,\n interactionType: state.activityType,\n title: state.title.trim(),\n body: state.description.trim() || null,\n status: 'planned',\n scheduledAt,\n durationMinutes: visibleFields.has('duration') && !state.allDay ? state.duration : null,\n location: visibleFields.has('location') ? (state.location.trim() || null) : null,\n allDay: visibleFields.has('allDay') ? state.allDay : null,\n recurrenceRule: visibleFields.has('recurrence') ? recurrenceRule : null,\n recurrenceEnd: visibleFields.has('recurrence') && state.recurrenceEndType === 'date' && state.recurrenceEndDate\n ? new Date(state.recurrenceEndDate).toISOString()\n : null,\n participants: visibleFields.has('participants') && state.participants.length > 0\n ? state.participants.map((p) => ({ userId: p.userId, name: p.name, email: p.email, status: p.status ?? 'pending' }))\n : null,\n guestPermissions: visibleFields.has('participants') && state.participants.length > 0 ? state.guestPermissions : null,\n linkedEntities: state.linkedEntities.length > 0\n ? state.linkedEntities.map((e) => ({ id: e.id, type: e.type, label: e.label }))\n : null,\n reminderMinutes: visibleFields.has('reminder') ? state.reminderMinutes : null,\n visibility: visibleFields.has('visibility') ? state.visibility : null,\n ...(Object.keys(customValues).length > 0 ? { customValues } : {}),\n }\n await runGuardedMutation(\n () =>\n apiCallOrThrow('/api/customers/interactions', {\n method: isSaveEdit ? 'PUT' : 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n }),\n {\n operation: isSaveEdit ? 'updateActivity' : 'createActivity',\n interactionId: editData?.id ?? null,\n interactionType: state.activityType,\n },\n )\n flash(t('customers.schedule.saved', 'Activity scheduled'), 'success')\n onClose()\n // Delay data reload so the dialog can unmount cleanly and Radix restores body scroll\n requestAnimationFrame(() => { onActivityCreated?.() })\n } catch {\n flash(t('customers.schedule.error', 'Failed to schedule activity'), 'error')\n } finally {\n state.setSaving(false)\n }\n }, [state.activityType, state.allDay, state.date, state.description, dealId, state.duration, editData, entityId, state.guestPermissions, state.linkedEntities, state.location, onActivityCreated, onClose, state.participants, state.recurrenceCount, state.recurrenceDays, state.recurrenceEnabled, state.recurrenceEndDate, state.recurrenceEndType, state.reminderMinutes, runGuardedMutation, state.startTime, t, state.title, state.visibility, visibleFields]) // eslint-disable-line react-hooks/exhaustive-deps\n\n const handleKeyDown = React.useCallback((e: React.KeyboardEvent) => {\n if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {\n e.preventDefault()\n handleSave()\n }\n }, [handleSave])\n\n return (\n <Dialog open={open} onOpenChange={(o) => { if (!o) void guardedClose() }}>\n {ConfirmDialogElement}\n <DialogContent className=\"flex max-h-[90vh] flex-col overflow-hidden border-border p-0 shadow-xl sm:max-w-[760px] sm:rounded-xl [&>[data-dialog-close]]:hidden\" onKeyDown={handleKeyDown} aria-describedby={undefined}>\n <VisuallyHidden>\n <DialogTitle>{isEditing ? t('customers.schedule.editTitle', 'Edit activity') : t(chrome.titleKey, chrome.titleFallback)}</DialogTitle>\n </VisuallyHidden>\n\n {/* Header */}\n <div className=\"flex shrink-0 items-start justify-between gap-3 border-b border-border bg-background px-6 py-5\">\n <div className=\"flex flex-col gap-1\">\n <h2 className=\"text-lg font-semibold leading-tight tracking-tight text-foreground\">\n {isEditing ? t('customers.schedule.editTitle', 'Edit activity') : t(chrome.titleKey, chrome.titleFallback)}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {t(chrome.subtitleKey, chrome.subtitleFallback)}\n </p>\n {entityName ? (\n <p className=\"mt-0.5 text-xs text-muted-foreground/80\">\n {t('customers.schedule.context', 'On timeline: {{name}}', { name: entityName })}\n {companyName ? ` \u00B7 ${companyName}` : ''}\n </p>\n ) : null}\n </div>\n <IconButton type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => { void guardedClose() }} className=\"flex size-9 shrink-0 items-center justify-center rounded-md border border-border bg-background\" aria-label={t('customers.schedule.cancel', 'Cancel')}>\n <X className=\"size-4 text-muted-foreground\" />\n </IconButton>\n </div>\n\n <div className=\"flex-1 overflow-y-auto\">\n <div className=\"flex flex-col gap-4 bg-background p-6\">\n\n {/* Conflict warning */}\n {state.conflict && (\n <Alert variant=\"warning\" className=\"rounded-lg\">\n <AlertTriangle className=\"size-5\" />\n <AlertTitle>\n {t('customers.schedule.conflict.title', 'Calendar conflict')}\n </AlertTitle>\n <AlertDescription>{state.conflict}</AlertDescription>\n </Alert>\n )}\n\n {/* Type tabs \u2014 large rectangular tiles per Figma */}\n <div className=\"grid grid-cols-4 gap-2\">\n {TYPE_TABS.map(({ type, icon: Icon, labelKey, fallback }) => {\n const isActive = state.activityType === type\n return (\n <button\n key={type}\n type=\"button\"\n onClick={() => state.setActivityType(type)}\n aria-pressed={isActive}\n className={cn(\n 'flex h-[80px] flex-col items-center justify-center gap-2 rounded-md border text-[14px] font-semibold transition-colors',\n isActive\n ? 'border-transparent bg-foreground text-background'\n : 'border-border bg-card text-muted-foreground hover:border-foreground/40 hover:text-foreground',\n )}\n >\n <Icon className=\"size-[18px]\" />\n {t(labelKey, fallback)}\n </button>\n )\n })}\n </div>\n\n {/* Title */}\n <div className=\"flex flex-col gap-1.5\">\n <label className=\"text-overline font-semibold text-muted-foreground tracking-wider\">\n {getFieldLabel(state.activityType, 'title', t, 'customers.schedule.titleLabel', 'Title')}\n </label>\n <input\n type=\"text\"\n value={state.title}\n onChange={(e) => state.setTitle(e.target.value)}\n placeholder={\n state.activityType === 'email'\n ? t('customers.schedule.subjectPlaceholder', 'Subject...')\n : t('customers.schedule.titlePlaceholder', 'Activity title...')\n }\n className=\"w-full rounded-md border border-border bg-background px-3 py-2.5 text-sm text-foreground outline-none focus:border-foreground\"\n autoFocus\n />\n </div>\n\n {/* Date/Time/Duration \u2014 placed before per-type chip rows so the call/task\n workflows match Figma 829:50 / 790:280 (date row first, then status chips). */}\n <DateTimeFields\n visible={visibleFields}\n activityType={state.activityType}\n date={state.date}\n setDate={state.setDate}\n startTime={state.startTime}\n setStartTime={state.setStartTime}\n duration={state.duration}\n setDuration={state.setDuration}\n allDay={state.allDay}\n setAllDay={state.setAllDay}\n recurrenceEnabled={state.recurrenceEnabled}\n setRecurrenceEnabled={state.setRecurrenceEnabled}\n recurrenceDays={state.recurrenceDays}\n toggleRecurrenceDay={state.toggleRecurrenceDay}\n recurrenceEndType={state.recurrenceEndType}\n setRecurrenceEndType={state.setRecurrenceEndType}\n recurrenceCount={state.recurrenceCount}\n setRecurrenceCount={state.setRecurrenceCount}\n recurrenceEndDate={state.recurrenceEndDate}\n setRecurrenceEndDate={state.setRecurrenceEndDate}\n />\n\n {/* Call: Direction + Outcome chips */}\n {state.activityType === 'call' && (\n <div className=\"flex flex-col gap-3\">\n <div>\n <label className=\"text-overline font-semibold uppercase text-muted-foreground tracking-wider\">\n {t('customers.schedule.call.directionLabel', 'Direction')}\n </label>\n <div className=\"mt-2 flex flex-wrap gap-2\">\n {CALL_DIRECTIONS.map((opt) => {\n const isActive = callDirection === opt.key\n return (\n <button\n key={opt.key}\n type=\"button\"\n aria-pressed={isActive}\n onClick={() => setCallDirection(opt.key)}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-sm font-medium transition-colors',\n isActive\n ? 'border-transparent bg-foreground text-background'\n : 'border-border bg-card text-muted-foreground hover:border-foreground/40',\n )}\n >\n <span className={cn('inline-block size-1.5 rounded-full', opt.dot)} aria-hidden />\n {t(opt.labelKey, opt.labelFallback)}\n </button>\n )\n })}\n </div>\n </div>\n <div>\n <label className=\"text-overline font-semibold uppercase text-muted-foreground tracking-wider\">\n {t('customers.schedule.call.outcomeLabel', 'Outcome')}\n </label>\n <div className=\"mt-2 flex flex-wrap gap-2\">\n {CALL_OUTCOMES.map((opt) => {\n const isActive = callOutcome === opt.key\n return (\n <button\n key={opt.key}\n type=\"button\"\n aria-pressed={isActive}\n onClick={() => setCallOutcome(isActive ? null : opt.key)}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-sm font-medium transition-colors',\n isActive\n ? 'border-transparent bg-foreground text-background'\n : 'border-border bg-card text-muted-foreground hover:border-foreground/40',\n )}\n >\n <span className={cn('inline-block size-1.5 rounded-full', opt.dot)} aria-hidden />\n {t(opt.labelKey, opt.labelFallback)}\n </button>\n )\n })}\n </div>\n </div>\n </div>\n )}\n\n {/* Task: Priority chips */}\n {state.activityType === 'task' && (\n <div>\n <label className=\"text-overline font-semibold uppercase text-muted-foreground tracking-wider\">\n {t('customers.schedule.task.priorityLabel', 'Priority')}\n </label>\n <div className=\"mt-2 flex flex-wrap gap-2\">\n {TASK_PRIORITIES.map((opt) => {\n const isActive = taskPriority === opt.key\n return (\n <button\n key={opt.key}\n type=\"button\"\n aria-pressed={isActive}\n onClick={() => setTaskPriority(opt.key)}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-sm font-medium transition-colors',\n isActive\n ? 'border-transparent bg-foreground text-background'\n : 'border-border bg-card text-muted-foreground hover:border-foreground/40',\n )}\n >\n <span className={cn('inline-block size-1.5 rounded-full', opt.dot)} aria-hidden />\n {t(opt.labelKey, opt.labelFallback)}\n </button>\n )\n })}\n </div>\n </div>\n )}\n\n {/* Participants */}\n <ParticipantsField\n visible={visibleFields}\n activityType={state.activityType}\n participants={state.participants}\n setParticipants={state.setParticipants}\n removeParticipant={state.removeParticipant}\n guestPermissions={state.guestPermissions}\n setGuestPermissions={state.setGuestPermissions}\n />\n\n {/* Location (or phone number for calls) */}\n {state.activityType === 'call' ? (\n <div className=\"flex flex-col gap-1.5\">\n <label className=\"text-overline font-semibold uppercase text-muted-foreground tracking-wider\">\n {t('customers.schedule.call.phoneLabel', 'Phone number')}\n </label>\n <input\n type=\"tel\"\n value={callPhoneNumber}\n onChange={(e) => setCallPhoneNumber(e.target.value)}\n placeholder={t('customers.schedule.call.phonePlaceholder', '+1 555 000 0000')}\n className=\"w-full rounded-md border border-border bg-background px-3 py-2.5 text-sm text-foreground outline-none focus:border-foreground\"\n />\n </div>\n ) : (\n <LocationField\n visible={visibleFields}\n activityType={state.activityType}\n location={state.location}\n setLocation={state.setLocation}\n />\n )}\n\n {/* Linked Entities */}\n <LinkedEntitiesField\n visible={visibleFields}\n activityType={state.activityType}\n linkedEntities={state.linkedEntities}\n setLinkedEntities={state.setLinkedEntities}\n />\n\n {/* Description */}\n <div>\n <label className=\"text-overline font-semibold uppercase text-muted-foreground tracking-wider\">\n {getFieldLabel(state.activityType, 'description', t, 'customers.schedule.description', 'Description')}\n </label>\n <div className=\"mt-[8px]\">\n <SwitchableMarkdownInput\n value={state.description}\n onChange={state.setDescription}\n isMarkdownEnabled={state.markdownEnabled}\n height={120}\n placeholder={t('customers.schedule.descriptionPlaceholder', 'Add details...')}\n />\n </div>\n </div>\n\n {/* Reminder + Visibility */}\n <FooterFields\n visible={visibleFields}\n activityType={state.activityType}\n reminderMinutes={state.reminderMinutes}\n setReminderMinutes={state.setReminderMinutes}\n visibility={state.visibility}\n setVisibility={state.setVisibility}\n />\n\n </div>\n </div>\n\n {/* Footer */}\n <div className=\"flex shrink-0 items-center justify-end gap-2.5 border-t border-border bg-muted/50 px-6 py-4\">\n <Button type=\"button\" variant=\"outline\" onClick={() => { void guardedClose() }} className=\"rounded-md border border-input bg-background px-5 py-3 text-sm font-semibold text-foreground\">\n {t('customers.schedule.cancel', 'Cancel')}\n </Button>\n <Button type=\"button\" onClick={handleSave} disabled={state.saving || !state.title.trim()} className=\"flex items-center gap-2 rounded-md bg-foreground px-5 py-3 text-sm font-semibold text-background hover:bg-foreground/90 disabled:opacity-50\">\n <SaveIcon className=\"size-3.5\" />\n {state.saving\n ? t('customers.schedule.saving', 'Saving...')\n : isEditing\n ? t('customers.schedule.update', 'Update activity')\n : t(chrome.saveKey, chrome.saveFallback)}\n </Button>\n </div>\n </DialogContent>\n </Dialog>\n )\n}\n\nexport type { ScheduleActivityEditData }\n\nfunction buildRecurrenceRule(\n days: boolean[],\n endType: 'never' | 'count' | 'date',\n count: number,\n endDate: string,\n): string {\n const dayNames = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU']\n const selectedDays = days.map((active, i) => (active ? dayNames[i] : null)).filter(Boolean)\n let rule = `FREQ=WEEKLY;BYDAY=${selectedDays.join(',')}`\n if (endType === 'count') rule += `;COUNT=${count}`\n if (endType === 'date' && endDate) rule += `;UNTIL=${endDate.replace(/-/g, '')}T235959Z`\n return rule\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -45,14 +45,14 @@ function DateTimeFields({
|
|
|
45
45
|
] })
|
|
46
46
|
] }),
|
|
47
47
|
showStartTime && /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col gap-1.5", children: [
|
|
48
|
-
/* @__PURE__ */ jsx("label", { className: "text-overline font-semibold text-muted-foreground tracking-wider", children:
|
|
48
|
+
/* @__PURE__ */ jsx("label", { className: "text-overline font-semibold text-muted-foreground tracking-wider", children: getFieldLabel(activityType, "startTime", t, "customers.schedule.start", "Start") }),
|
|
49
49
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-md border border-border bg-background px-3 py-2.5", children: [
|
|
50
50
|
/* @__PURE__ */ jsx(Clock, { className: "size-3.5 text-muted-foreground" }),
|
|
51
51
|
/* @__PURE__ */ jsx("input", { type: "time", value: startTime, onChange: (e) => setStartTime(e.target.value), disabled: allDay, className: "flex-1 bg-transparent text-sm text-foreground focus:outline-none disabled:opacity-50" })
|
|
52
52
|
] })
|
|
53
53
|
] }),
|
|
54
54
|
showDuration && /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col gap-1.5", children: [
|
|
55
|
-
/* @__PURE__ */ jsx("label", { className: "text-overline font-semibold text-muted-foreground tracking-wider", children:
|
|
55
|
+
/* @__PURE__ */ jsx("label", { className: "text-overline font-semibold text-muted-foreground tracking-wider", children: getFieldLabel(activityType, "duration", t, "customers.schedule.duration", "Duration") }),
|
|
56
56
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-md border border-border bg-background px-3 py-2.5", children: [
|
|
57
57
|
/* @__PURE__ */ jsx(
|
|
58
58
|
"select",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customers/components/detail/schedule/DateTimeFields.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { Calendar, Clock, ChevronDown, Globe, Repeat } 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 type { ActivityType, ScheduleFieldId } from './fieldConfig'\nimport { isVisible, getFieldLabel } from './fieldConfig'\n\nconst DURATION_OPTIONS = [15, 30, 45, 60, 90, 120]\nconst DAYS_OF_WEEK = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']\n\ninterface DateTimeFieldsProps {\n visible: Set<ScheduleFieldId>\n activityType: ActivityType\n date: string\n setDate: (value: string) => void\n startTime: string\n setStartTime: (value: string) => void\n duration: number\n setDuration: (value: number) => void\n allDay: boolean\n setAllDay: (value: boolean) => void\n recurrenceEnabled: boolean\n setRecurrenceEnabled: (value: boolean) => void\n recurrenceDays: boolean[]\n toggleRecurrenceDay: (index: number) => void\n recurrenceEndType: 'never' | 'count' | 'date'\n setRecurrenceEndType: (value: 'never' | 'count' | 'date') => void\n recurrenceCount: number\n setRecurrenceCount: (value: number) => void\n recurrenceEndDate: string\n setRecurrenceEndDate: (value: string) => void\n}\n\nexport function DateTimeFields({\n visible,\n activityType,\n date,\n setDate,\n startTime,\n setStartTime,\n duration,\n setDuration,\n allDay,\n setAllDay,\n recurrenceEnabled,\n setRecurrenceEnabled,\n recurrenceDays,\n toggleRecurrenceDay,\n recurrenceEndType,\n setRecurrenceEndType,\n recurrenceCount,\n setRecurrenceCount,\n recurrenceEndDate,\n setRecurrenceEndDate,\n}: DateTimeFieldsProps) {\n const t = useT()\n\n if (!visible.has('date')) return null\n\n const showStartTime = isVisible(activityType, 'startTime')\n const showDuration = isVisible(activityType, 'duration')\n const showAllDay = isVisible(activityType, 'allDay')\n const showRecurrence = isVisible(activityType, 'recurrence')\n\n return (\n <>\n {/* Date / Time / Duration */}\n <div className=\"flex gap-3\">\n <div className=\"flex flex-[2] flex-col gap-1.5\">\n <label className=\"text-overline font-semibold text-muted-foreground tracking-wider\">\n {getFieldLabel(activityType, 'date', t, 'customers.schedule.date', 'Date')}\n </label>\n <div className=\"flex items-center gap-2 rounded-md border border-border bg-background px-3 py-2.5\">\n <Calendar className=\"size-3.5 text-muted-foreground\" />\n <input type=\"date\" value={date} onChange={(e) => setDate(e.target.value)} className=\"flex-1 bg-transparent text-sm text-foreground focus:outline-none\" />\n </div>\n </div>\n {showStartTime && (\n <div className=\"flex flex-1 flex-col gap-1.5\">\n <label className=\"text-overline font-semibold text-muted-foreground tracking-wider\"
|
|
5
|
-
"mappings": ";AAwEU,
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { Calendar, Clock, ChevronDown, Globe, Repeat } 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 type { ActivityType, ScheduleFieldId } from './fieldConfig'\nimport { isVisible, getFieldLabel } from './fieldConfig'\n\nconst DURATION_OPTIONS = [15, 30, 45, 60, 90, 120]\nconst DAYS_OF_WEEK = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']\n\ninterface DateTimeFieldsProps {\n visible: Set<ScheduleFieldId>\n activityType: ActivityType\n date: string\n setDate: (value: string) => void\n startTime: string\n setStartTime: (value: string) => void\n duration: number\n setDuration: (value: number) => void\n allDay: boolean\n setAllDay: (value: boolean) => void\n recurrenceEnabled: boolean\n setRecurrenceEnabled: (value: boolean) => void\n recurrenceDays: boolean[]\n toggleRecurrenceDay: (index: number) => void\n recurrenceEndType: 'never' | 'count' | 'date'\n setRecurrenceEndType: (value: 'never' | 'count' | 'date') => void\n recurrenceCount: number\n setRecurrenceCount: (value: number) => void\n recurrenceEndDate: string\n setRecurrenceEndDate: (value: string) => void\n}\n\nexport function DateTimeFields({\n visible,\n activityType,\n date,\n setDate,\n startTime,\n setStartTime,\n duration,\n setDuration,\n allDay,\n setAllDay,\n recurrenceEnabled,\n setRecurrenceEnabled,\n recurrenceDays,\n toggleRecurrenceDay,\n recurrenceEndType,\n setRecurrenceEndType,\n recurrenceCount,\n setRecurrenceCount,\n recurrenceEndDate,\n setRecurrenceEndDate,\n}: DateTimeFieldsProps) {\n const t = useT()\n\n if (!visible.has('date')) return null\n\n const showStartTime = isVisible(activityType, 'startTime')\n const showDuration = isVisible(activityType, 'duration')\n const showAllDay = isVisible(activityType, 'allDay')\n const showRecurrence = isVisible(activityType, 'recurrence')\n\n return (\n <>\n {/* Date / Time / Duration */}\n <div className=\"flex gap-3\">\n <div className=\"flex flex-[2] flex-col gap-1.5\">\n <label className=\"text-overline font-semibold text-muted-foreground tracking-wider\">\n {getFieldLabel(activityType, 'date', t, 'customers.schedule.date', 'Date')}\n </label>\n <div className=\"flex items-center gap-2 rounded-md border border-border bg-background px-3 py-2.5\">\n <Calendar className=\"size-3.5 text-muted-foreground\" />\n <input type=\"date\" value={date} onChange={(e) => setDate(e.target.value)} className=\"flex-1 bg-transparent text-sm text-foreground focus:outline-none\" />\n </div>\n </div>\n {showStartTime && (\n <div className=\"flex flex-1 flex-col gap-1.5\">\n <label className=\"text-overline font-semibold text-muted-foreground tracking-wider\">\n {getFieldLabel(activityType, 'startTime', t, 'customers.schedule.start', 'Start')}\n </label>\n <div className=\"flex items-center gap-2 rounded-md border border-border bg-background px-3 py-2.5\">\n <Clock className=\"size-3.5 text-muted-foreground\" />\n <input type=\"time\" value={startTime} onChange={(e) => setStartTime(e.target.value)} disabled={allDay} className=\"flex-1 bg-transparent text-sm text-foreground focus:outline-none disabled:opacity-50\" />\n </div>\n </div>\n )}\n {showDuration && (\n <div className=\"flex flex-1 flex-col gap-1.5\">\n <label className=\"text-overline font-semibold text-muted-foreground tracking-wider\">\n {getFieldLabel(activityType, 'duration', t, 'customers.schedule.duration', 'Duration')}\n </label>\n <div className=\"flex items-center gap-2 rounded-md border border-border bg-background px-3 py-2.5\">\n <select\n value={duration}\n onChange={(e) => setDuration(Number(e.target.value))}\n disabled={allDay}\n className=\"flex-1 appearance-none bg-transparent text-sm text-foreground focus:outline-none disabled:opacity-50\"\n >\n {DURATION_OPTIONS.map((m) => (\n <option key={m} value={m}>{m} min</option>\n ))}\n </select>\n <ChevronDown className=\"size-3.5 text-muted-foreground\" />\n </div>\n </div>\n )}\n </div>\n\n {/* All day + timezone + recurrence */}\n {showAllDay && (\n <div className=\"flex flex-wrap items-center gap-3.5 text-xs text-muted-foreground\">\n <label className=\"flex items-center gap-2 cursor-pointer\">\n <input type=\"checkbox\" checked={allDay} onChange={(e) => setAllDay(e.target.checked)} className=\"rounded\" />\n {t('customers.schedule.allDay', 'All day')}\n </label>\n <span className=\"text-muted-foreground\">·</span>\n <span className=\"flex items-center gap-1.5\">\n <Globe className=\"size-3.5\" />\n {Intl.DateTimeFormat().resolvedOptions().timeZone} (GMT{new Date().getTimezoneOffset() <= 0 ? '+' : '-'}{String(Math.abs(Math.floor(new Date().getTimezoneOffset() / 60))).padStart(1, '0')})\n </span>\n {showRecurrence && (\n <>\n <span className=\"text-muted-foreground\">·</span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setRecurrenceEnabled(!recurrenceEnabled)}\n className={cn('h-auto flex items-center gap-1.5', recurrenceEnabled && 'font-medium text-foreground')}\n >\n <Repeat className=\"size-3.5\" />\n {recurrenceEnabled\n ? t('customers.schedule.recurrence.active', 'Repeats')\n : t('customers.schedule.recurrence.none', 'No repeat')}\n </Button>\n </>\n )}\n </div>\n )}\n\n {/* Recurrence config */}\n {showRecurrence && recurrenceEnabled && (\n <div className=\"rounded-lg border border-status-warning-border bg-status-warning-bg p-4 space-y-3\">\n <div className=\"flex items-center justify-between\">\n <span className=\"flex items-center gap-2 text-sm font-semibold text-foreground\">\n <Repeat className=\"size-3.5\" />\n {t('customers.schedule.recurrence.title', 'Recurrence')}\n </span>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" className=\"h-auto text-xs font-medium text-foreground\">\n {t('customers.schedule.recurrence.edit', 'Edit')}\n </Button>\n </div>\n <div className=\"flex gap-2\">\n {DAYS_OF_WEEK.map((day, i) => (\n <Button\n key={day}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => toggleRecurrenceDay(i)}\n className={cn(\n 'h-auto flex size-8 items-center justify-center rounded-full text-xs font-medium transition-colors p-0',\n recurrenceDays[i] ? 'bg-primary text-primary-foreground' : 'border border-border bg-background text-muted-foreground hover:bg-muted',\n )}\n >\n {day.slice(0, 2)}\n </Button>\n ))}\n </div>\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <span>{t('customers.schedule.recurrence.ends', 'Ends')}:</span>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => setRecurrenceEndType('never')} className={cn('h-auto rounded-full px-3 py-1 text-xs font-medium', recurrenceEndType === 'never' ? 'bg-background border border-border text-foreground' : 'text-muted-foreground')}>\n {t('customers.schedule.recurrence.never', 'Never')}\n </Button>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => setRecurrenceEndType('count')} className={cn('h-auto rounded-full px-3 py-1 text-xs font-medium', recurrenceEndType === 'count' ? 'bg-primary text-primary-foreground' : 'text-muted-foreground')}>\n {t('customers.schedule.recurrence.afterCount', 'After {{count}} occurrences', { count: recurrenceCount })}\n </Button>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => setRecurrenceEndType('date')} className={cn('h-auto rounded-full px-3 py-1 text-xs font-medium', recurrenceEndType === 'date' ? 'bg-background border border-border text-foreground' : 'text-muted-foreground')}>\n {recurrenceEndDate || t('customers.schedule.recurrence.onDate', 'On date')}\n </Button>\n </div>\n </div>\n )}\n </>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|