@open-mercato/core 0.6.8-develop.7063.1.664341cf65 → 0.6.8-develop.7065.1.b7b351a126

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 (57) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/customers/api/interactions/route.js +3 -1
  3. package/dist/modules/customers/api/interactions/route.js.map +2 -2
  4. package/dist/modules/customers/commands/interactions.js.map +1 -1
  5. package/dist/modules/customers/components/calendar/AgendaList.js +2 -2
  6. package/dist/modules/customers/components/calendar/AgendaList.js.map +2 -2
  7. package/dist/modules/customers/components/calendar/CalendarEventEditor.js +1 -1
  8. package/dist/modules/customers/components/calendar/CalendarEventEditor.js.map +2 -2
  9. package/dist/modules/customers/components/calendar/CalendarScreen.js +1 -1
  10. package/dist/modules/customers/components/calendar/CalendarScreen.js.map +2 -2
  11. package/dist/modules/customers/components/calendar/EventBlock.js +1 -1
  12. package/dist/modules/customers/components/calendar/EventBlock.js.map +2 -2
  13. package/dist/modules/customers/components/calendar/editor/PeopleField.js +40 -5
  14. package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
  15. package/dist/modules/customers/components/calendar/editor/hooks.js +2 -1
  16. package/dist/modules/customers/components/calendar/editor/hooks.js.map +2 -2
  17. package/dist/modules/customers/components/calendar/types.js +1 -1
  18. package/dist/modules/customers/components/calendar/types.js.map +2 -2
  19. package/dist/modules/customers/components/detail/schedule/ParticipantsField.js +4 -4
  20. package/dist/modules/customers/components/detail/schedule/ParticipantsField.js.map +2 -2
  21. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js +3 -3
  22. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
  23. package/dist/modules/customers/data/entities.js.map +1 -1
  24. package/dist/modules/customers/data/validators.js +22 -1
  25. package/dist/modules/customers/data/validators.js.map +2 -2
  26. package/dist/modules/customers/lib/calendar/conflicts.js +8 -2
  27. package/dist/modules/customers/lib/calendar/conflicts.js.map +2 -2
  28. package/dist/modules/customers/lib/calendar/editorPayload.js +3 -2
  29. package/dist/modules/customers/lib/calendar/editorPayload.js.map +2 -2
  30. package/dist/modules/customers/lib/calendar/mapItem.js +8 -3
  31. package/dist/modules/customers/lib/calendar/mapItem.js.map +2 -2
  32. package/dist/modules/customers/lib/calendar/participantIdentity.js +9 -0
  33. package/dist/modules/customers/lib/calendar/participantIdentity.js.map +7 -0
  34. package/package.json +7 -7
  35. package/src/modules/customers/api/interactions/route.ts +8 -2
  36. package/src/modules/customers/commands/interactions.ts +1 -1
  37. package/src/modules/customers/components/calendar/AgendaList.tsx +3 -3
  38. package/src/modules/customers/components/calendar/CalendarEventEditor.tsx +1 -1
  39. package/src/modules/customers/components/calendar/CalendarScreen.tsx +1 -1
  40. package/src/modules/customers/components/calendar/EventBlock.tsx +2 -2
  41. package/src/modules/customers/components/calendar/editor/PeopleField.tsx +61 -5
  42. package/src/modules/customers/components/calendar/editor/hooks.ts +7 -1
  43. package/src/modules/customers/components/calendar/types.ts +2 -2
  44. package/src/modules/customers/components/detail/schedule/ParticipantsField.tsx +5 -5
  45. package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +5 -5
  46. package/src/modules/customers/components/detail/types.ts +1 -1
  47. package/src/modules/customers/data/entities.ts +1 -1
  48. package/src/modules/customers/data/validators.ts +32 -6
  49. package/src/modules/customers/i18n/de.json +4 -0
  50. package/src/modules/customers/i18n/en.json +4 -0
  51. package/src/modules/customers/i18n/es.json +4 -0
  52. package/src/modules/customers/i18n/ko.json +4 -0
  53. package/src/modules/customers/i18n/pl.json +4 -0
  54. package/src/modules/customers/lib/calendar/conflicts.ts +10 -2
  55. package/src/modules/customers/lib/calendar/editorPayload.ts +4 -3
  56. package/src/modules/customers/lib/calendar/mapItem.ts +8 -3
  57. package/src/modules/customers/lib/calendar/participantIdentity.ts +17 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/customers/components/calendar/editor/PeopleField.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Avatar } from '@open-mercato/ui/primitives/avatar'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport type { EditorParticipant } from '../../../lib/calendar/editorPayload'\nimport { composeAccessibleName } from '../../../lib/calendar/labels'\nimport { searchPeopleOptions, type PersonOption } from './lookups'\nimport { CONTROL_BORDER, DROPDOWN_PANEL_CLASS, PersonChip, UppercaseBadge, useDropdownDismiss } from './inputs'\n\nexport function PeopleField({\n mode,\n placeholder,\n ariaLabel,\n value,\n onChange,\n includeCustomers,\n includeStaff = true,\n}: {\n mode: 'multi' | 'single'\n placeholder: string\n ariaLabel: string\n value: EditorParticipant[]\n onChange(next: EditorParticipant[]): void\n includeCustomers: boolean\n /** Pass false when the staff module is not loaded \u2014 customer-only options. */\n includeStaff?: boolean\n}) {\n const t = useT()\n const [query, setQuery] = React.useState('')\n const [open, setOpen] = React.useState(false)\n const [options, setOptions] = React.useState<PersonOption[]>([])\n const [loading, setLoading] = React.useState(false)\n const close = React.useCallback(() => setOpen(false), [])\n const rootRef = useDropdownDismiss(open, close)\n\n React.useEffect(() => {\n if (!open) return\n const controller = new AbortController()\n let cancelled = false\n const timer = window.setTimeout(async () => {\n setLoading(true)\n try {\n const results = await searchPeopleOptions(query.trim(), { includeCustomers, includeStaff, signal: controller.signal })\n if (cancelled) return\n setOptions(results)\n } catch {\n if (!cancelled) setOptions([])\n } finally {\n if (!cancelled) setLoading(false)\n }\n }, 250)\n return () => {\n cancelled = true\n controller.abort()\n window.clearTimeout(timer)\n }\n }, [open, query, includeCustomers, includeStaff])\n\n const selectedIds = new Set(value.map((participant) => participant.userId))\n const visibleOptions = options.filter((option) => !selectedIds.has(option.userId))\n\n const customerBadge = (\n <UppercaseBadge className=\"bg-status-info-bg text-status-info-text\">\n {t('customers.calendar.editor.customerBadge', 'Customer')}\n </UppercaseBadge>\n )\n\n return (\n <div\n ref={rootRef}\n className=\"relative w-full\"\n onBlur={(event) => {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setOpen(false)\n }}\n >\n <div\n className={cn(\n 'flex w-full flex-wrap content-center items-center gap-2 rounded-md bg-background px-2.5 py-2',\n 'min-h-14',\n CONTROL_BORDER,\n )}\n >\n {value.map((participant) => (\n <PersonChip\n key={participant.userId}\n name={participant.name}\n badge={participant.isCustomer ? customerBadge : undefined}\n onRemove={() => onChange(value.filter((entry) => entry.userId !== participant.userId))}\n removeLabel={t('customers.calendar.editor.removePerson', 'Remove {name}', { name: participant.name })}\n />\n ))}\n <Input\n type=\"text\"\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n onFocus={() => setOpen(true)}\n placeholder={placeholder}\n aria-label={ariaLabel}\n role=\"combobox\"\n aria-expanded={open}\n className=\"min-w-36 flex-1 border-0 bg-transparent px-0 shadow-none hover:bg-transparent focus-within:border-transparent focus-within:shadow-none\"\n inputClassName=\"text-sm text-foreground placeholder:text-muted-foreground\"\n />\n </div>\n {open ? (\n <div role=\"listbox\" aria-label={ariaLabel} className={DROPDOWN_PANEL_CLASS}>\n {loading ? (\n <p className=\"px-2 py-3 text-center text-xs text-muted-foreground\">\n {t('customers.calendar.editor.searching', 'Searching\u2026')}\n </p>\n ) : null}\n {!loading && visibleOptions.length === 0 ? (\n <p className=\"px-2 py-3 text-center text-xs text-muted-foreground\">\n {t('customers.calendar.editor.noResults', 'No results')}\n </p>\n ) : null}\n {!loading\n ? visibleOptions.map((option) => (\n <Button\n key={`${option.isCustomer ? 'customer' : 'staff'}:${option.userId}`}\n type=\"button\"\n variant=\"ghost\"\n role=\"option\"\n aria-selected={false}\n aria-label={composeAccessibleName([\n option.name,\n option.email,\n option.isCustomer ? t('customers.calendar.editor.customerBadge', 'Customer') : null,\n ])}\n title={composeAccessibleName([option.name, option.email])}\n onClick={() => {\n const participant: EditorParticipant = {\n userId: option.userId,\n name: option.name,\n email: option.email ?? undefined,\n isCustomer: option.isCustomer,\n }\n onChange(mode === 'single' ? [participant] : [...value, participant])\n setQuery('')\n if (mode === 'single') setOpen(false)\n }}\n className=\"h-auto w-full justify-start gap-2 whitespace-normal px-2 py-1.5 text-left text-sm font-normal text-foreground\"\n >\n <Avatar size=\"xs\" label={option.name} />\n <span className=\"min-w-0 flex-1 truncate\">\n {option.name}\n {option.email ? <span className=\"ml-1.5 text-xs text-muted-foreground\">{option.email}</span> : null}\n </span>\n {option.isCustomer ? customerBadge : null}\n </Button>\n ))\n : null}\n </div>\n ) : null}\n </div>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Avatar } from '@open-mercato/ui/primitives/avatar'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport type { EditorParticipant } from '../../../lib/calendar/editorPayload'\nimport { composeAccessibleName } from '../../../lib/calendar/labels'\nimport { searchPeopleOptions, type PersonOption } from './lookups'\nimport { CONTROL_BORDER, DROPDOWN_PANEL_CLASS, PersonChip, UppercaseBadge, useDropdownDismiss } from './inputs'\n\n// Mirrors the server-side guest rule in `interactionParticipantSchema`: a\n// participant with no user record is addressable only by a routable email.\nconst guestEmailSchema = z.string().email().max(320)\n\nfunction normalizeEmail(email: string | null | undefined): string | null {\n const normalized = email?.trim().toLowerCase()\n return normalized ? normalized : null\n}\n\nexport function PeopleField({\n mode,\n placeholder,\n ariaLabel,\n value,\n onChange,\n includeCustomers,\n includeStaff = true,\n}: {\n mode: 'multi' | 'single'\n placeholder: string\n ariaLabel: string\n value: EditorParticipant[]\n onChange(next: EditorParticipant[]): void\n includeCustomers: boolean\n /** Pass false when the staff module is not loaded \u2014 customer-only options. */\n includeStaff?: boolean\n}) {\n const t = useT()\n const [query, setQuery] = React.useState('')\n const [open, setOpen] = React.useState(false)\n const [options, setOptions] = React.useState<PersonOption[]>([])\n const [loading, setLoading] = React.useState(false)\n const close = React.useCallback(() => setOpen(false), [])\n const rootRef = useDropdownDismiss(open, close)\n\n React.useEffect(() => {\n if (!open) return\n const controller = new AbortController()\n let cancelled = false\n const timer = window.setTimeout(async () => {\n setLoading(true)\n try {\n const results = await searchPeopleOptions(query.trim(), { includeCustomers, includeStaff, signal: controller.signal })\n if (cancelled) return\n setOptions(results)\n } catch {\n if (!cancelled) setOptions([])\n } finally {\n if (!cancelled) setLoading(false)\n }\n }, 250)\n return () => {\n cancelled = true\n controller.abort()\n window.clearTimeout(timer)\n }\n }, [open, query, includeCustomers, includeStaff])\n\n const selectedIds = new Set(value.map((participant) => participant.userId))\n const visibleOptions = options.filter((option) => !selectedIds.has(option.userId))\n\n // An external guest has no record to search for, so a typed address that\n // matches nothing is the only way to invite one (#5115). Multi mode only \u2014\n // the single-mode field is the task assignee, which becomes `ownerUserId` and\n // must resolve to a real staff user.\n const typedEmail = normalizeEmail(query)\n const selectedEmails = new Set(\n value.map((participant) => normalizeEmail(participant.email)).filter((email): email is string => email !== null),\n )\n const guestEmail =\n mode === 'multi' &&\n typedEmail !== null &&\n guestEmailSchema.safeParse(typedEmail).success &&\n !selectedEmails.has(typedEmail) &&\n !options.some((option) => normalizeEmail(option.email) === typedEmail)\n ? typedEmail\n : null\n const guestLabel = guestEmail\n ? t('customers.calendar.editor.addGuest', 'Invite {email} as a guest', { email: guestEmail })\n : ''\n\n const customerBadge = (\n <UppercaseBadge className=\"bg-status-info-bg text-status-info-text\">\n {t('customers.calendar.editor.customerBadge', 'Customer')}\n </UppercaseBadge>\n )\n\n const guestBadge = (\n <UppercaseBadge className=\"bg-muted text-muted-foreground\">\n {t('customers.calendar.editor.guestBadge', 'Guest')}\n </UppercaseBadge>\n )\n\n return (\n <div\n ref={rootRef}\n className=\"relative w-full\"\n onBlur={(event) => {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setOpen(false)\n }}\n >\n <div\n className={cn(\n 'flex w-full flex-wrap content-center items-center gap-2 rounded-md bg-background px-2.5 py-2',\n 'min-h-14',\n CONTROL_BORDER,\n )}\n >\n {value.map((participant, index) => (\n <PersonChip\n key={participant.userId ?? participant.email ?? index}\n name={participant.name}\n badge={participant.isCustomer ? customerBadge : participant.userId ? undefined : guestBadge}\n onRemove={() => onChange(value.filter((_, entryIndex) => entryIndex !== index))}\n removeLabel={t('customers.calendar.editor.removePerson', 'Remove {name}', { name: participant.name })}\n />\n ))}\n <Input\n type=\"text\"\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n onFocus={() => setOpen(true)}\n placeholder={placeholder}\n aria-label={ariaLabel}\n role=\"combobox\"\n aria-expanded={open}\n className=\"min-w-36 flex-1 border-0 bg-transparent px-0 shadow-none hover:bg-transparent focus-within:border-transparent focus-within:shadow-none\"\n inputClassName=\"text-sm text-foreground placeholder:text-muted-foreground\"\n />\n </div>\n {open ? (\n <div role=\"listbox\" aria-label={ariaLabel} className={DROPDOWN_PANEL_CLASS}>\n {loading ? (\n <p className=\"px-2 py-3 text-center text-xs text-muted-foreground\">\n {t('customers.calendar.editor.searching', 'Searching\u2026')}\n </p>\n ) : null}\n {!loading && visibleOptions.length === 0 && !guestEmail ? (\n <p className=\"px-2 py-3 text-center text-xs text-muted-foreground\">\n {t('customers.calendar.editor.noResults', 'No results')}\n </p>\n ) : null}\n {!loading && guestEmail ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n role=\"option\"\n aria-selected={false}\n aria-label={guestLabel}\n title={guestLabel}\n onClick={() => {\n const participant: EditorParticipant = { name: guestEmail, email: guestEmail, isCustomer: false }\n onChange([...value, participant])\n setQuery('')\n }}\n className=\"h-auto w-full justify-start gap-2 whitespace-normal px-2 py-1.5 text-left text-sm font-normal text-foreground\"\n >\n <Avatar size=\"xs\" label={guestEmail} />\n <span className=\"min-w-0 flex-1 truncate\">{guestLabel}</span>\n {guestBadge}\n </Button>\n ) : null}\n {!loading\n ? visibleOptions.map((option) => (\n <Button\n key={`${option.isCustomer ? 'customer' : 'staff'}:${option.userId}`}\n type=\"button\"\n variant=\"ghost\"\n role=\"option\"\n aria-selected={false}\n aria-label={composeAccessibleName([\n option.name,\n option.email,\n option.isCustomer ? t('customers.calendar.editor.customerBadge', 'Customer') : null,\n ])}\n title={composeAccessibleName([option.name, option.email])}\n onClick={() => {\n const participant: EditorParticipant = {\n userId: option.userId,\n name: option.name,\n email: option.email ?? undefined,\n isCustomer: option.isCustomer,\n }\n onChange(mode === 'single' ? [participant] : [...value, participant])\n setQuery('')\n if (mode === 'single') setOpen(false)\n }}\n className=\"h-auto w-full justify-start gap-2 whitespace-normal px-2 py-1.5 text-left text-sm font-normal text-foreground\"\n >\n <Avatar size=\"xs\" label={option.name} />\n <span className=\"min-w-0 flex-1 truncate\">\n {option.name}\n {option.email ? <span className=\"ml-1.5 text-xs text-muted-foreground\">{option.email}</span> : null}\n </span>\n {option.isCustomer ? customerBadge : null}\n </Button>\n ))\n : null}\n </div>\n ) : null}\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -6,6 +6,7 @@ import { useT } from "@open-mercato/shared/lib/i18n/context";
6
6
  import { computeDurationMinutes } from "../../../lib/calendar/editorPayload.js";
7
7
  import { findEditorConflictItems } from "../../../lib/calendar/conflicts.js";
8
8
  import { mapInteractionToCalendarItem } from "../../../lib/calendar/mapItem.js";
9
+ import { participantActorKey } from "../../../lib/calendar/participantIdentity.js";
9
10
  import { expandOccurrences } from "../../../lib/calendar/recurrence.js";
10
11
  import { getFetchWindow } from "../../../lib/calendar/range.js";
11
12
  import { fetchCalendarCandidates } from "../useCalendarItems.js";
@@ -47,7 +48,7 @@ function formatClock(date) {
47
48
  function useConflictProbe(open, form, config, excludeId, draftOwnerUserId, scope, currentUserId) {
48
49
  const t = useT();
49
50
  const [conflict, setConflict] = React.useState(null);
50
- const participantsKey = form.participants.map((participant) => participant.userId).join(",");
51
+ const participantsKey = form.participants.map((participant) => participantActorKey(participant) ?? "").join(",");
51
52
  React.useEffect(() => {
52
53
  if (!open || !form.date) {
53
54
  setConflict(null);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/customers/components/calendar/editor/hooks.ts"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { endOfDay } from 'date-fns/endOfDay'\nimport { startOfDay } from 'date-fns/startOfDay'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { computeDurationMinutes, type EditorFormState, type EditorKindConfig } from '../../../lib/calendar/editorPayload'\nimport { findEditorConflictItems } from '../../../lib/calendar/conflicts'\nimport type { ConflictScope } from '../../../lib/calendar/preferences'\nimport { mapInteractionToCalendarItem } from '../../../lib/calendar/mapItem'\nimport { expandOccurrences } from '../../../lib/calendar/recurrence'\nimport { getFetchWindow } from '../../../lib/calendar/range'\nimport type { CalendarItem } from '../types'\nimport { fetchCalendarCandidates } from '../useCalendarItems'\nimport { fetchDealById, fetchRelatedEntityById, findStaffMemberName } from './lookups'\n\n// Edit-mode prefill stores ids only (parseItemToFormState is pure); resolve the\n// human labels for the related entity, the deal chip and the task assignee.\nexport function useEditorLabelResolution(\n open: boolean,\n form: EditorFormState,\n update: (patch: Partial<EditorFormState>) => void,\n): void {\n React.useEffect(() => {\n if (!open) return\n const controller = new AbortController()\n let cancelled = false\n async function resolveLabels() {\n if (form.relatedTo && form.relatedTo.kind === 'unknown') {\n const resolved = await fetchRelatedEntityById(form.relatedTo.id, controller.signal)\n if (!cancelled && resolved) update({ relatedTo: { id: resolved.id, kind: resolved.kind, label: resolved.label } })\n }\n if (form.dealId && !form.dealLabel) {\n const deal = await fetchDealById(form.dealId, controller.signal)\n if (!cancelled && deal) update({ dealLabel: deal.label })\n }\n if (form.assigneeUserId && !form.assigneeName) {\n const name = await findStaffMemberName(form.assigneeUserId, controller.signal)\n if (!cancelled && name) update({ assigneeName: name })\n }\n }\n resolveLabels().catch(() => {\n if (cancelled || controller.signal.aborted || !form.relatedTo || form.relatedTo.kind !== 'unknown' || form.relatedTo.label) {\n return\n }\n update({ relatedTo: { ...form.relatedTo, label: form.relatedTo.id } })\n })\n return () => {\n cancelled = true\n controller.abort()\n }\n }, [open, form.relatedTo, form.dealId, form.dealLabel, form.assigneeUserId, form.assigneeName, update])\n}\n\nfunction formatClock(date: Date): string {\n return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })\n}\n\n// Debounced save-time conflict probe. Detection uses the SAME `findConflicts`\n// logic the grid uses (overlap + shared owner/participant) against a freshly\n// fetched \u00B11-day window, so the editor warning is always consistent with the\n// conflict badges/rings the user sees on the calendar. The warning is\n// informational and never blocks saving.\nexport function useConflictProbe(\n open: boolean,\n form: EditorFormState,\n config: EditorKindConfig,\n excludeId: string | null,\n draftOwnerUserId: string | null,\n scope: ConflictScope,\n currentUserId: string | null,\n): string | null {\n const t = useT()\n const [conflict, setConflict] = React.useState<string | null>(null)\n const participantsKey = form.participants.map((participant) => participant.userId).join(',')\n React.useEffect(() => {\n if (!open || !form.date) {\n setConflict(null)\n return\n }\n // Resolve the draft's [start, end] span exactly as the grid will after save:\n // all-day spans startOfDay..endOfDay (matching mapInteractionToCalendarItem),\n // timed events use the start + computed duration (multi-day endDate included).\n const isAllDay = config.hasAllDay && form.allDay\n let start: Date\n let end: Date\n if (isAllDay) {\n const dayDate = new Date(`${form.date}T00:00:00`)\n if (Number.isNaN(dayDate.getTime())) {\n setConflict(null)\n return\n }\n start = startOfDay(dayDate)\n end = endOfDay(dayDate)\n } else {\n if (!form.startTime) {\n setConflict(null)\n return\n }\n start = new Date(`${form.date}T${form.startTime}:00`)\n if (Number.isNaN(start.getTime())) {\n setConflict(null)\n return\n }\n const durationMinutes = config.hasEnd ? computeDurationMinutes(form) ?? 30 : 30\n end = new Date(start.getTime() + durationMinutes * 60_000)\n }\n // The same padded window + recurrence expansion the grid uses, so the probe\n // sees the exact candidate set behind the grid's conflict badges/rings. The\n // window pads \u00B11 day around the FULL draft span so multi-day drafts still\n // catch neighbours near their end, not just their start day.\n const dayStart = startOfDay(start)\n dayStart.setDate(dayStart.getDate() - 1)\n const dayEnd = endOfDay(end)\n dayEnd.setDate(dayEnd.getDate() + 1)\n const fetchWindow = getFetchWindow({ from: dayStart, to: dayEnd })\n const controller = new AbortController()\n let active = true\n const timer = setTimeout(async () => {\n try {\n const { payloads } = await fetchCalendarCandidates(fetchWindow, controller.signal)\n if (!active) return\n const others: CalendarItem[] = []\n for (const payload of payloads) {\n const item = mapInteractionToCalendarItem(payload, {})\n if (!item) continue\n others.push(...expandOccurrences(item, fetchWindow))\n }\n const conflictItems = findEditorConflictItems(\n {\n start,\n end,\n ownerUserId: draftOwnerUserId,\n participants: form.participants.map((participant) => ({ userId: participant.userId, name: participant.name })),\n status: form.status,\n },\n others,\n excludeId,\n { scope, currentUserId },\n )\n if (!active) return\n if (conflictItems.length === 0) {\n setConflict(null)\n return\n }\n const summary = conflictItems\n .map((item) => `${formatClock(item.start)}\u2013${formatClock(item.end)}: ${item.title || t('customers.calendar.grid.untitled', 'Untitled')}`)\n .join(', ')\n setConflict(summary ? t('customers.calendar.editor.conflictWarning', 'Overlaps with: {items}', { items: summary }) : null)\n } catch {\n if (active) setConflict(null)\n }\n }, 500)\n return () => {\n active = false\n clearTimeout(timer)\n controller.abort()\n }\n // Re-probe when schedule-relevant inputs change (time window, all-day, attendees, owner, status, scope).\n }, [open, form.date, form.startTime, form.endDate, form.endTime, form.allDay, form.status, participantsKey, config.hasAllDay, config.hasEnd, excludeId, draftOwnerUserId, scope, currentUserId, t]) // eslint-disable-line react-hooks/exhaustive-deps\n return conflict\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { endOfDay } from 'date-fns/endOfDay'\nimport { startOfDay } from 'date-fns/startOfDay'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { computeDurationMinutes, type EditorFormState, type EditorKindConfig } from '../../../lib/calendar/editorPayload'\nimport { findEditorConflictItems } from '../../../lib/calendar/conflicts'\nimport type { ConflictScope } from '../../../lib/calendar/preferences'\nimport { mapInteractionToCalendarItem } from '../../../lib/calendar/mapItem'\nimport { participantActorKey } from '../../../lib/calendar/participantIdentity'\nimport { expandOccurrences } from '../../../lib/calendar/recurrence'\nimport { getFetchWindow } from '../../../lib/calendar/range'\nimport type { CalendarItem } from '../types'\nimport { fetchCalendarCandidates } from '../useCalendarItems'\nimport { fetchDealById, fetchRelatedEntityById, findStaffMemberName } from './lookups'\n\n// Edit-mode prefill stores ids only (parseItemToFormState is pure); resolve the\n// human labels for the related entity, the deal chip and the task assignee.\nexport function useEditorLabelResolution(\n open: boolean,\n form: EditorFormState,\n update: (patch: Partial<EditorFormState>) => void,\n): void {\n React.useEffect(() => {\n if (!open) return\n const controller = new AbortController()\n let cancelled = false\n async function resolveLabels() {\n if (form.relatedTo && form.relatedTo.kind === 'unknown') {\n const resolved = await fetchRelatedEntityById(form.relatedTo.id, controller.signal)\n if (!cancelled && resolved) update({ relatedTo: { id: resolved.id, kind: resolved.kind, label: resolved.label } })\n }\n if (form.dealId && !form.dealLabel) {\n const deal = await fetchDealById(form.dealId, controller.signal)\n if (!cancelled && deal) update({ dealLabel: deal.label })\n }\n if (form.assigneeUserId && !form.assigneeName) {\n const name = await findStaffMemberName(form.assigneeUserId, controller.signal)\n if (!cancelled && name) update({ assigneeName: name })\n }\n }\n resolveLabels().catch(() => {\n if (cancelled || controller.signal.aborted || !form.relatedTo || form.relatedTo.kind !== 'unknown' || form.relatedTo.label) {\n return\n }\n update({ relatedTo: { ...form.relatedTo, label: form.relatedTo.id } })\n })\n return () => {\n cancelled = true\n controller.abort()\n }\n }, [open, form.relatedTo, form.dealId, form.dealLabel, form.assigneeUserId, form.assigneeName, update])\n}\n\nfunction formatClock(date: Date): string {\n return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })\n}\n\n// Debounced save-time conflict probe. Detection uses the SAME `findConflicts`\n// logic the grid uses (overlap + shared owner/participant) against a freshly\n// fetched \u00B11-day window, so the editor warning is always consistent with the\n// conflict badges/rings the user sees on the calendar. The warning is\n// informational and never blocks saving.\nexport function useConflictProbe(\n open: boolean,\n form: EditorFormState,\n config: EditorKindConfig,\n excludeId: string | null,\n draftOwnerUserId: string | null,\n scope: ConflictScope,\n currentUserId: string | null,\n): string | null {\n const t = useT()\n const [conflict, setConflict] = React.useState<string | null>(null)\n // Keys on the canonical actor key, not the raw userId \u2014 an external guest has\n // no userId, so a userId-only key never changes when guests are added or\n // removed and the probe would keep reporting the previous attendee set.\n const participantsKey = form.participants\n .map((participant) => participantActorKey(participant) ?? '')\n .join(',')\n React.useEffect(() => {\n if (!open || !form.date) {\n setConflict(null)\n return\n }\n // Resolve the draft's [start, end] span exactly as the grid will after save:\n // all-day spans startOfDay..endOfDay (matching mapInteractionToCalendarItem),\n // timed events use the start + computed duration (multi-day endDate included).\n const isAllDay = config.hasAllDay && form.allDay\n let start: Date\n let end: Date\n if (isAllDay) {\n const dayDate = new Date(`${form.date}T00:00:00`)\n if (Number.isNaN(dayDate.getTime())) {\n setConflict(null)\n return\n }\n start = startOfDay(dayDate)\n end = endOfDay(dayDate)\n } else {\n if (!form.startTime) {\n setConflict(null)\n return\n }\n start = new Date(`${form.date}T${form.startTime}:00`)\n if (Number.isNaN(start.getTime())) {\n setConflict(null)\n return\n }\n const durationMinutes = config.hasEnd ? computeDurationMinutes(form) ?? 30 : 30\n end = new Date(start.getTime() + durationMinutes * 60_000)\n }\n // The same padded window + recurrence expansion the grid uses, so the probe\n // sees the exact candidate set behind the grid's conflict badges/rings. The\n // window pads \u00B11 day around the FULL draft span so multi-day drafts still\n // catch neighbours near their end, not just their start day.\n const dayStart = startOfDay(start)\n dayStart.setDate(dayStart.getDate() - 1)\n const dayEnd = endOfDay(end)\n dayEnd.setDate(dayEnd.getDate() + 1)\n const fetchWindow = getFetchWindow({ from: dayStart, to: dayEnd })\n const controller = new AbortController()\n let active = true\n const timer = setTimeout(async () => {\n try {\n const { payloads } = await fetchCalendarCandidates(fetchWindow, controller.signal)\n if (!active) return\n const others: CalendarItem[] = []\n for (const payload of payloads) {\n const item = mapInteractionToCalendarItem(payload, {})\n if (!item) continue\n others.push(...expandOccurrences(item, fetchWindow))\n }\n const conflictItems = findEditorConflictItems(\n {\n start,\n end,\n ownerUserId: draftOwnerUserId,\n participants: form.participants.map((participant) => ({ userId: participant.userId, name: participant.name })),\n status: form.status,\n },\n others,\n excludeId,\n { scope, currentUserId },\n )\n if (!active) return\n if (conflictItems.length === 0) {\n setConflict(null)\n return\n }\n const summary = conflictItems\n .map((item) => `${formatClock(item.start)}\u2013${formatClock(item.end)}: ${item.title || t('customers.calendar.grid.untitled', 'Untitled')}`)\n .join(', ')\n setConflict(summary ? t('customers.calendar.editor.conflictWarning', 'Overlaps with: {items}', { items: summary }) : null)\n } catch {\n if (active) setConflict(null)\n }\n }, 500)\n return () => {\n active = false\n clearTimeout(timer)\n controller.abort()\n }\n // Re-probe when schedule-relevant inputs change (time window, all-day, attendees, owner, status, scope).\n }, [open, form.date, form.startTime, form.endDate, form.endTime, form.allDay, form.status, participantsKey, config.hasAllDay, config.hasEnd, excludeId, draftOwnerUserId, scope, currentUserId, t]) // eslint-disable-line react-hooks/exhaustive-deps\n return conflict\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  const calendarParticipantSchema = z.object({
3
- userId: z.string(),
3
+ userId: z.string().optional(),
4
4
  name: z.string().optional(),
5
5
  email: z.string().optional()
6
6
  }).passthrough();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/components/calendar/types.ts"],
4
- "sourcesContent": ["import { z } from 'zod'\n\nexport type CalendarView = 'day' | 'week' | 'month' | 'agenda'\n\nexport type CalendarCategory = 'meeting' | 'event' | 'task' | 'other'\n\nexport type CalendarItemStatus = 'planned' | 'done' | 'canceled'\n\nexport type CalendarPlatform = 'zoom' | 'meet' | 'slack' | 'teams'\n\nexport type CalendarLocationKind = 'url' | 'venue' | 'platform'\n\nexport type CalendarTab = 'all' | 'meetings' | 'events'\n\nexport type CalendarRangePreset = 'thisWeek' | 'next7' | 'thisMonth' | 'next30'\n\nexport type CalendarRange = { from: Date; to: Date }\n\nconst calendarParticipantSchema = z\n .object({\n userId: z.string(),\n name: z.string().optional(),\n email: z.string().optional(),\n })\n .passthrough()\n\nexport const calendarInteractionPayloadSchema = z\n .object({\n id: z.string(),\n interactionType: z.string(),\n title: z.string().nullable().optional(),\n status: z.string(),\n scheduledAt: z.string().nullable().optional(),\n occurredAt: z.string().nullable().optional(),\n durationMinutes: z.number().nullable().optional(),\n allDay: z.boolean().nullable().optional(),\n location: z.string().nullable().optional(),\n participants: z.array(calendarParticipantSchema).nullable().optional(),\n recurrenceRule: z.string().nullable().optional(),\n recurrenceEnd: z.string().nullable().optional(),\n appearanceIcon: z.string().nullable().optional(),\n appearanceColor: z.string().nullable().optional(),\n ownerUserId: z.string().nullable().optional(),\n entityId: z.string().nullable().optional(),\n dealId: z.string().nullable().optional(),\n updatedAt: z.string().nullable().optional(),\n })\n .passthrough()\n\nexport type CalendarInteractionPayload = z.infer<typeof calendarInteractionPayloadSchema>\n\nexport type CalendarParticipant = { userId: string; name?: string; email?: string }\n\nexport interface CalendarItem {\n id: string\n title: string\n interactionType: string\n category: CalendarCategory\n status: CalendarItemStatus\n start: Date\n end: Date\n allDay: boolean\n location: string | null\n platform: CalendarPlatform | null\n locationKind: CalendarLocationKind | null\n participants: CalendarParticipant[]\n ownerUserId: string | null\n entityId: string | null\n dealId: string | null\n color: string | null\n isRecurringOccurrence: boolean\n updatedAt: string | null\n raw: CalendarInteractionPayload\n}\n\nexport interface CalendarFiltersValue {\n types: string[]\n status: string | null\n ownerUserId: string | null\n}\n\nexport interface TimeGridProps {\n days: 1 | 7\n anchor: Date\n items: CalendarItem[]\n conflictIds: Set<string>\n showWeekends: boolean\n showConflicts: boolean\n aiSummaries: boolean\n canManage?: boolean\n highlightItemId?: string | null\n onItemClick(item: CalendarItem): void\n onJoin(item: CalendarItem): void\n onNavigate(deltaDays: number): void\n onCreate?: () => void\n onCreateRange?(start: Date, end: Date): void\n}\n\nexport interface MonthGridProps {\n anchor: Date\n items: CalendarItem[]\n onItemClick(item: CalendarItem): void\n onDayOpen(date: Date): void\n}\n\nexport interface AgendaListProps {\n anchor: Date\n horizonDays: number\n items: CalendarItem[]\n typeLabels?: Record<string, string>\n onItemClick(item: CalendarItem): void\n}\n\nexport interface UpcomingCard {\n item: CalendarItem\n kind: 'today' | 'conflicted' | 'cancelled' | 'future'\n conflictCount: number\n}\n\nexport interface UpcomingCardsProps {\n cards: UpcomingCard[]\n canManage?: boolean\n onJoin(item: CalendarItem): void\n onSeeConflict(item: CalendarItem): void\n onOpen(item: CalendarItem): void\n onEdit(item: CalendarItem): void\n onCancel(item: CalendarItem): void\n}\n\nexport interface CalendarHeaderProps {\n view: CalendarView\n anchor: Date\n onNewEvent?: () => void\n}\n\nexport interface CalendarToolbarProps {\n view: CalendarView\n anchor: Date\n range: CalendarRange\n preset: CalendarRangePreset | null\n search: string\n filters: CalendarFiltersValue\n typeOptions: Array<{ value: string; label: string }>\n ownerOptions: Array<{ value: string; label: string }>\n onToday(): void\n onPresetChange(preset: CalendarRangePreset): void\n onAnchorChange(date: Date): void\n onSearchChange(value: string): void\n onFiltersChange(value: CalendarFiltersValue): void\n onOpenSettings(): void\n}\n\nexport interface CalendarTabsProps {\n tab: CalendarTab\n counts: { all: number; meetings: number; events: number }\n view: CalendarView\n onTabChange(tab: CalendarTab): void\n onViewChange(view: CalendarView): void\n}\n\nexport interface CalendarFooterProps {\n timezoneLabel: string\n onOpenShortcuts?: () => void\n}\n"],
5
- "mappings": "AAAA,SAAS,SAAS;AAkBlB,MAAM,4BAA4B,EAC/B,OAAO;AAAA,EACN,QAAQ,EAAE,OAAO;AAAA,EACjB,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,EAC1B,OAAO,EAAE,OAAO,EAAE,SAAS;AAC7B,CAAC,EACA,YAAY;AAER,MAAM,mCAAmC,EAC7C,OAAO;AAAA,EACN,IAAI,EAAE,OAAO;AAAA,EACb,iBAAiB,EAAE,OAAO;AAAA,EAC1B,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACtC,QAAQ,EAAE,OAAO;AAAA,EACjB,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC5C,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC3C,iBAAiB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAChD,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS;AAAA,EACxC,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACzC,cAAc,EAAE,MAAM,yBAAyB,EAAE,SAAS,EAAE,SAAS;AAAA,EACrE,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC/C,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC9C,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC/C,iBAAiB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAChD,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC5C,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACzC,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACvC,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAC5C,CAAC,EACA,YAAY;",
4
+ "sourcesContent": ["import { z } from 'zod'\n\nexport type CalendarView = 'day' | 'week' | 'month' | 'agenda'\n\nexport type CalendarCategory = 'meeting' | 'event' | 'task' | 'other'\n\nexport type CalendarItemStatus = 'planned' | 'done' | 'canceled'\n\nexport type CalendarPlatform = 'zoom' | 'meet' | 'slack' | 'teams'\n\nexport type CalendarLocationKind = 'url' | 'venue' | 'platform'\n\nexport type CalendarTab = 'all' | 'meetings' | 'events'\n\nexport type CalendarRangePreset = 'thisWeek' | 'next7' | 'thisMonth' | 'next30'\n\nexport type CalendarRange = { from: Date; to: Date }\n\nconst calendarParticipantSchema = z\n .object({\n userId: z.string().optional(),\n name: z.string().optional(),\n email: z.string().optional(),\n })\n .passthrough()\n\nexport const calendarInteractionPayloadSchema = z\n .object({\n id: z.string(),\n interactionType: z.string(),\n title: z.string().nullable().optional(),\n status: z.string(),\n scheduledAt: z.string().nullable().optional(),\n occurredAt: z.string().nullable().optional(),\n durationMinutes: z.number().nullable().optional(),\n allDay: z.boolean().nullable().optional(),\n location: z.string().nullable().optional(),\n participants: z.array(calendarParticipantSchema).nullable().optional(),\n recurrenceRule: z.string().nullable().optional(),\n recurrenceEnd: z.string().nullable().optional(),\n appearanceIcon: z.string().nullable().optional(),\n appearanceColor: z.string().nullable().optional(),\n ownerUserId: z.string().nullable().optional(),\n entityId: z.string().nullable().optional(),\n dealId: z.string().nullable().optional(),\n updatedAt: z.string().nullable().optional(),\n })\n .passthrough()\n\nexport type CalendarInteractionPayload = z.infer<typeof calendarInteractionPayloadSchema>\n\nexport type CalendarParticipant = { userId?: string; name?: string; email?: string }\n\nexport interface CalendarItem {\n id: string\n title: string\n interactionType: string\n category: CalendarCategory\n status: CalendarItemStatus\n start: Date\n end: Date\n allDay: boolean\n location: string | null\n platform: CalendarPlatform | null\n locationKind: CalendarLocationKind | null\n participants: CalendarParticipant[]\n ownerUserId: string | null\n entityId: string | null\n dealId: string | null\n color: string | null\n isRecurringOccurrence: boolean\n updatedAt: string | null\n raw: CalendarInteractionPayload\n}\n\nexport interface CalendarFiltersValue {\n types: string[]\n status: string | null\n ownerUserId: string | null\n}\n\nexport interface TimeGridProps {\n days: 1 | 7\n anchor: Date\n items: CalendarItem[]\n conflictIds: Set<string>\n showWeekends: boolean\n showConflicts: boolean\n aiSummaries: boolean\n canManage?: boolean\n highlightItemId?: string | null\n onItemClick(item: CalendarItem): void\n onJoin(item: CalendarItem): void\n onNavigate(deltaDays: number): void\n onCreate?: () => void\n onCreateRange?(start: Date, end: Date): void\n}\n\nexport interface MonthGridProps {\n anchor: Date\n items: CalendarItem[]\n onItemClick(item: CalendarItem): void\n onDayOpen(date: Date): void\n}\n\nexport interface AgendaListProps {\n anchor: Date\n horizonDays: number\n items: CalendarItem[]\n typeLabels?: Record<string, string>\n onItemClick(item: CalendarItem): void\n}\n\nexport interface UpcomingCard {\n item: CalendarItem\n kind: 'today' | 'conflicted' | 'cancelled' | 'future'\n conflictCount: number\n}\n\nexport interface UpcomingCardsProps {\n cards: UpcomingCard[]\n canManage?: boolean\n onJoin(item: CalendarItem): void\n onSeeConflict(item: CalendarItem): void\n onOpen(item: CalendarItem): void\n onEdit(item: CalendarItem): void\n onCancel(item: CalendarItem): void\n}\n\nexport interface CalendarHeaderProps {\n view: CalendarView\n anchor: Date\n onNewEvent?: () => void\n}\n\nexport interface CalendarToolbarProps {\n view: CalendarView\n anchor: Date\n range: CalendarRange\n preset: CalendarRangePreset | null\n search: string\n filters: CalendarFiltersValue\n typeOptions: Array<{ value: string; label: string }>\n ownerOptions: Array<{ value: string; label: string }>\n onToday(): void\n onPresetChange(preset: CalendarRangePreset): void\n onAnchorChange(date: Date): void\n onSearchChange(value: string): void\n onFiltersChange(value: CalendarFiltersValue): void\n onOpenSettings(): void\n}\n\nexport interface CalendarTabsProps {\n tab: CalendarTab\n counts: { all: number; meetings: number; events: number }\n view: CalendarView\n onTabChange(tab: CalendarTab): void\n onViewChange(view: CalendarView): void\n}\n\nexport interface CalendarFooterProps {\n timezoneLabel: string\n onOpenShortcuts?: () => void\n}\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAkBlB,MAAM,4BAA4B,EAC/B,OAAO;AAAA,EACN,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,EAC1B,OAAO,EAAE,OAAO,EAAE,SAAS;AAC7B,CAAC,EACA,YAAY;AAER,MAAM,mCAAmC,EAC7C,OAAO;AAAA,EACN,IAAI,EAAE,OAAO;AAAA,EACb,iBAAiB,EAAE,OAAO;AAAA,EAC1B,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACtC,QAAQ,EAAE,OAAO;AAAA,EACjB,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC5C,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC3C,iBAAiB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAChD,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS;AAAA,EACxC,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACzC,cAAc,EAAE,MAAM,yBAAyB,EAAE,SAAS,EAAE,SAAS;AAAA,EACrE,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC/C,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC9C,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC/C,iBAAiB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAChD,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC5C,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACzC,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACvC,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAC5C,CAAC,EACA,YAAY;",
6
6
  "names": []
7
7
  }
@@ -161,15 +161,15 @@ function ParticipantsField({
161
161
  return /* @__PURE__ */ jsxs("div", { children: [
162
162
  /* @__PURE__ */ jsx("label", { className: "text-overline font-semibold uppercase text-muted-foreground tracking-wider", children: sectionLabel }),
163
163
  /* @__PURE__ */ jsxs("div", { className: "mt-2.5 flex flex-wrap content-center items-center gap-2 rounded-lg border border-border bg-background px-3 py-2.5", children: [
164
- participants.map((p) => /* @__PURE__ */ jsxs("div", { className: "inline-flex items-center gap-1.5 rounded-full border border-border bg-muted px-2.5 py-1.5", children: [
164
+ participants.map((p, index) => /* @__PURE__ */ jsxs("div", { className: "inline-flex items-center gap-1.5 rounded-full border border-border bg-muted px-2.5 py-1.5", children: [
165
165
  /* @__PURE__ */ jsx("span", { className: cn("inline-flex size-5 items-center justify-center rounded-full text-xs font-bold text-white", p.color ?? "bg-primary"), children: p.name.charAt(0).toUpperCase() }),
166
166
  /* @__PURE__ */ jsx("span", { className: "text-xs text-foreground", children: p.name }),
167
- /* @__PURE__ */ jsx(IconButton, { type: "button", variant: "ghost", size: "sm", onClick: () => removeParticipant(p.userId), className: "h-auto text-muted-foreground hover:text-foreground p-0", "aria-label": t("customers.schedule.removeParticipant", "Remove participant"), children: /* @__PURE__ */ jsx(X, { className: "size-3" }) })
168
- ] }, p.userId)),
167
+ /* @__PURE__ */ jsx(IconButton, { type: "button", variant: "ghost", size: "sm", onClick: () => removeParticipant(index), className: "h-auto text-muted-foreground hover:text-foreground p-0", "aria-label": t("customers.schedule.removeParticipant", "Remove participant"), children: /* @__PURE__ */ jsx(X, { className: "size-3" }) })
168
+ ] }, p.userId ?? p.email ?? index)),
169
169
  /* @__PURE__ */ jsx(
170
170
  ParticipantSearchPopover,
171
171
  {
172
- existingIds: new Set(participants.map((p) => p.userId)),
172
+ existingIds: new Set(participants.map((p) => p.userId).filter((userId) => Boolean(userId))),
173
173
  onAdd: (p) => setParticipants((prev) => [...prev, { ...p, status: "pending" }]),
174
174
  onAddMany: (nextParticipants) => {
175
175
  setParticipants((prev) => [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/customers/components/detail/schedule/ParticipantsField.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { Users, X, Clock, CheckCircle2, XCircle } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { hasMoreFromPage } from '@open-mercato/shared/lib/pagination/load-more'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'\nimport { SearchInput } from '@open-mercato/ui/primitives/search-input'\nimport { fetchAssignableStaffMembersPage } from '../assignableStaff'\nimport type { ActivityType, ScheduleFieldId } from './fieldConfig'\nimport { isVisible, getFieldLabel } from './fieldConfig'\nimport type { Participant, RsvpStatus } from './useScheduleFormState'\nimport { PARTICIPANT_COLORS } from './useScheduleFormState'\n\nconst PAGE_SIZE = 20\n\nfunction ParticipantSearchPopover({\n existingIds,\n onAdd,\n onAddMany,\n t,\n}: {\n existingIds: Set<string>\n onAdd: (p: Participant) => void\n onAddMany: (participants: Participant[]) => void\n t: (key: string, fallback: string) => string\n}) {\n const [open, setOpen] = React.useState(false)\n const [query, setQuery] = React.useState('')\n const [results, setResults] = React.useState<Array<{ userId: string; name: string; email: string }>>([])\n const [page, setPage] = React.useState(1)\n // Short-page termination instead of a `total`-derived page bound \u2014 see\n // `hasMoreFromPage`. Measured on the served count, not on `members`, which is\n // deduped by user id.\n const [hasMore, setHasMore] = React.useState(false)\n const [loading, setLoading] = React.useState(false)\n const [loadError, setLoadError] = React.useState<string | null>(null)\n const selectableResults = React.useMemo(\n () => results.filter((result) => !existingIds.has(result.userId)),\n [existingIds, results],\n )\n\n React.useEffect(() => {\n if (!open) return\n const controller = new AbortController()\n setLoading(true)\n fetchAssignableStaffMembersPage(query, { page, pageSize: PAGE_SIZE, signal: controller.signal })\n .then((result) => {\n const members = result.items\n const nextResults = members.map((member) => ({\n userId: member.userId,\n name: member.displayName,\n email: member.email ?? '',\n }))\n setResults((current) => {\n if (page <= 1) return nextResults\n const merged = new Map(current.map((entry) => [entry.userId, entry]))\n nextResults.forEach((entry) => merged.set(entry.userId, entry))\n return Array.from(merged.values())\n })\n setHasMore(hasMoreFromPage(result.servedCount, PAGE_SIZE))\n setLoadError(null)\n })\n .catch(() => {\n setResults([])\n setHasMore(false)\n setLoadError(\n t(\n 'customers.assignableStaff.loadError',\n 'Unable to load team members. Check your permissions and try again.',\n ),\n )\n })\n .finally(() => setLoading(false))\n return () => controller.abort()\n }, [open, page, query, t])\n\n React.useEffect(() => {\n if (!open) return\n setPage(1)\n }, [open, query])\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" className=\"h-auto inline-flex items-center gap-1.5 rounded-full border border-status-success-border bg-status-success-bg px-2.5 py-1.5 text-xs font-semibold text-foreground\">\n <Users className=\"size-3\" />\n {t('customers.schedule.addParticipant', 'Add participant')}\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-72 p-2\">\n <SearchInput\n value={query}\n onChange={setQuery}\n placeholder={t('customers.schedule.searchParticipant', 'Search team members...')}\n className=\"mb-2\"\n autoFocus\n />\n {selectableResults.length ? (\n <div className=\"mb-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"w-full\"\n onClick={() => {\n onAddMany(\n selectableResults.map((participant, index) => ({\n userId: participant.userId,\n name: participant.name,\n email: participant.email,\n color: PARTICIPANT_COLORS[(existingIds.size + index) % PARTICIPANT_COLORS.length],\n })),\n )\n setOpen(false)\n setQuery('')\n }}\n >\n {t('customers.schedule.addVisibleParticipants', 'Add all visible')}\n </Button>\n </div>\n ) : null}\n <div className=\"max-h-48 overflow-y-auto space-y-0.5\">\n {loading && <p className=\"px-2 py-3 text-xs text-muted-foreground text-center\">{t('customers.schedule.searching', 'Searching...')}</p>}\n {!loading && loadError && <p className=\"px-2 py-3 text-xs text-destructive text-center\">{loadError}</p>}\n {!loading && !loadError && results.length === 0 && <p className=\"px-2 py-3 text-xs text-muted-foreground text-center\">{t('customers.schedule.noResults', 'No results')}</p>}\n {results.map((r) => {\n const alreadyAdded = existingIds.has(r.userId)\n return (\n <Button\n key={r.userId}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n disabled={alreadyAdded}\n onClick={() => {\n onAdd({ userId: r.userId, name: r.name, email: r.email, color: PARTICIPANT_COLORS[existingIds.size % PARTICIPANT_COLORS.length] })\n setOpen(false)\n setQuery('')\n }}\n className={cn(\n 'h-auto flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm transition-colors',\n alreadyAdded ? 'opacity-40 cursor-default' : 'hover:bg-accent cursor-pointer',\n )}\n >\n <span className=\"inline-flex size-6 items-center justify-center rounded-full bg-muted text-xs font-bold shrink-0\">\n {r.name.charAt(0).toUpperCase()}\n </span>\n <span className=\"min-w-0 flex-1 truncate\">{r.name}</span>\n {r.email && <span className=\"text-xs text-muted-foreground truncate\">{r.email}</span>}\n </Button>\n )\n })}\n {!loading && !loadError && hasMore ? (\n <div className=\"px-2 py-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" className=\"w-full\" onClick={() => setPage((current) => current + 1)}>\n {t('customers.schedule.loadMore', 'Load more')}\n </Button>\n </div>\n ) : null}\n </div>\n </PopoverContent>\n </Popover>\n )\n}\n\ninterface ParticipantsFieldProps {\n visible: Set<ScheduleFieldId>\n activityType: ActivityType\n participants: Participant[]\n setParticipants: React.Dispatch<React.SetStateAction<Participant[]>>\n removeParticipant: (userId: string) => void\n guestPermissions: { canInviteOthers: boolean; canModify: boolean; canSeeList: boolean }\n setGuestPermissions: React.Dispatch<React.SetStateAction<{ canInviteOthers: boolean; canModify: boolean; canSeeList: boolean }>>\n}\n\nexport function ParticipantsField({\n visible,\n activityType,\n participants,\n setParticipants,\n removeParticipant,\n guestPermissions,\n setGuestPermissions,\n}: ParticipantsFieldProps) {\n const t = useT()\n\n if (!isVisible(activityType, 'participants')) return null\n\n const sectionLabel = getFieldLabel(\n activityType,\n 'participants',\n t,\n 'customers.schedule.participants',\n 'Participants',\n )\n\n return (\n <div>\n <label className=\"text-overline font-semibold uppercase text-muted-foreground tracking-wider\">\n {sectionLabel}\n </label>\n <div className=\"mt-2.5 flex flex-wrap content-center items-center gap-2 rounded-lg border border-border bg-background px-3 py-2.5\">\n {participants.map((p) => (\n <div key={p.userId} className=\"inline-flex items-center gap-1.5 rounded-full border border-border bg-muted px-2.5 py-1.5\">\n <span className={cn('inline-flex size-5 items-center justify-center rounded-full text-xs font-bold text-white', p.color ?? 'bg-primary')}>\n {p.name.charAt(0).toUpperCase()}\n </span>\n <span className=\"text-xs text-foreground\">{p.name}</span>\n <IconButton type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => removeParticipant(p.userId)} className=\"h-auto text-muted-foreground hover:text-foreground p-0\" aria-label={t('customers.schedule.removeParticipant', 'Remove participant')}>\n <X className=\"size-3\" />\n </IconButton>\n </div>\n ))}\n <ParticipantSearchPopover\n existingIds={new Set(participants.map((p) => p.userId))}\n onAdd={(p) => setParticipants((prev) => [...prev, { ...p, status: 'pending' as RsvpStatus }])}\n onAddMany={(nextParticipants) => {\n setParticipants((prev) => [\n ...prev,\n ...nextParticipants.map((participant) => ({ ...participant, status: 'pending' as RsvpStatus })),\n ])\n }}\n t={t}\n />\n </div>\n\n {/* Guest permissions -- shown when participants exist */}\n {participants.length > 0 && (\n <div className=\"mt-3 flex flex-wrap items-center gap-x-[16px] gap-y-[6px] text-xs\">\n <span className=\"font-medium text-muted-foreground\">{t('customers.schedule.guestPermissions', 'Guest permissions:')}</span>\n <label htmlFor=\"guest-perm-invite\" className=\"flex items-center gap-1.5 cursor-pointer\">\n <Checkbox id=\"guest-perm-invite\" checked={guestPermissions.canInviteOthers} onCheckedChange={(checked) => setGuestPermissions((p) => ({ ...p, canInviteOthers: checked === true }))} />\n {t('customers.schedule.guestPerm.invite', 'Invite others')}\n </label>\n <label htmlFor=\"guest-perm-modify\" className=\"flex items-center gap-1.5 cursor-pointer\">\n <Checkbox id=\"guest-perm-modify\" checked={guestPermissions.canModify} onCheckedChange={(checked) => setGuestPermissions((p) => ({ ...p, canModify: checked === true }))} />\n {t('customers.schedule.guestPerm.modify', 'Modify')}\n </label>\n <label htmlFor=\"guest-perm-see-list\" className=\"flex items-center gap-1.5 cursor-pointer\">\n <Checkbox id=\"guest-perm-see-list\" checked={guestPermissions.canSeeList} onCheckedChange={(checked) => setGuestPermissions((p) => ({ ...p, canSeeList: checked === true }))} />\n {t('customers.schedule.guestPerm.seeList', 'See list')}\n </label>\n </div>\n )}\n\n {/* RSVP summary -- shown when participants exist */}\n {participants.length > 0 && (() => {\n const accepted = participants.filter((p) => p.status === 'accepted').length\n const pending = participants.filter((p) => !p.status || p.status === 'pending').length\n const declined = participants.filter((p) => p.status === 'declined').length\n if (accepted === 0 && pending === 0 && declined === 0) return null\n return (\n <div className=\"mt-2 flex items-center gap-3 text-xs\">\n <span className=\"text-muted-foreground\">{t('customers.schedule.rsvp.label', 'Responses:')}</span>\n {accepted > 0 && <span className=\"flex items-center gap-1 font-medium text-status-success-text\"><CheckCircle2 className=\"size-3.5\" /> {accepted} {t('customers.schedule.rsvp.accepted', 'tak')}</span>}\n {pending > 0 && <span className=\"flex items-center gap-1 font-medium text-status-warning-text\"><Clock className=\"size-3.5\" /> {pending} {t('customers.schedule.rsvp.pending', 'czeka')}</span>}\n {declined > 0 && <span className=\"flex items-center gap-1 font-medium text-status-error-text\"><XCircle className=\"size-3.5\" /> {declined} {t('customers.schedule.rsvp.declined', 'nie')}</span>}\n </div>\n )\n })()}\n </div>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { Users, X, Clock, CheckCircle2, XCircle } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { hasMoreFromPage } from '@open-mercato/shared/lib/pagination/load-more'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'\nimport { SearchInput } from '@open-mercato/ui/primitives/search-input'\nimport { fetchAssignableStaffMembersPage } from '../assignableStaff'\nimport type { ActivityType, ScheduleFieldId } from './fieldConfig'\nimport { isVisible, getFieldLabel } from './fieldConfig'\nimport type { Participant, RsvpStatus } from './useScheduleFormState'\nimport { PARTICIPANT_COLORS } from './useScheduleFormState'\n\nconst PAGE_SIZE = 20\n\nfunction ParticipantSearchPopover({\n existingIds,\n onAdd,\n onAddMany,\n t,\n}: {\n existingIds: Set<string>\n onAdd: (p: Participant) => void\n onAddMany: (participants: Participant[]) => void\n t: (key: string, fallback: string) => string\n}) {\n const [open, setOpen] = React.useState(false)\n const [query, setQuery] = React.useState('')\n const [results, setResults] = React.useState<Array<{ userId: string; name: string; email: string }>>([])\n const [page, setPage] = React.useState(1)\n // Short-page termination instead of a `total`-derived page bound \u2014 see\n // `hasMoreFromPage`. Measured on the served count, not on `members`, which is\n // deduped by user id.\n const [hasMore, setHasMore] = React.useState(false)\n const [loading, setLoading] = React.useState(false)\n const [loadError, setLoadError] = React.useState<string | null>(null)\n const selectableResults = React.useMemo(\n () => results.filter((result) => !existingIds.has(result.userId)),\n [existingIds, results],\n )\n\n React.useEffect(() => {\n if (!open) return\n const controller = new AbortController()\n setLoading(true)\n fetchAssignableStaffMembersPage(query, { page, pageSize: PAGE_SIZE, signal: controller.signal })\n .then((result) => {\n const members = result.items\n const nextResults = members.map((member) => ({\n userId: member.userId,\n name: member.displayName,\n email: member.email ?? '',\n }))\n setResults((current) => {\n if (page <= 1) return nextResults\n const merged = new Map(current.map((entry) => [entry.userId, entry]))\n nextResults.forEach((entry) => merged.set(entry.userId, entry))\n return Array.from(merged.values())\n })\n setHasMore(hasMoreFromPage(result.servedCount, PAGE_SIZE))\n setLoadError(null)\n })\n .catch(() => {\n setResults([])\n setHasMore(false)\n setLoadError(\n t(\n 'customers.assignableStaff.loadError',\n 'Unable to load team members. Check your permissions and try again.',\n ),\n )\n })\n .finally(() => setLoading(false))\n return () => controller.abort()\n }, [open, page, query, t])\n\n React.useEffect(() => {\n if (!open) return\n setPage(1)\n }, [open, query])\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" className=\"h-auto inline-flex items-center gap-1.5 rounded-full border border-status-success-border bg-status-success-bg px-2.5 py-1.5 text-xs font-semibold text-foreground\">\n <Users className=\"size-3\" />\n {t('customers.schedule.addParticipant', 'Add participant')}\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-72 p-2\">\n <SearchInput\n value={query}\n onChange={setQuery}\n placeholder={t('customers.schedule.searchParticipant', 'Search team members...')}\n className=\"mb-2\"\n autoFocus\n />\n {selectableResults.length ? (\n <div className=\"mb-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"w-full\"\n onClick={() => {\n onAddMany(\n selectableResults.map((participant, index) => ({\n userId: participant.userId,\n name: participant.name,\n email: participant.email,\n color: PARTICIPANT_COLORS[(existingIds.size + index) % PARTICIPANT_COLORS.length],\n })),\n )\n setOpen(false)\n setQuery('')\n }}\n >\n {t('customers.schedule.addVisibleParticipants', 'Add all visible')}\n </Button>\n </div>\n ) : null}\n <div className=\"max-h-48 overflow-y-auto space-y-0.5\">\n {loading && <p className=\"px-2 py-3 text-xs text-muted-foreground text-center\">{t('customers.schedule.searching', 'Searching...')}</p>}\n {!loading && loadError && <p className=\"px-2 py-3 text-xs text-destructive text-center\">{loadError}</p>}\n {!loading && !loadError && results.length === 0 && <p className=\"px-2 py-3 text-xs text-muted-foreground text-center\">{t('customers.schedule.noResults', 'No results')}</p>}\n {results.map((r) => {\n const alreadyAdded = existingIds.has(r.userId)\n return (\n <Button\n key={r.userId}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n disabled={alreadyAdded}\n onClick={() => {\n onAdd({ userId: r.userId, name: r.name, email: r.email, color: PARTICIPANT_COLORS[existingIds.size % PARTICIPANT_COLORS.length] })\n setOpen(false)\n setQuery('')\n }}\n className={cn(\n 'h-auto flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm transition-colors',\n alreadyAdded ? 'opacity-40 cursor-default' : 'hover:bg-accent cursor-pointer',\n )}\n >\n <span className=\"inline-flex size-6 items-center justify-center rounded-full bg-muted text-xs font-bold shrink-0\">\n {r.name.charAt(0).toUpperCase()}\n </span>\n <span className=\"min-w-0 flex-1 truncate\">{r.name}</span>\n {r.email && <span className=\"text-xs text-muted-foreground truncate\">{r.email}</span>}\n </Button>\n )\n })}\n {!loading && !loadError && hasMore ? (\n <div className=\"px-2 py-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" className=\"w-full\" onClick={() => setPage((current) => current + 1)}>\n {t('customers.schedule.loadMore', 'Load more')}\n </Button>\n </div>\n ) : null}\n </div>\n </PopoverContent>\n </Popover>\n )\n}\n\ninterface ParticipantsFieldProps {\n visible: Set<ScheduleFieldId>\n activityType: ActivityType\n participants: Participant[]\n setParticipants: React.Dispatch<React.SetStateAction<Participant[]>>\n removeParticipant: (index: number) => void\n guestPermissions: { canInviteOthers: boolean; canModify: boolean; canSeeList: boolean }\n setGuestPermissions: React.Dispatch<React.SetStateAction<{ canInviteOthers: boolean; canModify: boolean; canSeeList: boolean }>>\n}\n\nexport function ParticipantsField({\n visible,\n activityType,\n participants,\n setParticipants,\n removeParticipant,\n guestPermissions,\n setGuestPermissions,\n}: ParticipantsFieldProps) {\n const t = useT()\n\n if (!isVisible(activityType, 'participants')) return null\n\n const sectionLabel = getFieldLabel(\n activityType,\n 'participants',\n t,\n 'customers.schedule.participants',\n 'Participants',\n )\n\n return (\n <div>\n <label className=\"text-overline font-semibold uppercase text-muted-foreground tracking-wider\">\n {sectionLabel}\n </label>\n <div className=\"mt-2.5 flex flex-wrap content-center items-center gap-2 rounded-lg border border-border bg-background px-3 py-2.5\">\n {participants.map((p, index) => (\n <div key={p.userId ?? p.email ?? index} className=\"inline-flex items-center gap-1.5 rounded-full border border-border bg-muted px-2.5 py-1.5\">\n <span className={cn('inline-flex size-5 items-center justify-center rounded-full text-xs font-bold text-white', p.color ?? 'bg-primary')}>\n {p.name.charAt(0).toUpperCase()}\n </span>\n <span className=\"text-xs text-foreground\">{p.name}</span>\n <IconButton type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => removeParticipant(index)} className=\"h-auto text-muted-foreground hover:text-foreground p-0\" aria-label={t('customers.schedule.removeParticipant', 'Remove participant')}>\n <X className=\"size-3\" />\n </IconButton>\n </div>\n ))}\n <ParticipantSearchPopover\n existingIds={new Set(participants.map((p) => p.userId).filter((userId): userId is string => Boolean(userId)))}\n onAdd={(p) => setParticipants((prev) => [...prev, { ...p, status: 'pending' as RsvpStatus }])}\n onAddMany={(nextParticipants) => {\n setParticipants((prev) => [\n ...prev,\n ...nextParticipants.map((participant) => ({ ...participant, status: 'pending' as RsvpStatus })),\n ])\n }}\n t={t}\n />\n </div>\n\n {/* Guest permissions -- shown when participants exist */}\n {participants.length > 0 && (\n <div className=\"mt-3 flex flex-wrap items-center gap-x-[16px] gap-y-[6px] text-xs\">\n <span className=\"font-medium text-muted-foreground\">{t('customers.schedule.guestPermissions', 'Guest permissions:')}</span>\n <label htmlFor=\"guest-perm-invite\" className=\"flex items-center gap-1.5 cursor-pointer\">\n <Checkbox id=\"guest-perm-invite\" checked={guestPermissions.canInviteOthers} onCheckedChange={(checked) => setGuestPermissions((p) => ({ ...p, canInviteOthers: checked === true }))} />\n {t('customers.schedule.guestPerm.invite', 'Invite others')}\n </label>\n <label htmlFor=\"guest-perm-modify\" className=\"flex items-center gap-1.5 cursor-pointer\">\n <Checkbox id=\"guest-perm-modify\" checked={guestPermissions.canModify} onCheckedChange={(checked) => setGuestPermissions((p) => ({ ...p, canModify: checked === true }))} />\n {t('customers.schedule.guestPerm.modify', 'Modify')}\n </label>\n <label htmlFor=\"guest-perm-see-list\" className=\"flex items-center gap-1.5 cursor-pointer\">\n <Checkbox id=\"guest-perm-see-list\" checked={guestPermissions.canSeeList} onCheckedChange={(checked) => setGuestPermissions((p) => ({ ...p, canSeeList: checked === true }))} />\n {t('customers.schedule.guestPerm.seeList', 'See list')}\n </label>\n </div>\n )}\n\n {/* RSVP summary -- shown when participants exist */}\n {participants.length > 0 && (() => {\n const accepted = participants.filter((p) => p.status === 'accepted').length\n const pending = participants.filter((p) => !p.status || p.status === 'pending').length\n const declined = participants.filter((p) => p.status === 'declined').length\n if (accepted === 0 && pending === 0 && declined === 0) return null\n return (\n <div className=\"mt-2 flex items-center gap-3 text-xs\">\n <span className=\"text-muted-foreground\">{t('customers.schedule.rsvp.label', 'Responses:')}</span>\n {accepted > 0 && <span className=\"flex items-center gap-1 font-medium text-status-success-text\"><CheckCircle2 className=\"size-3.5\" /> {accepted} {t('customers.schedule.rsvp.accepted', 'tak')}</span>}\n {pending > 0 && <span className=\"flex items-center gap-1 font-medium text-status-warning-text\"><Clock className=\"size-3.5\" /> {pending} {t('customers.schedule.rsvp.pending', 'czeka')}</span>}\n {declined > 0 && <span className=\"flex items-center gap-1 font-medium text-status-error-text\"><XCircle className=\"size-3.5\" /> {declined} {t('customers.schedule.rsvp.declined', 'nie')}</span>}\n </div>\n )\n })()}\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -67,7 +67,7 @@ function useScheduleFormState({ open, editData }) {
67
67
  setParticipants(
68
68
  Array.isArray(editData.participants) ? editData.participants.map((p, i) => ({
69
69
  userId: p.userId,
70
- name: p.name ?? p.userId,
70
+ name: p.name ?? p.email ?? p.userId ?? "",
71
71
  email: p.email,
72
72
  color: PARTICIPANT_COLORS[i % PARTICIPANT_COLORS.length],
73
73
  status: p.status ?? "pending"
@@ -139,8 +139,8 @@ function useScheduleFormState({ open, editData }) {
139
139
  lastReminderTypeRef.current = activityType;
140
140
  setReminderMinutes(DEFAULT_REMINDER_MINUTES[activityType]);
141
141
  }, [activityType, editData, open]);
142
- const removeParticipant = React.useCallback((userId) => {
143
- setParticipants((prev) => prev.filter((p) => p.userId !== userId));
142
+ const removeParticipant = React.useCallback((index) => {
143
+ setParticipants((prev) => prev.filter((_, i) => i !== index));
144
144
  }, []);
145
145
  const toggleRecurrenceDay = React.useCallback((index) => {
146
146
  setRecurrenceDays((prev) => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/customers/components/detail/schedule/useScheduleFormState.ts"],
4
- "sourcesContent": ["import * as React from 'react'\nimport type { ActivityType } from './fieldConfig'\n\nexport type RsvpStatus = 'pending' | 'accepted' | 'declined' | 'tentative'\n\nexport type Participant = {\n userId: string\n name: string\n email?: string\n color?: string\n status?: RsvpStatus\n}\n\nexport type LinkedEntity = {\n id: string\n type: 'company' | 'deal' | 'offer'\n label: string\n}\n\nexport type ScheduleActivityEditData = {\n id: string\n /** Record version for the OSS optimistic-lock header on edit (#2055). */\n updatedAt?: string | null\n interactionType?: string\n title?: string | null\n body?: string | null\n scheduledAt?: string | null\n /**\n * Historical timestamp for completed activities (status `done`). Required for\n * the edit prefill to restore the original date/time instead of falling back\n * to \"today\" (#1807).\n */\n occurredAt?: string | null\n durationMinutes?: number | null\n location?: string | null\n allDay?: boolean | null\n recurrenceRule?: string | null\n recurrenceEnd?: string | null\n participants?: Array<{ userId: string; name?: string; email?: string; status?: string }> | null\n reminderMinutes?: number | null\n visibility?: string | null\n linkedEntities?: Array<{ id: string; type: string; label: string }> | null\n guestPermissions?: { canInviteOthers?: boolean; canModify?: boolean; canSeeList?: boolean } | null\n}\n\nexport const PARTICIPANT_COLORS = [\n 'bg-chart-emerald',\n 'bg-chart-blue',\n 'bg-chart-orange',\n 'bg-chart-violet',\n 'bg-chart-pink',\n 'bg-chart-teal',\n]\n\n// Per-Figma defaults for the Reminder dropdown when the user picks an activity\n// type. Meeting/email keep the standard 15 min; tasks default to 1 day (1440 min)\n// because they're plan-ahead artefacts; calls default to 5 min as a stand-in for\n// the Figma \"After call ends\" treatment (which would need a non-numeric sentinel\n// in the API contract \u2014 tracked as a follow-up).\nconst DEFAULT_REMINDER_MINUTES: Record<ActivityType, number> = {\n meeting: 15,\n call: 5,\n task: 1440,\n email: 15,\n note: 15,\n}\n\nfunction padDatePart(value: number): string {\n return String(value).padStart(2, '0')\n}\n\nfunction formatLocalDateInput(date: Date): string {\n return `${date.getFullYear()}-${padDatePart(date.getMonth() + 1)}-${padDatePart(date.getDate())}`\n}\n\nfunction formatLocalTimeInput(date: Date): string {\n return `${padDatePart(date.getHours())}:${padDatePart(date.getMinutes())}`\n}\n\ninterface UseScheduleFormStateParams {\n open: boolean\n editData: ScheduleActivityEditData | null | undefined\n}\n\nexport function useScheduleFormState({ open, editData }: UseScheduleFormStateParams) {\n const [activityType, setActivityType] = React.useState<ActivityType>('meeting')\n const [title, setTitle] = React.useState('')\n const [date, setDate] = React.useState(() => formatLocalDateInput(new Date()))\n const [startTime, setStartTime] = React.useState('10:00')\n const [duration, setDuration] = React.useState(30)\n const [allDay, setAllDay] = React.useState(false)\n const [description, setDescription] = React.useState('')\n const [markdownEnabled, setMarkdownEnabled] = React.useState(true)\n const [location, setLocation] = React.useState('')\n const [reminderMinutes, setReminderMinutes] = React.useState(15)\n const [visibility, setVisibility] = React.useState('team')\n const [participants, setParticipants] = React.useState<Participant[]>([])\n const [linkedEntities, setLinkedEntities] = React.useState<LinkedEntity[]>([])\n const [recurrenceEnabled, setRecurrenceEnabled] = React.useState(false)\n const [recurrenceDays, setRecurrenceDays] = React.useState<boolean[]>([true, false, true, false, false, false, false])\n const [recurrenceEndType, setRecurrenceEndType] = React.useState<'never' | 'count' | 'date'>('never')\n const [recurrenceCount, setRecurrenceCount] = React.useState(8)\n const [recurrenceEndDate, setRecurrenceEndDate] = React.useState('')\n const [conflict, setConflict] = React.useState<string | null>(null)\n const [saving, setSaving] = React.useState(false)\n const [guestPermissions, setGuestPermissions] = React.useState({ canInviteOthers: true, canModify: false, canSeeList: true })\n\n React.useEffect(() => {\n if (open) {\n if (editData) {\n // Edit mode: populate from existing interaction\n const resolvedType = (editData.interactionType as ActivityType) ?? 'meeting'\n setActivityType(resolvedType)\n setTitle(editData.title ?? '')\n // For historical activities the canonical timestamp is `occurredAt`; for\n // planned/future ones it's `scheduledAt`. Without this fallback editing a\n // past activity prefilled to \"today\" instead of its actual moment (#1807).\n // Keep seed values in the user's local timezone, matching the cluster-E\n // local-day convention.\n const sourceTimestamp = editData.occurredAt ?? editData.scheduledAt ?? null\n const seedDate = sourceTimestamp ? new Date(sourceTimestamp) : new Date()\n const seedDateValid = !Number.isNaN(seedDate.getTime())\n const fallbackNow = new Date()\n const dateForForm = seedDateValid ? seedDate : fallbackNow\n setDate(formatLocalDateInput(dateForForm))\n setStartTime(formatLocalTimeInput(dateForForm))\n setDuration(editData.durationMinutes ?? 30)\n setAllDay(editData.allDay ?? false)\n setDescription(editData.body ?? '')\n setLocation(editData.location ?? '')\n // Use per-type default when the editData omits an explicit reminder\n // (the menu-driven \"New X\" flow opens the dialog with `reminderMinutes: null`).\n setReminderMinutes(editData.reminderMinutes ?? DEFAULT_REMINDER_MINUTES[resolvedType])\n setVisibility(editData.visibility ?? 'team')\n setParticipants(\n Array.isArray(editData.participants)\n ? editData.participants.map((p, i) => ({\n userId: p.userId,\n name: p.name ?? p.userId,\n email: p.email,\n color: PARTICIPANT_COLORS[i % PARTICIPANT_COLORS.length],\n status: (p.status ?? 'pending') as RsvpStatus,\n }))\n : [],\n )\n setLinkedEntities(\n Array.isArray(editData.linkedEntities)\n ? editData.linkedEntities.map((e) => ({ id: e.id, type: e.type as LinkedEntity['type'], label: e.label }))\n : [],\n )\n if (editData.recurrenceRule) {\n setRecurrenceEnabled(true)\n // Parse RRULE to set days and end type\n const rule = editData.recurrenceRule\n const byDayMatch = rule.match(/BYDAY=([A-Z,]+)/)\n if (byDayMatch) {\n const dayNames = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU']\n const activeDays = byDayMatch[1].split(',')\n setRecurrenceDays(dayNames.map((d) => activeDays.includes(d)))\n }\n const countMatch = rule.match(/COUNT=(\\d+)/)\n const untilMatch = rule.match(/UNTIL=(\\d{8})/)\n if (countMatch) {\n setRecurrenceEndType('count')\n setRecurrenceCount(Number(countMatch[1]))\n } else if (untilMatch) {\n setRecurrenceEndType('date')\n const raw = untilMatch[1]\n setRecurrenceEndDate(`${raw.slice(0, 4)}-${raw.slice(4, 6)}-${raw.slice(6, 8)}`)\n } else {\n setRecurrenceEndType('never')\n }\n } else {\n setRecurrenceEnabled(false)\n }\n if (editData.guestPermissions) {\n setGuestPermissions({\n canInviteOthers: editData.guestPermissions.canInviteOthers ?? true,\n canModify: editData.guestPermissions.canModify ?? false,\n canSeeList: editData.guestPermissions.canSeeList ?? true,\n })\n }\n } else {\n // Create mode: reset all fields\n setActivityType('meeting')\n setTitle('')\n setDate(formatLocalDateInput(new Date()))\n setStartTime('10:00')\n setDuration(30)\n setAllDay(false)\n setDescription('')\n setLocation('')\n setReminderMinutes(DEFAULT_REMINDER_MINUTES.meeting)\n setVisibility('team')\n setParticipants([])\n setLinkedEntities([])\n setRecurrenceEnabled(false)\n }\n setConflict(null)\n }\n return () => {\n // Safety net: restore body scroll if Radix Dialog fails to clean up\n document.body.style.removeProperty('overflow')\n document.body.style.removeProperty('pointer-events')\n }\n }, [open, editData])\n\n // Update the Reminder default when the activity type changes in create mode.\n // Skipped in edit mode (the persisted value wins), and gated by `open` to\n // avoid flipping the default in a closed-but-mounted dialog.\n const lastReminderTypeRef = React.useRef<ActivityType>('meeting')\n React.useEffect(() => {\n if (!open || editData) {\n lastReminderTypeRef.current = activityType\n return\n }\n if (lastReminderTypeRef.current === activityType) return\n lastReminderTypeRef.current = activityType\n setReminderMinutes(DEFAULT_REMINDER_MINUTES[activityType])\n }, [activityType, editData, open])\n\n const removeParticipant = React.useCallback((userId: string) => {\n setParticipants((prev) => prev.filter((p) => p.userId !== userId))\n }, [])\n\n const toggleRecurrenceDay = React.useCallback((index: number) => {\n setRecurrenceDays((prev) => {\n const next = [...prev]\n next[index] = !next[index]\n return next\n })\n }, [])\n\n return {\n activityType,\n setActivityType,\n title,\n setTitle,\n date,\n setDate,\n startTime,\n setStartTime,\n duration,\n setDuration,\n allDay,\n setAllDay,\n description,\n setDescription,\n markdownEnabled,\n setMarkdownEnabled,\n location,\n setLocation,\n reminderMinutes,\n setReminderMinutes,\n visibility,\n setVisibility,\n participants,\n setParticipants,\n linkedEntities,\n setLinkedEntities,\n recurrenceEnabled,\n setRecurrenceEnabled,\n recurrenceDays,\n setRecurrenceDays,\n recurrenceEndType,\n setRecurrenceEndType,\n recurrenceCount,\n setRecurrenceCount,\n recurrenceEndDate,\n setRecurrenceEndDate,\n conflict,\n setConflict,\n saving,\n setSaving,\n guestPermissions,\n setGuestPermissions,\n removeParticipant,\n toggleRecurrenceDay,\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import * as React from 'react'\nimport type { ActivityType } from './fieldConfig'\n\nexport type RsvpStatus = 'pending' | 'accepted' | 'declined' | 'tentative'\n\nexport type Participant = {\n userId?: string\n name: string\n email?: string\n color?: string\n status?: RsvpStatus\n}\n\nexport type LinkedEntity = {\n id: string\n type: 'company' | 'deal' | 'offer'\n label: string\n}\n\nexport type ScheduleActivityEditData = {\n id: string\n /** Record version for the OSS optimistic-lock header on edit (#2055). */\n updatedAt?: string | null\n interactionType?: string\n title?: string | null\n body?: string | null\n scheduledAt?: string | null\n /**\n * Historical timestamp for completed activities (status `done`). Required for\n * the edit prefill to restore the original date/time instead of falling back\n * to \"today\" (#1807).\n */\n occurredAt?: string | null\n durationMinutes?: number | null\n location?: string | null\n allDay?: boolean | null\n recurrenceRule?: string | null\n recurrenceEnd?: string | null\n participants?: Array<{ userId?: string; name?: string; email?: string; status?: string }> | null\n reminderMinutes?: number | null\n visibility?: string | null\n linkedEntities?: Array<{ id: string; type: string; label: string }> | null\n guestPermissions?: { canInviteOthers?: boolean; canModify?: boolean; canSeeList?: boolean } | null\n}\n\nexport const PARTICIPANT_COLORS = [\n 'bg-chart-emerald',\n 'bg-chart-blue',\n 'bg-chart-orange',\n 'bg-chart-violet',\n 'bg-chart-pink',\n 'bg-chart-teal',\n]\n\n// Per-Figma defaults for the Reminder dropdown when the user picks an activity\n// type. Meeting/email keep the standard 15 min; tasks default to 1 day (1440 min)\n// because they're plan-ahead artefacts; calls default to 5 min as a stand-in for\n// the Figma \"After call ends\" treatment (which would need a non-numeric sentinel\n// in the API contract \u2014 tracked as a follow-up).\nconst DEFAULT_REMINDER_MINUTES: Record<ActivityType, number> = {\n meeting: 15,\n call: 5,\n task: 1440,\n email: 15,\n note: 15,\n}\n\nfunction padDatePart(value: number): string {\n return String(value).padStart(2, '0')\n}\n\nfunction formatLocalDateInput(date: Date): string {\n return `${date.getFullYear()}-${padDatePart(date.getMonth() + 1)}-${padDatePart(date.getDate())}`\n}\n\nfunction formatLocalTimeInput(date: Date): string {\n return `${padDatePart(date.getHours())}:${padDatePart(date.getMinutes())}`\n}\n\ninterface UseScheduleFormStateParams {\n open: boolean\n editData: ScheduleActivityEditData | null | undefined\n}\n\nexport function useScheduleFormState({ open, editData }: UseScheduleFormStateParams) {\n const [activityType, setActivityType] = React.useState<ActivityType>('meeting')\n const [title, setTitle] = React.useState('')\n const [date, setDate] = React.useState(() => formatLocalDateInput(new Date()))\n const [startTime, setStartTime] = React.useState('10:00')\n const [duration, setDuration] = React.useState(30)\n const [allDay, setAllDay] = React.useState(false)\n const [description, setDescription] = React.useState('')\n const [markdownEnabled, setMarkdownEnabled] = React.useState(true)\n const [location, setLocation] = React.useState('')\n const [reminderMinutes, setReminderMinutes] = React.useState(15)\n const [visibility, setVisibility] = React.useState('team')\n const [participants, setParticipants] = React.useState<Participant[]>([])\n const [linkedEntities, setLinkedEntities] = React.useState<LinkedEntity[]>([])\n const [recurrenceEnabled, setRecurrenceEnabled] = React.useState(false)\n const [recurrenceDays, setRecurrenceDays] = React.useState<boolean[]>([true, false, true, false, false, false, false])\n const [recurrenceEndType, setRecurrenceEndType] = React.useState<'never' | 'count' | 'date'>('never')\n const [recurrenceCount, setRecurrenceCount] = React.useState(8)\n const [recurrenceEndDate, setRecurrenceEndDate] = React.useState('')\n const [conflict, setConflict] = React.useState<string | null>(null)\n const [saving, setSaving] = React.useState(false)\n const [guestPermissions, setGuestPermissions] = React.useState({ canInviteOthers: true, canModify: false, canSeeList: true })\n\n React.useEffect(() => {\n if (open) {\n if (editData) {\n // Edit mode: populate from existing interaction\n const resolvedType = (editData.interactionType as ActivityType) ?? 'meeting'\n setActivityType(resolvedType)\n setTitle(editData.title ?? '')\n // For historical activities the canonical timestamp is `occurredAt`; for\n // planned/future ones it's `scheduledAt`. Without this fallback editing a\n // past activity prefilled to \"today\" instead of its actual moment (#1807).\n // Keep seed values in the user's local timezone, matching the cluster-E\n // local-day convention.\n const sourceTimestamp = editData.occurredAt ?? editData.scheduledAt ?? null\n const seedDate = sourceTimestamp ? new Date(sourceTimestamp) : new Date()\n const seedDateValid = !Number.isNaN(seedDate.getTime())\n const fallbackNow = new Date()\n const dateForForm = seedDateValid ? seedDate : fallbackNow\n setDate(formatLocalDateInput(dateForForm))\n setStartTime(formatLocalTimeInput(dateForForm))\n setDuration(editData.durationMinutes ?? 30)\n setAllDay(editData.allDay ?? false)\n setDescription(editData.body ?? '')\n setLocation(editData.location ?? '')\n // Use per-type default when the editData omits an explicit reminder\n // (the menu-driven \"New X\" flow opens the dialog with `reminderMinutes: null`).\n setReminderMinutes(editData.reminderMinutes ?? DEFAULT_REMINDER_MINUTES[resolvedType])\n setVisibility(editData.visibility ?? 'team')\n setParticipants(\n Array.isArray(editData.participants)\n ? editData.participants.map((p, i) => ({\n userId: p.userId,\n name: p.name ?? p.email ?? p.userId ?? '',\n email: p.email,\n color: PARTICIPANT_COLORS[i % PARTICIPANT_COLORS.length],\n status: (p.status ?? 'pending') as RsvpStatus,\n }))\n : [],\n )\n setLinkedEntities(\n Array.isArray(editData.linkedEntities)\n ? editData.linkedEntities.map((e) => ({ id: e.id, type: e.type as LinkedEntity['type'], label: e.label }))\n : [],\n )\n if (editData.recurrenceRule) {\n setRecurrenceEnabled(true)\n // Parse RRULE to set days and end type\n const rule = editData.recurrenceRule\n const byDayMatch = rule.match(/BYDAY=([A-Z,]+)/)\n if (byDayMatch) {\n const dayNames = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU']\n const activeDays = byDayMatch[1].split(',')\n setRecurrenceDays(dayNames.map((d) => activeDays.includes(d)))\n }\n const countMatch = rule.match(/COUNT=(\\d+)/)\n const untilMatch = rule.match(/UNTIL=(\\d{8})/)\n if (countMatch) {\n setRecurrenceEndType('count')\n setRecurrenceCount(Number(countMatch[1]))\n } else if (untilMatch) {\n setRecurrenceEndType('date')\n const raw = untilMatch[1]\n setRecurrenceEndDate(`${raw.slice(0, 4)}-${raw.slice(4, 6)}-${raw.slice(6, 8)}`)\n } else {\n setRecurrenceEndType('never')\n }\n } else {\n setRecurrenceEnabled(false)\n }\n if (editData.guestPermissions) {\n setGuestPermissions({\n canInviteOthers: editData.guestPermissions.canInviteOthers ?? true,\n canModify: editData.guestPermissions.canModify ?? false,\n canSeeList: editData.guestPermissions.canSeeList ?? true,\n })\n }\n } else {\n // Create mode: reset all fields\n setActivityType('meeting')\n setTitle('')\n setDate(formatLocalDateInput(new Date()))\n setStartTime('10:00')\n setDuration(30)\n setAllDay(false)\n setDescription('')\n setLocation('')\n setReminderMinutes(DEFAULT_REMINDER_MINUTES.meeting)\n setVisibility('team')\n setParticipants([])\n setLinkedEntities([])\n setRecurrenceEnabled(false)\n }\n setConflict(null)\n }\n return () => {\n // Safety net: restore body scroll if Radix Dialog fails to clean up\n document.body.style.removeProperty('overflow')\n document.body.style.removeProperty('pointer-events')\n }\n }, [open, editData])\n\n // Update the Reminder default when the activity type changes in create mode.\n // Skipped in edit mode (the persisted value wins), and gated by `open` to\n // avoid flipping the default in a closed-but-mounted dialog.\n const lastReminderTypeRef = React.useRef<ActivityType>('meeting')\n React.useEffect(() => {\n if (!open || editData) {\n lastReminderTypeRef.current = activityType\n return\n }\n if (lastReminderTypeRef.current === activityType) return\n lastReminderTypeRef.current = activityType\n setReminderMinutes(DEFAULT_REMINDER_MINUTES[activityType])\n }, [activityType, editData, open])\n\n const removeParticipant = React.useCallback((index: number) => {\n setParticipants((prev) => prev.filter((_, i) => i !== index))\n }, [])\n\n const toggleRecurrenceDay = React.useCallback((index: number) => {\n setRecurrenceDays((prev) => {\n const next = [...prev]\n next[index] = !next[index]\n return next\n })\n }, [])\n\n return {\n activityType,\n setActivityType,\n title,\n setTitle,\n date,\n setDate,\n startTime,\n setStartTime,\n duration,\n setDuration,\n allDay,\n setAllDay,\n description,\n setDescription,\n markdownEnabled,\n setMarkdownEnabled,\n location,\n setLocation,\n reminderMinutes,\n setReminderMinutes,\n visibility,\n setVisibility,\n participants,\n setParticipants,\n linkedEntities,\n setLinkedEntities,\n recurrenceEnabled,\n setRecurrenceEnabled,\n recurrenceDays,\n setRecurrenceDays,\n recurrenceEndType,\n setRecurrenceEndType,\n recurrenceCount,\n setRecurrenceCount,\n recurrenceEndDate,\n setRecurrenceEndDate,\n conflict,\n setConflict,\n saving,\n setSaving,\n guestPermissions,\n setGuestPermissions,\n removeParticipant,\n toggleRecurrenceDay,\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }