@open-mercato/core 0.8.1-develop.7215.1.60786cf836 → 0.8.1-develop.7219.1.f96cb57e70
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/dist/modules/customers/components/calendar/CalendarEventEditor.js +1 -4
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarToolbar.js +3 -0
- package/dist/modules/customers/components/calendar/CalendarToolbar.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/RepeatField.js +1 -3
- package/dist/modules/customers/components/calendar/editor/RepeatField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/ScheduleSection.js +1 -5
- package/dist/modules/customers/components/calendar/editor/ScheduleSection.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/inputs.js.map +2 -2
- package/dist/modules/entities/lib/helpers.js +2 -2
- package/dist/modules/entities/lib/helpers.js.map +2 -2
- package/dist/modules/query_index/lib/batch.js +2 -2
- package/dist/modules/query_index/lib/batch.js.map +2 -2
- package/dist/modules/query_index/lib/document.js +7 -1
- package/dist/modules/query_index/lib/document.js.map +2 -2
- package/dist/modules/query_index/lib/indexer.js +9 -2
- package/dist/modules/query_index/lib/indexer.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/customers/components/calendar/CalendarEventEditor.tsx +1 -4
- package/src/modules/customers/components/calendar/CalendarToolbar.tsx +3 -0
- package/src/modules/customers/components/calendar/editor/RepeatField.tsx +0 -3
- package/src/modules/customers/components/calendar/editor/ScheduleSection.tsx +1 -7
- package/src/modules/customers/components/calendar/editor/inputs.tsx +0 -1
- package/src/modules/entities/lib/helpers.ts +2 -2
- package/src/modules/query_index/lib/batch.ts +8 -2
- package/src/modules/query_index/lib/document.ts +28 -0
- package/src/modules/query_index/lib/indexer.ts +15 -2
|
@@ -3,7 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Calendar, X } from "lucide-react";
|
|
5
5
|
import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
|
|
6
|
-
import {
|
|
6
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
7
7
|
import { apiCallOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
|
|
8
8
|
import { extractOptimisticLockConflict } from "@open-mercato/ui/backend/utils/optimisticLock";
|
|
9
9
|
import { surfaceRecordConflict } from "@open-mercato/ui/backend/conflicts";
|
|
@@ -72,7 +72,6 @@ function EditorBody({
|
|
|
72
72
|
staffEnabled
|
|
73
73
|
}) {
|
|
74
74
|
const t = useT();
|
|
75
|
-
const locale = useLocale();
|
|
76
75
|
const { setValue, errors } = ctx;
|
|
77
76
|
const form = formStateOfValues(ctx.values);
|
|
78
77
|
const config = KIND_CONFIG[form.kind];
|
|
@@ -162,7 +161,6 @@ function EditorBody({
|
|
|
162
161
|
startTime: form.startTime,
|
|
163
162
|
endDate: form.endDate,
|
|
164
163
|
endTime: form.endTime,
|
|
165
|
-
locale,
|
|
166
164
|
endsError: errors.ends,
|
|
167
165
|
onAllDayChange: (allDay) => update({ allDay }),
|
|
168
166
|
onDateChange: (date) => {
|
|
@@ -184,7 +182,6 @@ function EditorBody({
|
|
|
184
182
|
endType: form.repeatEndType,
|
|
185
183
|
count: form.repeatCount,
|
|
186
184
|
untilDate: form.repeatUntilDate,
|
|
187
|
-
locale,
|
|
188
185
|
onFreqChange: (repeatFreq) => update(
|
|
189
186
|
repeatFreq === "weekly" ? { repeatFreq, repeatDays: defaultRepeatDaysForDateInput(form.date) } : { repeatFreq }
|
|
190
187
|
),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/customers/components/calendar/CalendarEventEditor.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Calendar, X } from 'lucide-react'\nimport { VisuallyHidden } from '@radix-ui/react-visually-hidden'\nimport { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { extractOptimisticLockConflict } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { CrudForm, type CrudFormGroup, type CrudFormGroupComponentProps } from '@open-mercato/ui/backend/CrudForm'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Dialog, DialogContent, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Textarea } from '@open-mercato/ui/primitives/textarea'\nimport { useDialogKeyHandler } from '@open-mercato/ui/hooks/useDialogKeyHandler'\nimport { E } from '#generated/entities.ids.generated'\nimport {\n buildEditorTypeOptions,\n buildInteractionPayload,\n computeDurationMinutes,\n createDefaultFormState,\n defaultRepeatDaysForDateInput,\n editorKindOfInteractionType,\n KIND_CONFIG,\n parseItemToFormState,\n resolveSavedOwnerUserId,\n type EditorFormState,\n type EditorKind,\n type EditorPriority,\n} from '../../lib/calendar/editorPayload'\nimport type { ConflictScope } from '../../lib/calendar/preferences'\nimport { renderDictionaryIcon } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\nimport { normalizeCustomFieldSubmitValue } from '../detail/customFieldUtils'\nimport type { CalendarItem } from './types'\nimport { EDITOR_SCROLL_EVENT, Field } from './editor/inputs'\nimport { SegmentGroup } from './editor/SegmentGroup'\nimport { PriorityField } from './editor/PriorityField'\nimport { RelatedToField } from './editor/RelatedToField'\nimport { RepeatField } from './editor/RepeatField'\nimport { PeopleField } from './editor/PeopleField'\nimport { ResourcesField } from './editor/ResourcesField'\nimport { ScheduleSection } from './editor/ScheduleSection'\nimport { LocationField } from './editor/LocationField'\nimport { useConflictProbe, useEditorLabelResolution } from './editor/hooks'\n\nexport interface CalendarEventEditorProps {\n open: boolean\n mode: 'create' | 'edit'\n item?: CalendarItem | null\n defaultDate?: Date\n defaultRange?: { start: Date; end: Date } | null\n typeLabels: Record<string, string>\n typeIcons?: Record<string, string | null>\n conflictScope?: ConflictScope\n currentUserId?: string | null\n resourcesEnabled?: boolean\n staffEnabled?: boolean\n onOpenChange(open: boolean): void\n onSaved(): void\n}\n\nconst FORM_ID = 'customers-calendar-event-editor'\nconst INTERACTION_ENTITY_IDS = [E.customers.customer_interaction]\n\nconst PEOPLE_FIELD_TEXT = {\n attendees: { labelKey: 'customers.calendar.editor.attendees', label: 'Attendees', placeholderKey: 'customers.calendar.editor.addPeoplePlaceholder', placeholder: 'Add staff or customer\u2026' },\n participants: { labelKey: 'customers.calendar.editor.participants', label: 'Participants', placeholderKey: 'customers.calendar.editor.addPeoplePlaceholder', placeholder: 'Add staff or customer\u2026' },\n to: { labelKey: 'customers.calendar.editor.to', label: 'To', placeholderKey: 'customers.calendar.editor.addRecipientPlaceholder', placeholder: 'Add recipient\u2026' },\n} as const\n\n// CrudForm values carry the flattened EditorFormState keys (seeded via\n// initialValues) plus cf_* custom-field keys managed by CrudForm itself.\nfunction formStateOfValues(values: Record<string, unknown>): EditorFormState {\n return values as unknown as EditorFormState\n}\n\nfunction customFieldInitialValues(item: CalendarItem): Record<string, unknown> {\n const customValues = (item.raw as Record<string, unknown>).customValues\n if (!customValues || typeof customValues !== 'object' || Array.isArray(customValues)) return {}\n return Object.fromEntries(\n Object.entries(customValues as Record<string, unknown>).map(([key, value]) => [`cf_${key}`, value]),\n )\n}\n\ntype EditorBodyProps = {\n ctx: CrudFormGroupComponentProps\n open: boolean\n isEdit: boolean\n item?: CalendarItem | null\n typeLabels: Record<string, string>\n typeIcons: Record<string, string | null>\n conflictScope: ConflictScope\n currentUserId: string | null\n resourcesEnabled: boolean\n staffEnabled: boolean\n}\n\nfunction EditorBody({\n ctx,\n open,\n isEdit,\n item,\n typeLabels,\n typeIcons,\n conflictScope,\n currentUserId,\n resourcesEnabled,\n staffEnabled,\n}: EditorBodyProps) {\n const t = useT()\n const locale = useLocale()\n const { setValue, errors } = ctx\n const form = formStateOfValues(ctx.values)\n const config = KIND_CONFIG[form.kind]\n\n const update = React.useCallback(\n (patch: Partial<EditorFormState>) => {\n for (const [key, value] of Object.entries(patch)) setValue(key, value)\n },\n [setValue],\n )\n\n useEditorLabelResolution(open, form, update)\n // Probe against the owner the interaction will actually be SAVED with so the\n // editor warning matches the grid's post-save conflict rings: tasks own via\n // their assignee, other kinds stay ownerless on create / keep the existing\n // owner on edit (conflicts then come from shared participants).\n const draftOwnerUserId = resolveSavedOwnerUserId(config, form, isEdit, item?.ownerUserId ?? null)\n // Self-exclude by the underlying interaction id (raw.id): findEditorConflictItems\n // drops candidates by raw.id, and every expanded occurrence of a recurring series\n // shares it \u2014 so the edited record never conflicts with itself.\n const conflict = useConflictProbe(open, form, config, isEdit && item ? item.raw.id : null, draftOwnerUserId, conflictScope, currentUserId)\n\n const kindLabels = React.useMemo(\n () => ({\n meeting: t('customers.calendar.editor.types.meeting', 'meeting'),\n call: t('customers.calendar.editor.types.call', 'call'),\n email: t('customers.calendar.editor.types.email', 'email'),\n note: t('customers.calendar.editor.types.note', 'note'),\n event: t('customers.calendar.editor.types.event', 'event'),\n task: t('customers.calendar.editor.types.task', 'task'),\n }),\n [t],\n ) satisfies Record<EditorKind, string>\n\n const selectedType = form.category ?? form.kind\n const typeOptions = React.useMemo(\n () => buildEditorTypeOptions({ typeLabels, typeIcons, selectedValue: selectedType, kindLabels }),\n [typeLabels, typeIcons, selectedType, kindLabels],\n )\n const typeSwitcherOptions = React.useMemo(\n () =>\n typeOptions.map((option) => ({\n value: option.value,\n label: option.label,\n icon: renderDictionaryIcon(option.icon, 'h-4 w-4'),\n })),\n [typeOptions],\n )\n\n // Candidates for the Call \"insert phone from contact\" button: the linked\n // person (companies have no phone) + customer attendees. Their userId is the\n // person id, so the picker can resolve primary_phone from the people API.\n const phoneContactIds = React.useMemo(() => {\n const ids: string[] = []\n if (form.relatedTo && form.relatedTo.kind !== 'company') ids.push(form.relatedTo.id)\n for (const participant of form.participants) {\n if (participant.isCustomer && participant.userId) ids.push(participant.userId)\n }\n return Array.from(new Set(ids))\n }, [form.relatedTo, form.participants])\n\n const titleLabel = form.kind === 'email'\n ? t('customers.calendar.editor.titleLabel.email', 'Subject')\n : form.kind === 'note'\n ? t('customers.calendar.editor.titleLabel.note', 'Note')\n : t('customers.calendar.editor.titleLabel.generic', 'Title')\n\n return (\n // Single column on phones (full-screen sheet). On lg+ the dialog widens and\n // the fields group into two thematic columns: WHEN (schedule + repeat) on\n // the left, CONTEXT (related record, category, location) on the right;\n // people/resources and the task fields pair up below; title, description\n // and the type switcher span both columns.\n <div className=\"grid w-full grid-cols-1 items-start gap-4 lg:grid-cols-2 lg:gap-x-6\">\n {conflict ? (\n <Alert status=\"warning\" className=\"rounded-lg lg:col-span-2\">\n <AlertTitle>{t('customers.calendar.editor.conflictTitle', 'Calendar conflict')}</AlertTitle>\n <AlertDescription>{conflict}</AlertDescription>\n </Alert>\n ) : null}\n <div className=\"w-full lg:col-span-2\">\n <SegmentGroup<string>\n ariaLabel={t('customers.calendar.editor.typeSwitcher', 'Event type')}\n value={selectedType}\n onChange={(type) => update({ kind: editorKindOfInteractionType(type), category: type })}\n options={typeSwitcherOptions}\n />\n </div>\n <Field label={titleLabel} error={errors.title} className=\"lg:col-span-2\">\n <Input\n type=\"text\"\n value={form.title}\n onChange={(event) => update({ title: event.target.value })}\n placeholder={t('customers.calendar.editor.titlePlaceholder', 'Add a title\u2026')}\n aria-label={titleLabel}\n autoFocus\n size=\"lg\"\n />\n </Field>\n <div className=\"flex w-full flex-col gap-4\">\n <ScheduleSection\n dateLabel={config.dateLabel}\n hasAllDay={config.hasAllDay}\n hasEnd={config.hasEnd}\n allDay={form.allDay}\n date={form.date}\n startTime={form.startTime}\n endDate={form.endDate}\n endTime={form.endTime}\n locale={locale}\n endsError={errors.ends}\n onAllDayChange={(allDay) => update({ allDay })}\n onDateChange={(date) => {\n const untouchedDefault =\n JSON.stringify(form.repeatDays) === JSON.stringify(defaultRepeatDaysForDateInput(form.date))\n update(\n untouchedDefault\n ? { date, repeatDays: defaultRepeatDaysForDateInput(date) }\n : { date },\n )\n }}\n onStartTimeChange={(startTime) => update({ startTime })}\n onEndDateChange={(endDate) => update({ endDate })}\n onEndTimeChange={(endTime) => update({ endTime })}\n />\n {config.hasRepeat ? (\n <RepeatField\n freq={form.repeatFreq}\n days={form.repeatDays}\n endType={form.repeatEndType}\n count={form.repeatCount}\n untilDate={form.repeatUntilDate}\n locale={locale}\n onFreqChange={(repeatFreq) =>\n update(\n repeatFreq === 'weekly'\n ? { repeatFreq, repeatDays: defaultRepeatDaysForDateInput(form.date) }\n : { repeatFreq },\n )}\n onToggleDay={(index) =>\n update({ repeatDays: form.repeatDays.map((active, dayIndex) => (dayIndex === index ? !active : active)) })}\n onEndTypeChange={(repeatEndType) => update({ repeatEndType })}\n onCountChange={(repeatCount) => update({ repeatCount })}\n onUntilDateChange={(repeatUntilDate) => update({ repeatUntilDate })}\n />\n ) : null}\n {config.people && config.people !== 'assignee' ? (\n <Field label={t(PEOPLE_FIELD_TEXT[config.people].labelKey, PEOPLE_FIELD_TEXT[config.people].label)}>\n <PeopleField\n mode=\"multi\"\n includeCustomers\n includeStaff={staffEnabled}\n placeholder={t(PEOPLE_FIELD_TEXT[config.people].placeholderKey, PEOPLE_FIELD_TEXT[config.people].placeholder)}\n ariaLabel={t(PEOPLE_FIELD_TEXT[config.people].labelKey, PEOPLE_FIELD_TEXT[config.people].label)}\n value={form.participants}\n onChange={(participants) => update({ participants })}\n />\n </Field>\n ) : null}\n {config.people === 'assignee' && staffEnabled ? (\n <Field label={t('customers.calendar.editor.assignee', 'Assignee')} error={errors.assignee}>\n <PeopleField\n mode=\"single\"\n includeCustomers={false}\n includeStaff\n placeholder={t('customers.calendar.editor.assigneePlaceholder', 'Assign to a team member\u2026')}\n ariaLabel={t('customers.calendar.editor.assignee', 'Assignee')}\n value={form.assigneeUserId\n ? [{ userId: form.assigneeUserId, name: form.assigneeName ?? form.assigneeUserId, isCustomer: false }]\n : []}\n onChange={(entries) => {\n const next = entries[entries.length - 1] ?? null\n update({ assigneeUserId: next?.userId ?? null, assigneeName: next?.name ?? null })\n }}\n />\n </Field>\n ) : null}\n </div>\n <div className=\"flex w-full flex-col gap-4\">\n <Field label={t('customers.calendar.editor.relatedTo', 'Related to')} error={errors.relatedTo}>\n <RelatedToField\n label={t('customers.calendar.editor.relatedTo', 'Related to')}\n value={form.relatedTo}\n deal={form.dealId && form.dealLabel ? { id: form.dealId, label: form.dealLabel } : null}\n onChange={(relatedTo) => update({ relatedTo })}\n onDealChange={(deal) => update({ dealId: deal?.id ?? null, dealLabel: deal?.label ?? null })}\n error={errors.relatedTo}\n />\n </Field>\n {config.location ? (\n <LocationField\n variant={config.location}\n value={form.location}\n onChange={(location) => update({ location })}\n phoneContactIds={phoneContactIds}\n />\n ) : null}\n {resourcesEnabled ? (\n <Field label={t('customers.calendar.editor.resources', 'Resources')}>\n <ResourcesField\n placeholder={t('customers.calendar.editor.resourcesPlaceholder', 'Add a resource\u2026')}\n ariaLabel={t('customers.calendar.editor.resources', 'Resources')}\n value={form.resources}\n onChange={(resources) => update({ resources })}\n />\n </Field>\n ) : null}\n {config.hasPriority ? (\n <Field label={t('customers.calendar.editor.priority.label', 'Priority')}>\n <PriorityField\n value={form.priority}\n ariaLabel={t('customers.calendar.editor.priority.label', 'Priority')}\n labels={{\n low: t('customers.calendar.editor.priority.low', 'Low'),\n medium: t('customers.calendar.editor.priority.medium', 'Medium'),\n high: t('customers.calendar.editor.priority.high', 'High'),\n }}\n onChange={(priority) => update({ priority })}\n />\n </Field>\n ) : null}\n </div>\n <Field label={t('customers.calendar.editor.description', 'Description')} className=\"lg:col-span-2\">\n <Textarea\n value={form.description}\n onChange={(event) => update({ description: event.target.value })}\n placeholder={t('customers.calendar.editor.descriptionPlaceholder', 'Add details\u2026')}\n aria-label={t('customers.calendar.editor.description', 'Description')}\n className=\"h-20 resize-none\"\n />\n </Field>\n </div>\n )\n}\n\nexport function CalendarEventEditor({\n open,\n mode,\n item,\n defaultDate,\n defaultRange,\n typeLabels,\n typeIcons,\n conflictScope,\n currentUserId,\n resourcesEnabled,\n staffEnabled,\n onOpenChange,\n onSaved,\n}: CalendarEventEditorProps) {\n const t = useT()\n const [saving, setSaving] = React.useState(false)\n const isEdit = mode === 'edit' && Boolean(item?.id)\n\n // CrudForm reads initialValues once, so every dialog open gets a fresh form\n // instance keyed by the open sequence + edited record.\n const [openSeq, setOpenSeq] = React.useState(0)\n const wasOpenRef = React.useRef(false)\n React.useEffect(() => {\n if (open && !wasOpenRef.current) setOpenSeq((seq) => seq + 1)\n wasOpenRef.current = open\n }, [open])\n const formKey = `${mode}:${item?.id ?? 'new'}:${openSeq}`\n\n const initialValues = React.useMemo<Record<string, unknown>>(() => {\n if (isEdit && item) {\n return {\n ...parseItemToFormState(item),\n ...customFieldInitialValues(item),\n id: item.id,\n updatedAt: item.updatedAt ?? undefined,\n }\n }\n return { ...createDefaultFormState(defaultDate ?? null, undefined, defaultRange ?? null) }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- openSeq re-seeds defaults per dialog open\n }, [isEdit, item, defaultDate, defaultRange, openSeq])\n\n const handleSubmit = React.useCallback(\n async (values: Record<string, unknown>) => {\n const form = formStateOfValues(values)\n const config = KIND_CONFIG[form.kind]\n const fieldErrors: Record<string, string> = {}\n if (!form.title.trim()) {\n fieldErrors.title = t('customers.calendar.editor.validation.titleRequired', 'Title is required')\n }\n if (!form.relatedTo) {\n fieldErrors.relatedTo = t('customers.calendar.editor.validation.relatedToRequired', 'Select a person or company to link this event')\n }\n if (config.hasEnd && !form.allDay && computeDurationMinutes(form) === null) {\n fieldErrors.ends = t('customers.calendar.editor.validation.endsBeforeStarts', 'End must be after start')\n }\n // A task must be assigned to a team member \u2014 surface the requirement inline\n // instead of letting the save fail with no visible reason (#3747 feedback).\n if (config.people === 'assignee' && staffEnabled !== false && !form.assigneeUserId) {\n fieldErrors.assignee = t('customers.calendar.editor.validation.assigneeRequired', 'Assign this task to a team member')\n }\n if (Object.keys(fieldErrors).length > 0) {\n throw createCrudFormError(Object.values(fieldErrors)[0], fieldErrors)\n }\n setSaving(true)\n try {\n const payload = buildInteractionPayload(form, {\n mode,\n id: item?.id,\n resourcesEnabled: resourcesEnabled === true,\n staffEnabled: staffEnabled !== false,\n })\n const custom = collectCustomFieldValues(values, {\n transform: (value) => normalizeCustomFieldSubmitValue(value),\n })\n for (const [key, value] of Object.entries(custom)) payload[`cf_${key}`] = value\n // CrudForm supplies the optimistic-lock header (auto-derived from\n // initialValues.updatedAt) via scoped request headers around onSubmit.\n await apiCallOrThrow('/api/customers/interactions', {\n method: isEdit ? 'PUT' : 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n flash(t('customers.calendar.editor.saved', 'Event saved'), 'success')\n onOpenChange(false)\n requestAnimationFrame(() => { onSaved() })\n } catch (err) {\n // Surface an optimistic-lock 409 as the persistent conflict bar and\n // close \u2014 the bar renders at page level, behind the dialog overlay.\n if (extractOptimisticLockConflict(err)) {\n surfaceRecordConflict(err, t)\n onOpenChange(false)\n return\n }\n throw err\n } finally {\n setSaving(false)\n }\n },\n [isEdit, item?.id, mode, onOpenChange, onSaved, resourcesEnabled, staffEnabled, t],\n )\n\n const groups = React.useMemo<CrudFormGroup[]>(\n () => [\n {\n id: 'details',\n bare: true,\n component: (ctx) => (\n <EditorBody\n ctx={ctx}\n open={open}\n isEdit={isEdit}\n item={item}\n typeLabels={typeLabels}\n typeIcons={typeIcons ?? {}}\n conflictScope={conflictScope ?? 'all'}\n currentUserId={currentUserId ?? null}\n resourcesEnabled={resourcesEnabled === true}\n staffEnabled={staffEnabled !== false}\n />\n ),\n },\n { id: 'customFields', kind: 'customFields' },\n ],\n [open, isEdit, item, typeLabels, typeIcons, conflictScope, currentUserId, resourcesEnabled, staffEnabled],\n )\n\n const handleKeyDown = useDialogKeyHandler({\n onConfirm: () => {\n const formElement = document.getElementById(FORM_ID)\n if (formElement instanceof HTMLFormElement) formElement.requestSubmit()\n },\n disabled: saving,\n })\n\n const dialogTitle = isEdit ? t('customers.calendar.editor.title.edit', 'Edit event') : t('customers.calendar.editor.title.create', 'New event')\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent\n onKeyDown={handleKeyDown}\n // Only dismiss on a genuine click OUTSIDE the whole editor. Radix\n // relays a click inside the dialog (but outside an open DS\n // DatePicker/Select popover) up to the dialog as an \"interact\n // outside\" event when it dismisses that popover \u2014 without this guard\n // it would tear down the entire editor. Keep it open when the target\n // is inside the dialog body or inside any portalled popover.\n onInteractOutside={(event) => {\n const target = event.detail.originalEvent.target as HTMLElement | null\n if (target?.closest('[data-dialog-content]') || target?.closest('[data-radix-popper-content-wrapper]')) {\n event.preventDefault()\n }\n }}\n aria-describedby={undefined}\n dismissible={false}\n className=\"flex h-dvh max-h-dvh w-screen max-w-none flex-col gap-0 overflow-hidden rounded-none border-0 bg-background p-0 shadow-xl sm:h-auto sm:max-h-[calc(100dvh-4rem)] sm:w-full sm:max-w-lg sm:rounded-2xl sm:border-0 lg:max-w-3xl\"\n >\n <VisuallyHidden>\n <DialogTitle>{dialogTitle}</DialogTitle>\n </VisuallyHidden>\n <div className=\"flex shrink-0 items-center gap-3 border-b border-border bg-background py-4 pl-5 pr-4\">\n <Calendar aria-hidden className=\"size-6 shrink-0 text-foreground\" strokeWidth={1.75} />\n <p className=\"min-w-0 flex-1 text-sm font-medium leading-5 text-foreground\">{dialogTitle}</p>\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => onOpenChange(false)}\n aria-label={t('customers.calendar.editor.close', 'Close')}\n className=\"shrink-0 text-muted-foreground\"\n >\n <X aria-hidden className=\"size-5\" />\n </IconButton>\n </div>\n <div\n className=\"flex-1 overflow-y-auto\"\n onScroll={() => {\n // Tell the DS date/time fields to close their (controlled) popover\n // so a portalled popover doesn't float over the form or drift away\n // from its field while scrolling (#3747 feedback). Radix ignores\n // synthetic dismiss events, so the fields drive their own `open`.\n document.dispatchEvent(new CustomEvent(EDITOR_SCROLL_EVENT))\n }}\n >\n <div className=\"px-4 py-4 sm:px-6 sm:py-5\">\n <CrudForm<Record<string, unknown>>\n key={formKey}\n formId={FORM_ID}\n embedded\n hideFooterActions\n customFieldsManageMode=\"page\"\n fields={[]}\n groups={groups}\n initialValues={initialValues}\n entityIds={INTERACTION_ENTITY_IDS}\n onSubmit={handleSubmit}\n />\n </div>\n </div>\n <div className=\"flex shrink-0 items-center justify-end gap-3 border-t border-border bg-background px-5 py-4\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => onOpenChange(false)}\n >\n {t('customers.calendar.editor.cancel', 'Cancel')}\n </Button>\n <Button\n type=\"submit\"\n form={FORM_ID}\n disabled={saving}\n >\n {saving ? t('customers.calendar.editor.saving', 'Saving\u2026') : t('customers.calendar.editor.save', 'Save event')}\n </Button>\n </div>\n </DialogContent>\n </Dialog>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Calendar, X } from 'lucide-react'\nimport { VisuallyHidden } from '@radix-ui/react-visually-hidden'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { extractOptimisticLockConflict } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { CrudForm, type CrudFormGroup, type CrudFormGroupComponentProps } from '@open-mercato/ui/backend/CrudForm'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Dialog, DialogContent, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Textarea } from '@open-mercato/ui/primitives/textarea'\nimport { useDialogKeyHandler } from '@open-mercato/ui/hooks/useDialogKeyHandler'\nimport { E } from '#generated/entities.ids.generated'\nimport {\n buildEditorTypeOptions,\n buildInteractionPayload,\n computeDurationMinutes,\n createDefaultFormState,\n defaultRepeatDaysForDateInput,\n editorKindOfInteractionType,\n KIND_CONFIG,\n parseItemToFormState,\n resolveSavedOwnerUserId,\n type EditorFormState,\n type EditorKind,\n type EditorPriority,\n} from '../../lib/calendar/editorPayload'\nimport type { ConflictScope } from '../../lib/calendar/preferences'\nimport { renderDictionaryIcon } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\nimport { normalizeCustomFieldSubmitValue } from '../detail/customFieldUtils'\nimport type { CalendarItem } from './types'\nimport { EDITOR_SCROLL_EVENT, Field } from './editor/inputs'\nimport { SegmentGroup } from './editor/SegmentGroup'\nimport { PriorityField } from './editor/PriorityField'\nimport { RelatedToField } from './editor/RelatedToField'\nimport { RepeatField } from './editor/RepeatField'\nimport { PeopleField } from './editor/PeopleField'\nimport { ResourcesField } from './editor/ResourcesField'\nimport { ScheduleSection } from './editor/ScheduleSection'\nimport { LocationField } from './editor/LocationField'\nimport { useConflictProbe, useEditorLabelResolution } from './editor/hooks'\n\nexport interface CalendarEventEditorProps {\n open: boolean\n mode: 'create' | 'edit'\n item?: CalendarItem | null\n defaultDate?: Date\n defaultRange?: { start: Date; end: Date } | null\n typeLabels: Record<string, string>\n typeIcons?: Record<string, string | null>\n conflictScope?: ConflictScope\n currentUserId?: string | null\n resourcesEnabled?: boolean\n staffEnabled?: boolean\n onOpenChange(open: boolean): void\n onSaved(): void\n}\n\nconst FORM_ID = 'customers-calendar-event-editor'\nconst INTERACTION_ENTITY_IDS = [E.customers.customer_interaction]\n\nconst PEOPLE_FIELD_TEXT = {\n attendees: { labelKey: 'customers.calendar.editor.attendees', label: 'Attendees', placeholderKey: 'customers.calendar.editor.addPeoplePlaceholder', placeholder: 'Add staff or customer\u2026' },\n participants: { labelKey: 'customers.calendar.editor.participants', label: 'Participants', placeholderKey: 'customers.calendar.editor.addPeoplePlaceholder', placeholder: 'Add staff or customer\u2026' },\n to: { labelKey: 'customers.calendar.editor.to', label: 'To', placeholderKey: 'customers.calendar.editor.addRecipientPlaceholder', placeholder: 'Add recipient\u2026' },\n} as const\n\n// CrudForm values carry the flattened EditorFormState keys (seeded via\n// initialValues) plus cf_* custom-field keys managed by CrudForm itself.\nfunction formStateOfValues(values: Record<string, unknown>): EditorFormState {\n return values as unknown as EditorFormState\n}\n\nfunction customFieldInitialValues(item: CalendarItem): Record<string, unknown> {\n const customValues = (item.raw as Record<string, unknown>).customValues\n if (!customValues || typeof customValues !== 'object' || Array.isArray(customValues)) return {}\n return Object.fromEntries(\n Object.entries(customValues as Record<string, unknown>).map(([key, value]) => [`cf_${key}`, value]),\n )\n}\n\ntype EditorBodyProps = {\n ctx: CrudFormGroupComponentProps\n open: boolean\n isEdit: boolean\n item?: CalendarItem | null\n typeLabels: Record<string, string>\n typeIcons: Record<string, string | null>\n conflictScope: ConflictScope\n currentUserId: string | null\n resourcesEnabled: boolean\n staffEnabled: boolean\n}\n\nfunction EditorBody({\n ctx,\n open,\n isEdit,\n item,\n typeLabels,\n typeIcons,\n conflictScope,\n currentUserId,\n resourcesEnabled,\n staffEnabled,\n}: EditorBodyProps) {\n const t = useT()\n const { setValue, errors } = ctx\n const form = formStateOfValues(ctx.values)\n const config = KIND_CONFIG[form.kind]\n\n const update = React.useCallback(\n (patch: Partial<EditorFormState>) => {\n for (const [key, value] of Object.entries(patch)) setValue(key, value)\n },\n [setValue],\n )\n\n useEditorLabelResolution(open, form, update)\n // Probe against the owner the interaction will actually be SAVED with so the\n // editor warning matches the grid's post-save conflict rings: tasks own via\n // their assignee, other kinds stay ownerless on create / keep the existing\n // owner on edit (conflicts then come from shared participants).\n const draftOwnerUserId = resolveSavedOwnerUserId(config, form, isEdit, item?.ownerUserId ?? null)\n // Self-exclude by the underlying interaction id (raw.id): findEditorConflictItems\n // drops candidates by raw.id, and every expanded occurrence of a recurring series\n // shares it \u2014 so the edited record never conflicts with itself.\n const conflict = useConflictProbe(open, form, config, isEdit && item ? item.raw.id : null, draftOwnerUserId, conflictScope, currentUserId)\n\n const kindLabels = React.useMemo(\n () => ({\n meeting: t('customers.calendar.editor.types.meeting', 'meeting'),\n call: t('customers.calendar.editor.types.call', 'call'),\n email: t('customers.calendar.editor.types.email', 'email'),\n note: t('customers.calendar.editor.types.note', 'note'),\n event: t('customers.calendar.editor.types.event', 'event'),\n task: t('customers.calendar.editor.types.task', 'task'),\n }),\n [t],\n ) satisfies Record<EditorKind, string>\n\n const selectedType = form.category ?? form.kind\n const typeOptions = React.useMemo(\n () => buildEditorTypeOptions({ typeLabels, typeIcons, selectedValue: selectedType, kindLabels }),\n [typeLabels, typeIcons, selectedType, kindLabels],\n )\n const typeSwitcherOptions = React.useMemo(\n () =>\n typeOptions.map((option) => ({\n value: option.value,\n label: option.label,\n icon: renderDictionaryIcon(option.icon, 'h-4 w-4'),\n })),\n [typeOptions],\n )\n\n // Candidates for the Call \"insert phone from contact\" button: the linked\n // person (companies have no phone) + customer attendees. Their userId is the\n // person id, so the picker can resolve primary_phone from the people API.\n const phoneContactIds = React.useMemo(() => {\n const ids: string[] = []\n if (form.relatedTo && form.relatedTo.kind !== 'company') ids.push(form.relatedTo.id)\n for (const participant of form.participants) {\n if (participant.isCustomer && participant.userId) ids.push(participant.userId)\n }\n return Array.from(new Set(ids))\n }, [form.relatedTo, form.participants])\n\n const titleLabel = form.kind === 'email'\n ? t('customers.calendar.editor.titleLabel.email', 'Subject')\n : form.kind === 'note'\n ? t('customers.calendar.editor.titleLabel.note', 'Note')\n : t('customers.calendar.editor.titleLabel.generic', 'Title')\n\n return (\n // Single column on phones (full-screen sheet). On lg+ the dialog widens and\n // the fields group into two thematic columns: WHEN (schedule + repeat) on\n // the left, CONTEXT (related record, category, location) on the right;\n // people/resources and the task fields pair up below; title, description\n // and the type switcher span both columns.\n <div className=\"grid w-full grid-cols-1 items-start gap-4 lg:grid-cols-2 lg:gap-x-6\">\n {conflict ? (\n <Alert status=\"warning\" className=\"rounded-lg lg:col-span-2\">\n <AlertTitle>{t('customers.calendar.editor.conflictTitle', 'Calendar conflict')}</AlertTitle>\n <AlertDescription>{conflict}</AlertDescription>\n </Alert>\n ) : null}\n <div className=\"w-full lg:col-span-2\">\n <SegmentGroup<string>\n ariaLabel={t('customers.calendar.editor.typeSwitcher', 'Event type')}\n value={selectedType}\n onChange={(type) => update({ kind: editorKindOfInteractionType(type), category: type })}\n options={typeSwitcherOptions}\n />\n </div>\n <Field label={titleLabel} error={errors.title} className=\"lg:col-span-2\">\n <Input\n type=\"text\"\n value={form.title}\n onChange={(event) => update({ title: event.target.value })}\n placeholder={t('customers.calendar.editor.titlePlaceholder', 'Add a title\u2026')}\n aria-label={titleLabel}\n autoFocus\n size=\"lg\"\n />\n </Field>\n <div className=\"flex w-full flex-col gap-4\">\n <ScheduleSection\n dateLabel={config.dateLabel}\n hasAllDay={config.hasAllDay}\n hasEnd={config.hasEnd}\n allDay={form.allDay}\n date={form.date}\n startTime={form.startTime}\n endDate={form.endDate}\n endTime={form.endTime}\n endsError={errors.ends}\n onAllDayChange={(allDay) => update({ allDay })}\n onDateChange={(date) => {\n const untouchedDefault =\n JSON.stringify(form.repeatDays) === JSON.stringify(defaultRepeatDaysForDateInput(form.date))\n update(\n untouchedDefault\n ? { date, repeatDays: defaultRepeatDaysForDateInput(date) }\n : { date },\n )\n }}\n onStartTimeChange={(startTime) => update({ startTime })}\n onEndDateChange={(endDate) => update({ endDate })}\n onEndTimeChange={(endTime) => update({ endTime })}\n />\n {config.hasRepeat ? (\n <RepeatField\n freq={form.repeatFreq}\n days={form.repeatDays}\n endType={form.repeatEndType}\n count={form.repeatCount}\n untilDate={form.repeatUntilDate}\n onFreqChange={(repeatFreq) =>\n update(\n repeatFreq === 'weekly'\n ? { repeatFreq, repeatDays: defaultRepeatDaysForDateInput(form.date) }\n : { repeatFreq },\n )}\n onToggleDay={(index) =>\n update({ repeatDays: form.repeatDays.map((active, dayIndex) => (dayIndex === index ? !active : active)) })}\n onEndTypeChange={(repeatEndType) => update({ repeatEndType })}\n onCountChange={(repeatCount) => update({ repeatCount })}\n onUntilDateChange={(repeatUntilDate) => update({ repeatUntilDate })}\n />\n ) : null}\n {config.people && config.people !== 'assignee' ? (\n <Field label={t(PEOPLE_FIELD_TEXT[config.people].labelKey, PEOPLE_FIELD_TEXT[config.people].label)}>\n <PeopleField\n mode=\"multi\"\n includeCustomers\n includeStaff={staffEnabled}\n placeholder={t(PEOPLE_FIELD_TEXT[config.people].placeholderKey, PEOPLE_FIELD_TEXT[config.people].placeholder)}\n ariaLabel={t(PEOPLE_FIELD_TEXT[config.people].labelKey, PEOPLE_FIELD_TEXT[config.people].label)}\n value={form.participants}\n onChange={(participants) => update({ participants })}\n />\n </Field>\n ) : null}\n {config.people === 'assignee' && staffEnabled ? (\n <Field label={t('customers.calendar.editor.assignee', 'Assignee')} error={errors.assignee}>\n <PeopleField\n mode=\"single\"\n includeCustomers={false}\n includeStaff\n placeholder={t('customers.calendar.editor.assigneePlaceholder', 'Assign to a team member\u2026')}\n ariaLabel={t('customers.calendar.editor.assignee', 'Assignee')}\n value={form.assigneeUserId\n ? [{ userId: form.assigneeUserId, name: form.assigneeName ?? form.assigneeUserId, isCustomer: false }]\n : []}\n onChange={(entries) => {\n const next = entries[entries.length - 1] ?? null\n update({ assigneeUserId: next?.userId ?? null, assigneeName: next?.name ?? null })\n }}\n />\n </Field>\n ) : null}\n </div>\n <div className=\"flex w-full flex-col gap-4\">\n <Field label={t('customers.calendar.editor.relatedTo', 'Related to')} error={errors.relatedTo}>\n <RelatedToField\n label={t('customers.calendar.editor.relatedTo', 'Related to')}\n value={form.relatedTo}\n deal={form.dealId && form.dealLabel ? { id: form.dealId, label: form.dealLabel } : null}\n onChange={(relatedTo) => update({ relatedTo })}\n onDealChange={(deal) => update({ dealId: deal?.id ?? null, dealLabel: deal?.label ?? null })}\n error={errors.relatedTo}\n />\n </Field>\n {config.location ? (\n <LocationField\n variant={config.location}\n value={form.location}\n onChange={(location) => update({ location })}\n phoneContactIds={phoneContactIds}\n />\n ) : null}\n {resourcesEnabled ? (\n <Field label={t('customers.calendar.editor.resources', 'Resources')}>\n <ResourcesField\n placeholder={t('customers.calendar.editor.resourcesPlaceholder', 'Add a resource\u2026')}\n ariaLabel={t('customers.calendar.editor.resources', 'Resources')}\n value={form.resources}\n onChange={(resources) => update({ resources })}\n />\n </Field>\n ) : null}\n {config.hasPriority ? (\n <Field label={t('customers.calendar.editor.priority.label', 'Priority')}>\n <PriorityField\n value={form.priority}\n ariaLabel={t('customers.calendar.editor.priority.label', 'Priority')}\n labels={{\n low: t('customers.calendar.editor.priority.low', 'Low'),\n medium: t('customers.calendar.editor.priority.medium', 'Medium'),\n high: t('customers.calendar.editor.priority.high', 'High'),\n }}\n onChange={(priority) => update({ priority })}\n />\n </Field>\n ) : null}\n </div>\n <Field label={t('customers.calendar.editor.description', 'Description')} className=\"lg:col-span-2\">\n <Textarea\n value={form.description}\n onChange={(event) => update({ description: event.target.value })}\n placeholder={t('customers.calendar.editor.descriptionPlaceholder', 'Add details\u2026')}\n aria-label={t('customers.calendar.editor.description', 'Description')}\n className=\"h-20 resize-none\"\n />\n </Field>\n </div>\n )\n}\n\nexport function CalendarEventEditor({\n open,\n mode,\n item,\n defaultDate,\n defaultRange,\n typeLabels,\n typeIcons,\n conflictScope,\n currentUserId,\n resourcesEnabled,\n staffEnabled,\n onOpenChange,\n onSaved,\n}: CalendarEventEditorProps) {\n const t = useT()\n const [saving, setSaving] = React.useState(false)\n const isEdit = mode === 'edit' && Boolean(item?.id)\n\n // CrudForm reads initialValues once, so every dialog open gets a fresh form\n // instance keyed by the open sequence + edited record.\n const [openSeq, setOpenSeq] = React.useState(0)\n const wasOpenRef = React.useRef(false)\n React.useEffect(() => {\n if (open && !wasOpenRef.current) setOpenSeq((seq) => seq + 1)\n wasOpenRef.current = open\n }, [open])\n const formKey = `${mode}:${item?.id ?? 'new'}:${openSeq}`\n\n const initialValues = React.useMemo<Record<string, unknown>>(() => {\n if (isEdit && item) {\n return {\n ...parseItemToFormState(item),\n ...customFieldInitialValues(item),\n id: item.id,\n updatedAt: item.updatedAt ?? undefined,\n }\n }\n return { ...createDefaultFormState(defaultDate ?? null, undefined, defaultRange ?? null) }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- openSeq re-seeds defaults per dialog open\n }, [isEdit, item, defaultDate, defaultRange, openSeq])\n\n const handleSubmit = React.useCallback(\n async (values: Record<string, unknown>) => {\n const form = formStateOfValues(values)\n const config = KIND_CONFIG[form.kind]\n const fieldErrors: Record<string, string> = {}\n if (!form.title.trim()) {\n fieldErrors.title = t('customers.calendar.editor.validation.titleRequired', 'Title is required')\n }\n if (!form.relatedTo) {\n fieldErrors.relatedTo = t('customers.calendar.editor.validation.relatedToRequired', 'Select a person or company to link this event')\n }\n if (config.hasEnd && !form.allDay && computeDurationMinutes(form) === null) {\n fieldErrors.ends = t('customers.calendar.editor.validation.endsBeforeStarts', 'End must be after start')\n }\n // A task must be assigned to a team member \u2014 surface the requirement inline\n // instead of letting the save fail with no visible reason (#3747 feedback).\n if (config.people === 'assignee' && staffEnabled !== false && !form.assigneeUserId) {\n fieldErrors.assignee = t('customers.calendar.editor.validation.assigneeRequired', 'Assign this task to a team member')\n }\n if (Object.keys(fieldErrors).length > 0) {\n throw createCrudFormError(Object.values(fieldErrors)[0], fieldErrors)\n }\n setSaving(true)\n try {\n const payload = buildInteractionPayload(form, {\n mode,\n id: item?.id,\n resourcesEnabled: resourcesEnabled === true,\n staffEnabled: staffEnabled !== false,\n })\n const custom = collectCustomFieldValues(values, {\n transform: (value) => normalizeCustomFieldSubmitValue(value),\n })\n for (const [key, value] of Object.entries(custom)) payload[`cf_${key}`] = value\n // CrudForm supplies the optimistic-lock header (auto-derived from\n // initialValues.updatedAt) via scoped request headers around onSubmit.\n await apiCallOrThrow('/api/customers/interactions', {\n method: isEdit ? 'PUT' : 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n flash(t('customers.calendar.editor.saved', 'Event saved'), 'success')\n onOpenChange(false)\n requestAnimationFrame(() => { onSaved() })\n } catch (err) {\n // Surface an optimistic-lock 409 as the persistent conflict bar and\n // close \u2014 the bar renders at page level, behind the dialog overlay.\n if (extractOptimisticLockConflict(err)) {\n surfaceRecordConflict(err, t)\n onOpenChange(false)\n return\n }\n throw err\n } finally {\n setSaving(false)\n }\n },\n [isEdit, item?.id, mode, onOpenChange, onSaved, resourcesEnabled, staffEnabled, t],\n )\n\n const groups = React.useMemo<CrudFormGroup[]>(\n () => [\n {\n id: 'details',\n bare: true,\n component: (ctx) => (\n <EditorBody\n ctx={ctx}\n open={open}\n isEdit={isEdit}\n item={item}\n typeLabels={typeLabels}\n typeIcons={typeIcons ?? {}}\n conflictScope={conflictScope ?? 'all'}\n currentUserId={currentUserId ?? null}\n resourcesEnabled={resourcesEnabled === true}\n staffEnabled={staffEnabled !== false}\n />\n ),\n },\n { id: 'customFields', kind: 'customFields' },\n ],\n [open, isEdit, item, typeLabels, typeIcons, conflictScope, currentUserId, resourcesEnabled, staffEnabled],\n )\n\n const handleKeyDown = useDialogKeyHandler({\n onConfirm: () => {\n const formElement = document.getElementById(FORM_ID)\n if (formElement instanceof HTMLFormElement) formElement.requestSubmit()\n },\n disabled: saving,\n })\n\n const dialogTitle = isEdit ? t('customers.calendar.editor.title.edit', 'Edit event') : t('customers.calendar.editor.title.create', 'New event')\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent\n onKeyDown={handleKeyDown}\n // Only dismiss on a genuine click OUTSIDE the whole editor. Radix\n // relays a click inside the dialog (but outside an open DS\n // DatePicker/Select popover) up to the dialog as an \"interact\n // outside\" event when it dismisses that popover \u2014 without this guard\n // it would tear down the entire editor. Keep it open when the target\n // is inside the dialog body or inside any portalled popover.\n onInteractOutside={(event) => {\n const target = event.detail.originalEvent.target as HTMLElement | null\n if (target?.closest('[data-dialog-content]') || target?.closest('[data-radix-popper-content-wrapper]')) {\n event.preventDefault()\n }\n }}\n aria-describedby={undefined}\n dismissible={false}\n className=\"flex h-dvh max-h-dvh w-screen max-w-none flex-col gap-0 overflow-hidden rounded-none border-0 bg-background p-0 shadow-xl sm:h-auto sm:max-h-[calc(100dvh-4rem)] sm:w-full sm:max-w-lg sm:rounded-2xl sm:border-0 lg:max-w-3xl\"\n >\n <VisuallyHidden>\n <DialogTitle>{dialogTitle}</DialogTitle>\n </VisuallyHidden>\n <div className=\"flex shrink-0 items-center gap-3 border-b border-border bg-background py-4 pl-5 pr-4\">\n <Calendar aria-hidden className=\"size-6 shrink-0 text-foreground\" strokeWidth={1.75} />\n <p className=\"min-w-0 flex-1 text-sm font-medium leading-5 text-foreground\">{dialogTitle}</p>\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => onOpenChange(false)}\n aria-label={t('customers.calendar.editor.close', 'Close')}\n className=\"shrink-0 text-muted-foreground\"\n >\n <X aria-hidden className=\"size-5\" />\n </IconButton>\n </div>\n <div\n className=\"flex-1 overflow-y-auto\"\n onScroll={() => {\n // Tell the DS date/time fields to close their (controlled) popover\n // so a portalled popover doesn't float over the form or drift away\n // from its field while scrolling (#3747 feedback). Radix ignores\n // synthetic dismiss events, so the fields drive their own `open`.\n document.dispatchEvent(new CustomEvent(EDITOR_SCROLL_EVENT))\n }}\n >\n <div className=\"px-4 py-4 sm:px-6 sm:py-5\">\n <CrudForm<Record<string, unknown>>\n key={formKey}\n formId={FORM_ID}\n embedded\n hideFooterActions\n customFieldsManageMode=\"page\"\n fields={[]}\n groups={groups}\n initialValues={initialValues}\n entityIds={INTERACTION_ENTITY_IDS}\n onSubmit={handleSubmit}\n />\n </div>\n </div>\n <div className=\"flex shrink-0 items-center justify-end gap-3 border-t border-border bg-background px-5 py-4\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => onOpenChange(false)}\n >\n {t('customers.calendar.editor.cancel', 'Cancel')}\n </Button>\n <Button\n type=\"submit\"\n form={FORM_ID}\n disabled={saving}\n >\n {saving ? t('customers.calendar.editor.saving', 'Saving\u2026') : t('customers.calendar.editor.save', 'Save event')}\n </Button>\n </div>\n </DialogContent>\n </Dialog>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
SelectTrigger,
|
|
18
18
|
SelectValue
|
|
19
19
|
} from "@open-mercato/ui/primitives/select";
|
|
20
|
+
import { resolveDateFnsLocale } from "@open-mercato/ui/primitives/date-locale";
|
|
20
21
|
import { useLocale, useT } from "@open-mercato/shared/lib/i18n/context";
|
|
21
22
|
import { formatDateRangeLabel } from "../../lib/calendar/format.js";
|
|
22
23
|
const RANGE_PRESETS = ["thisWeek", "next7", "thisMonth", "next30"];
|
|
@@ -41,6 +42,7 @@ function CalendarToolbar(props) {
|
|
|
41
42
|
} = props;
|
|
42
43
|
const t = useT();
|
|
43
44
|
const locale = useLocale();
|
|
45
|
+
const dateFnsLocale = resolveDateFnsLocale(locale);
|
|
44
46
|
const [rangeOpen, setRangeOpen] = React.useState(false);
|
|
45
47
|
const [filtersOpen, setFiltersOpen] = React.useState(false);
|
|
46
48
|
const [pendingFilters, setPendingFilters] = React.useState(filters);
|
|
@@ -116,6 +118,7 @@ function CalendarToolbar(props) {
|
|
|
116
118
|
mode: "single",
|
|
117
119
|
selected: anchor,
|
|
118
120
|
defaultMonth: anchor,
|
|
121
|
+
locale: dateFnsLocale,
|
|
119
122
|
onSelect: (date) => {
|
|
120
123
|
if (!date) return;
|
|
121
124
|
onAnchorChange(date);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/customers/components/calendar/CalendarToolbar.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { CalendarRange, ListFilter, Settings } from 'lucide-react'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Calendar } from '@open-mercato/ui/primitives/calendar'\nimport { CheckboxField } from '@open-mercato/ui/primitives/checkbox-field'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Kbd } from '@open-mercato/ui/primitives/kbd'\nimport { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'\nimport { SearchInput } from '@open-mercato/ui/primitives/search-input'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'\nimport { formatDateRangeLabel } from '../../lib/calendar/format'\nimport type {\n CalendarFiltersValue,\n CalendarRangePreset,\n CalendarToolbarProps,\n} from './types'\n\nconst RANGE_PRESETS: CalendarRangePreset[] = ['thisWeek', 'next7', 'thisMonth', 'next30']\n\nconst STATUS_OPTIONS = ['planned', 'done', 'canceled'] as const\n\nconst ALL_OPTION = 'all'\n\nconst EMPTY_FILTERS: CalendarFiltersValue = { types: [], status: null, ownerUserId: null }\n\nexport function CalendarToolbar(props: CalendarToolbarProps) {\n const {\n anchor,\n range,\n preset,\n search,\n filters,\n typeOptions,\n ownerOptions,\n onToday,\n onPresetChange,\n onAnchorChange,\n onSearchChange,\n onFiltersChange,\n onOpenSettings,\n } = props\n const t = useT()\n const locale = useLocale()\n const [rangeOpen, setRangeOpen] = React.useState(false)\n const [filtersOpen, setFiltersOpen] = React.useState(false)\n const [pendingFilters, setPendingFilters] = React.useState<CalendarFiltersValue>(filters)\n\n const presetLabels: Record<CalendarRangePreset, string> = {\n thisWeek: t('customers.calendar.toolbar.presets.thisWeek', 'This week'),\n next7: t('customers.calendar.toolbar.presets.next7', 'Next 7 days'),\n thisMonth: t('customers.calendar.toolbar.presets.thisMonth', 'This month'),\n next30: t('customers.calendar.toolbar.presets.next30', 'Next 30 days'),\n }\n\n const activeFilterCount =\n filters.types.length + (filters.status ? 1 : 0) + (filters.ownerUserId ? 1 : 0)\n\n const handleFiltersOpenChange = (open: boolean) => {\n if (open) setPendingFilters(filters)\n setFiltersOpen(open)\n }\n\n const applyFilters = () => {\n onFiltersChange(pendingFilters)\n setFiltersOpen(false)\n }\n\n const clearFilters = () => {\n setPendingFilters(EMPTY_FILTERS)\n onFiltersChange(EMPTY_FILTERS)\n setFiltersOpen(false)\n }\n\n const togglePendingType = (value: string, checked: boolean) => {\n setPendingFilters((current) => ({\n ...current,\n types: checked\n ? [...current.types, value]\n : current.types.filter((entry) => entry !== value),\n }))\n }\n\n return (\n <div className=\"flex flex-wrap items-center gap-2 sm:gap-3\">\n <div className=\"flex min-w-0 flex-wrap items-center gap-2 sm:gap-3\">\n <Button type=\"button\" variant=\"outline\" onClick={onToday}>\n {t('customers.calendar.toolbar.today', 'Today')}\n </Button>\n <div className=\"flex min-w-0 items-center\">\n <Select\n value={preset ?? ''}\n onValueChange={(value) => onPresetChange(value as CalendarRangePreset)}\n >\n <SelectTrigger\n className=\"hidden w-auto min-w-32 rounded-r-none sm:flex\"\n aria-label={t('customers.calendar.toolbar.presetLabel', 'Date range preset')}\n >\n <SelectValue\n placeholder={t('customers.calendar.toolbar.presetPlaceholder', 'Custom range')}\n />\n </SelectTrigger>\n <SelectContent>\n {RANGE_PRESETS.map((value) => (\n <SelectItem key={value} value={value}>\n {presetLabels[value]}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Popover open={rangeOpen} onOpenChange={setRangeOpen}>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n className=\"min-w-0 text-muted-foreground sm:-ml-px sm:rounded-l-none\"\n >\n <CalendarRange aria-hidden=\"true\" />\n <span className=\"truncate\">{formatDateRangeLabel(locale, range.from, range.to)}</span>\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-auto min-w-0 p-2\">\n <Calendar\n mode=\"single\"\n selected={anchor}\n defaultMonth={anchor}\n onSelect={(date) => {\n if (!date) return\n onAnchorChange(date)\n setRangeOpen(false)\n }}\n />\n </PopoverContent>\n </Popover>\n </div>\n </div>\n <div className=\"flex w-full min-w-0 items-center gap-2 sm:ml-auto sm:w-auto sm:flex-none sm:gap-3\">\n <div className=\"relative min-w-0 flex-1 sm:w-72 sm:flex-none\">\n <SearchInput\n value={search}\n onChange={onSearchChange}\n placeholder={t('customers.calendar.toolbar.searchPlaceholder', 'Search\u2026')}\n aria-label={t('customers.calendar.toolbar.searchPlaceholder', 'Search\u2026')}\n data-calendar-search=\"\"\n />\n {search.length === 0 ? (\n <Kbd className=\"pointer-events-none absolute right-2 top-1/2 hidden -translate-y-1/2 sm:inline-flex\">/</Kbd>\n ) : null}\n </div>\n <Popover open={filtersOpen} onOpenChange={handleFiltersOpenChange}>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n aria-label={t('customers.calendar.toolbar.filters.label', 'Filter')}\n >\n <ListFilter aria-hidden=\"true\" />\n <span className=\"hidden sm:inline\">\n {t('customers.calendar.toolbar.filters.label', 'Filter')}\n </span>\n {activeFilterCount > 0 ? (\n <Badge variant=\"secondary\" size=\"sm\">\n {activeFilterCount}\n </Badge>\n ) : null}\n </Button>\n </PopoverTrigger>\n <PopoverContent\n align=\"end\"\n className=\"w-72 p-3\"\n onKeyDown={(event) => {\n if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n applyFilters()\n }\n }}\n >\n <div className=\"flex flex-col gap-4\">\n {typeOptions.length > 0 ? (\n <fieldset className=\"flex flex-col gap-2\">\n <legend className=\"pb-1 text-overline font-semibold uppercase tracking-widest text-muted-foreground\">\n {t('customers.calendar.toolbar.filters.types', 'Type')}\n </legend>\n {typeOptions.map((option) => (\n <CheckboxField\n key={option.value}\n label={option.label}\n size=\"sm\"\n checked={pendingFilters.types.includes(option.value)}\n onCheckedChange={(checked) =>\n togglePendingType(option.value, checked === true)\n }\n />\n ))}\n </fieldset>\n ) : null}\n <div className=\"flex flex-col gap-1.5\">\n <span className=\"text-overline font-semibold uppercase tracking-widest text-muted-foreground\">\n {t('customers.calendar.toolbar.filters.status', 'Status')}\n </span>\n <Select\n value={pendingFilters.status ?? ALL_OPTION}\n onValueChange={(value) =>\n setPendingFilters((current) => ({\n ...current,\n status: value === ALL_OPTION ? null : value,\n }))\n }\n >\n <SelectTrigger\n size=\"sm\"\n aria-label={t('customers.calendar.toolbar.filters.status', 'Status')}\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ALL_OPTION}>\n {t('customers.calendar.toolbar.filters.allStatuses', 'All statuses')}\n </SelectItem>\n {STATUS_OPTIONS.map((status) => (\n <SelectItem key={status} value={status}>\n {t(`customers.calendar.toolbar.filters.statuses.${status}`, status)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n {ownerOptions.length > 0 ? (\n <div className=\"flex flex-col gap-1.5\">\n <span className=\"text-overline font-semibold uppercase tracking-widest text-muted-foreground\">\n {t('customers.calendar.toolbar.filters.owner', 'Owner')}\n </span>\n <Select\n value={pendingFilters.ownerUserId ?? ALL_OPTION}\n onValueChange={(value) =>\n setPendingFilters((current) => ({\n ...current,\n ownerUserId: value === ALL_OPTION ? null : value,\n }))\n }\n >\n <SelectTrigger\n size=\"sm\"\n aria-label={t('customers.calendar.toolbar.filters.owner', 'Owner')}\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ALL_OPTION}>\n {t('customers.calendar.toolbar.filters.allOwners', 'All owners')}\n </SelectItem>\n {ownerOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n <div className=\"flex items-center justify-end gap-2 border-t pt-3\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={clearFilters}>\n {t('customers.calendar.toolbar.filters.clear', 'Clear')}\n </Button>\n <Button type=\"button\" size=\"sm\" onClick={applyFilters}>\n {t('customers.calendar.toolbar.filters.apply', 'Apply')}\n </Button>\n </div>\n </div>\n </PopoverContent>\n </Popover>\n <IconButton\n type=\"button\"\n variant=\"outline\"\n aria-label={t('customers.calendar.toolbar.settings', 'Calendar settings')}\n onClick={onOpenSettings}\n >\n <Settings aria-hidden />\n </IconButton>\n </div>\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { CalendarRange, ListFilter, Settings } from 'lucide-react'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Calendar } from '@open-mercato/ui/primitives/calendar'\nimport { CheckboxField } from '@open-mercato/ui/primitives/checkbox-field'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Kbd } from '@open-mercato/ui/primitives/kbd'\nimport { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'\nimport { SearchInput } from '@open-mercato/ui/primitives/search-input'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { resolveDateFnsLocale } from '@open-mercato/ui/primitives/date-locale'\nimport { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'\nimport { formatDateRangeLabel } from '../../lib/calendar/format'\nimport type {\n CalendarFiltersValue,\n CalendarRangePreset,\n CalendarToolbarProps,\n} from './types'\n\nconst RANGE_PRESETS: CalendarRangePreset[] = ['thisWeek', 'next7', 'thisMonth', 'next30']\n\nconst STATUS_OPTIONS = ['planned', 'done', 'canceled'] as const\n\nconst ALL_OPTION = 'all'\n\nconst EMPTY_FILTERS: CalendarFiltersValue = { types: [], status: null, ownerUserId: null }\n\nexport function CalendarToolbar(props: CalendarToolbarProps) {\n const {\n anchor,\n range,\n preset,\n search,\n filters,\n typeOptions,\n ownerOptions,\n onToday,\n onPresetChange,\n onAnchorChange,\n onSearchChange,\n onFiltersChange,\n onOpenSettings,\n } = props\n const t = useT()\n const locale = useLocale()\n const dateFnsLocale = resolveDateFnsLocale(locale)\n const [rangeOpen, setRangeOpen] = React.useState(false)\n const [filtersOpen, setFiltersOpen] = React.useState(false)\n const [pendingFilters, setPendingFilters] = React.useState<CalendarFiltersValue>(filters)\n\n const presetLabels: Record<CalendarRangePreset, string> = {\n thisWeek: t('customers.calendar.toolbar.presets.thisWeek', 'This week'),\n next7: t('customers.calendar.toolbar.presets.next7', 'Next 7 days'),\n thisMonth: t('customers.calendar.toolbar.presets.thisMonth', 'This month'),\n next30: t('customers.calendar.toolbar.presets.next30', 'Next 30 days'),\n }\n\n const activeFilterCount =\n filters.types.length + (filters.status ? 1 : 0) + (filters.ownerUserId ? 1 : 0)\n\n const handleFiltersOpenChange = (open: boolean) => {\n if (open) setPendingFilters(filters)\n setFiltersOpen(open)\n }\n\n const applyFilters = () => {\n onFiltersChange(pendingFilters)\n setFiltersOpen(false)\n }\n\n const clearFilters = () => {\n setPendingFilters(EMPTY_FILTERS)\n onFiltersChange(EMPTY_FILTERS)\n setFiltersOpen(false)\n }\n\n const togglePendingType = (value: string, checked: boolean) => {\n setPendingFilters((current) => ({\n ...current,\n types: checked\n ? [...current.types, value]\n : current.types.filter((entry) => entry !== value),\n }))\n }\n\n return (\n <div className=\"flex flex-wrap items-center gap-2 sm:gap-3\">\n <div className=\"flex min-w-0 flex-wrap items-center gap-2 sm:gap-3\">\n <Button type=\"button\" variant=\"outline\" onClick={onToday}>\n {t('customers.calendar.toolbar.today', 'Today')}\n </Button>\n <div className=\"flex min-w-0 items-center\">\n <Select\n value={preset ?? ''}\n onValueChange={(value) => onPresetChange(value as CalendarRangePreset)}\n >\n <SelectTrigger\n className=\"hidden w-auto min-w-32 rounded-r-none sm:flex\"\n aria-label={t('customers.calendar.toolbar.presetLabel', 'Date range preset')}\n >\n <SelectValue\n placeholder={t('customers.calendar.toolbar.presetPlaceholder', 'Custom range')}\n />\n </SelectTrigger>\n <SelectContent>\n {RANGE_PRESETS.map((value) => (\n <SelectItem key={value} value={value}>\n {presetLabels[value]}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Popover open={rangeOpen} onOpenChange={setRangeOpen}>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n className=\"min-w-0 text-muted-foreground sm:-ml-px sm:rounded-l-none\"\n >\n <CalendarRange aria-hidden=\"true\" />\n <span className=\"truncate\">{formatDateRangeLabel(locale, range.from, range.to)}</span>\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-auto min-w-0 p-2\">\n <Calendar\n mode=\"single\"\n selected={anchor}\n defaultMonth={anchor}\n locale={dateFnsLocale}\n onSelect={(date) => {\n if (!date) return\n onAnchorChange(date)\n setRangeOpen(false)\n }}\n />\n </PopoverContent>\n </Popover>\n </div>\n </div>\n <div className=\"flex w-full min-w-0 items-center gap-2 sm:ml-auto sm:w-auto sm:flex-none sm:gap-3\">\n <div className=\"relative min-w-0 flex-1 sm:w-72 sm:flex-none\">\n <SearchInput\n value={search}\n onChange={onSearchChange}\n placeholder={t('customers.calendar.toolbar.searchPlaceholder', 'Search\u2026')}\n aria-label={t('customers.calendar.toolbar.searchPlaceholder', 'Search\u2026')}\n data-calendar-search=\"\"\n />\n {search.length === 0 ? (\n <Kbd className=\"pointer-events-none absolute right-2 top-1/2 hidden -translate-y-1/2 sm:inline-flex\">/</Kbd>\n ) : null}\n </div>\n <Popover open={filtersOpen} onOpenChange={handleFiltersOpenChange}>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n aria-label={t('customers.calendar.toolbar.filters.label', 'Filter')}\n >\n <ListFilter aria-hidden=\"true\" />\n <span className=\"hidden sm:inline\">\n {t('customers.calendar.toolbar.filters.label', 'Filter')}\n </span>\n {activeFilterCount > 0 ? (\n <Badge variant=\"secondary\" size=\"sm\">\n {activeFilterCount}\n </Badge>\n ) : null}\n </Button>\n </PopoverTrigger>\n <PopoverContent\n align=\"end\"\n className=\"w-72 p-3\"\n onKeyDown={(event) => {\n if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n applyFilters()\n }\n }}\n >\n <div className=\"flex flex-col gap-4\">\n {typeOptions.length > 0 ? (\n <fieldset className=\"flex flex-col gap-2\">\n <legend className=\"pb-1 text-overline font-semibold uppercase tracking-widest text-muted-foreground\">\n {t('customers.calendar.toolbar.filters.types', 'Type')}\n </legend>\n {typeOptions.map((option) => (\n <CheckboxField\n key={option.value}\n label={option.label}\n size=\"sm\"\n checked={pendingFilters.types.includes(option.value)}\n onCheckedChange={(checked) =>\n togglePendingType(option.value, checked === true)\n }\n />\n ))}\n </fieldset>\n ) : null}\n <div className=\"flex flex-col gap-1.5\">\n <span className=\"text-overline font-semibold uppercase tracking-widest text-muted-foreground\">\n {t('customers.calendar.toolbar.filters.status', 'Status')}\n </span>\n <Select\n value={pendingFilters.status ?? ALL_OPTION}\n onValueChange={(value) =>\n setPendingFilters((current) => ({\n ...current,\n status: value === ALL_OPTION ? null : value,\n }))\n }\n >\n <SelectTrigger\n size=\"sm\"\n aria-label={t('customers.calendar.toolbar.filters.status', 'Status')}\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ALL_OPTION}>\n {t('customers.calendar.toolbar.filters.allStatuses', 'All statuses')}\n </SelectItem>\n {STATUS_OPTIONS.map((status) => (\n <SelectItem key={status} value={status}>\n {t(`customers.calendar.toolbar.filters.statuses.${status}`, status)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n {ownerOptions.length > 0 ? (\n <div className=\"flex flex-col gap-1.5\">\n <span className=\"text-overline font-semibold uppercase tracking-widest text-muted-foreground\">\n {t('customers.calendar.toolbar.filters.owner', 'Owner')}\n </span>\n <Select\n value={pendingFilters.ownerUserId ?? ALL_OPTION}\n onValueChange={(value) =>\n setPendingFilters((current) => ({\n ...current,\n ownerUserId: value === ALL_OPTION ? null : value,\n }))\n }\n >\n <SelectTrigger\n size=\"sm\"\n aria-label={t('customers.calendar.toolbar.filters.owner', 'Owner')}\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ALL_OPTION}>\n {t('customers.calendar.toolbar.filters.allOwners', 'All owners')}\n </SelectItem>\n {ownerOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n <div className=\"flex items-center justify-end gap-2 border-t pt-3\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={clearFilters}>\n {t('customers.calendar.toolbar.filters.clear', 'Clear')}\n </Button>\n <Button type=\"button\" size=\"sm\" onClick={applyFilters}>\n {t('customers.calendar.toolbar.filters.apply', 'Apply')}\n </Button>\n </div>\n </div>\n </PopoverContent>\n </Popover>\n <IconButton\n type=\"button\"\n variant=\"outline\"\n aria-label={t('customers.calendar.toolbar.settings', 'Calendar settings')}\n onClick={onOpenSettings}\n >\n <Settings aria-hidden />\n </IconButton>\n </div>\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -22,7 +22,6 @@ function RepeatField({
|
|
|
22
22
|
endType,
|
|
23
23
|
count,
|
|
24
24
|
untilDate,
|
|
25
|
-
locale,
|
|
26
25
|
onFreqChange,
|
|
27
26
|
onToggleDay,
|
|
28
27
|
onEndTypeChange,
|
|
@@ -88,8 +87,7 @@ function RepeatField({
|
|
|
88
87
|
className: "w-44",
|
|
89
88
|
value: untilDate || "",
|
|
90
89
|
onChange: onUntilDateChange,
|
|
91
|
-
ariaLabel: t("customers.calendar.editor.repeat.onDate", "On date")
|
|
92
|
-
locale
|
|
90
|
+
ariaLabel: t("customers.calendar.editor.repeat.onDate", "On date")
|
|
93
91
|
}
|
|
94
92
|
) : null,
|
|
95
93
|
endType === "count" ? /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customers/components/calendar/editor/RepeatField.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 { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@open-mercato/ui/primitives/select'\nimport type { EditorRepeatEndType, EditorRepeatFreq } from '../../../lib/calendar/editorPayload'\nimport { DateControl, LABEL_CLASS } from './inputs'\nimport { SegmentGroup } from './SegmentGroup'\n\nconst DAY_LABEL_KEYS = [\n { letterKey: 'customers.calendar.editor.repeat.days.mon', letterFallback: 'M', ariaKey: 'customers.calendar.day.mon', ariaFallback: 'MON' },\n { letterKey: 'customers.calendar.editor.repeat.days.tue', letterFallback: 'T', ariaKey: 'customers.calendar.day.tue', ariaFallback: 'TUE' },\n { letterKey: 'customers.calendar.editor.repeat.days.wed', letterFallback: 'W', ariaKey: 'customers.calendar.day.wed', ariaFallback: 'WED' },\n { letterKey: 'customers.calendar.editor.repeat.days.thu', letterFallback: 'T', ariaKey: 'customers.calendar.day.thu', ariaFallback: 'THU' },\n { letterKey: 'customers.calendar.editor.repeat.days.fri', letterFallback: 'F', ariaKey: 'customers.calendar.day.fri', ariaFallback: 'FRI' },\n { letterKey: 'customers.calendar.editor.repeat.days.sat', letterFallback: 'S', ariaKey: 'customers.calendar.day.sat', ariaFallback: 'SAT' },\n { letterKey: 'customers.calendar.editor.repeat.days.sun', letterFallback: 'S', ariaKey: 'customers.calendar.day.sun', ariaFallback: 'SUN' },\n]\n\nexport function RepeatField({\n freq,\n days,\n endType,\n count,\n untilDate,\n
|
|
5
|
-
"mappings": ";
|
|
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 { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@open-mercato/ui/primitives/select'\nimport type { EditorRepeatEndType, EditorRepeatFreq } from '../../../lib/calendar/editorPayload'\nimport { DateControl, LABEL_CLASS } from './inputs'\nimport { SegmentGroup } from './SegmentGroup'\n\nconst DAY_LABEL_KEYS = [\n { letterKey: 'customers.calendar.editor.repeat.days.mon', letterFallback: 'M', ariaKey: 'customers.calendar.day.mon', ariaFallback: 'MON' },\n { letterKey: 'customers.calendar.editor.repeat.days.tue', letterFallback: 'T', ariaKey: 'customers.calendar.day.tue', ariaFallback: 'TUE' },\n { letterKey: 'customers.calendar.editor.repeat.days.wed', letterFallback: 'W', ariaKey: 'customers.calendar.day.wed', ariaFallback: 'WED' },\n { letterKey: 'customers.calendar.editor.repeat.days.thu', letterFallback: 'T', ariaKey: 'customers.calendar.day.thu', ariaFallback: 'THU' },\n { letterKey: 'customers.calendar.editor.repeat.days.fri', letterFallback: 'F', ariaKey: 'customers.calendar.day.fri', ariaFallback: 'FRI' },\n { letterKey: 'customers.calendar.editor.repeat.days.sat', letterFallback: 'S', ariaKey: 'customers.calendar.day.sat', ariaFallback: 'SAT' },\n { letterKey: 'customers.calendar.editor.repeat.days.sun', letterFallback: 'S', ariaKey: 'customers.calendar.day.sun', ariaFallback: 'SUN' },\n]\n\nexport function RepeatField({\n freq,\n days,\n endType,\n count,\n untilDate,\n onFreqChange,\n onToggleDay,\n onEndTypeChange,\n onCountChange,\n onUntilDateChange,\n}: {\n freq: EditorRepeatFreq\n days: boolean[]\n endType: EditorRepeatEndType\n count: number\n untilDate: string\n onFreqChange(next: EditorRepeatFreq): void\n onToggleDay(index: number): void\n onEndTypeChange(next: EditorRepeatEndType): void\n onCountChange(next: number): void\n onUntilDateChange(next: string): void\n}) {\n const t = useT()\n const freqLabel = t('customers.calendar.editor.repeat.label', 'Repeat')\n return (\n <div className=\"flex w-full flex-col gap-2.5\">\n <span className={LABEL_CLASS}>{freqLabel}</span>\n <Select\n value={freq}\n onValueChange={(value) => onFreqChange(value as EditorRepeatFreq)}\n >\n <SelectTrigger aria-label={freqLabel}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"none\">{t('customers.calendar.editor.repeat.freq.none', 'Does not repeat')}</SelectItem>\n <SelectItem value=\"daily\">{t('customers.calendar.editor.repeat.freq.daily', 'Daily')}</SelectItem>\n <SelectItem value=\"weekly\">{t('customers.calendar.editor.repeat.freq.weekly', 'Weekly')}</SelectItem>\n </SelectContent>\n </Select>\n {freq === 'weekly' ? (\n <div className=\"flex items-start gap-1.5\">\n {DAY_LABEL_KEYS.map((day, index) => {\n const isActive = Boolean(days[index])\n return (\n <Button\n key={day.ariaKey}\n type=\"button\"\n variant={isActive ? 'default' : 'outline'}\n aria-pressed={isActive}\n aria-label={t(day.ariaKey, day.ariaFallback)}\n onClick={() => onToggleDay(index)}\n className={cn(\n 'size-9 px-0 text-xs font-medium',\n !isActive && 'text-muted-foreground',\n )}\n >\n {t(day.letterKey, day.letterFallback)}\n </Button>\n )\n })}\n </div>\n ) : null}\n {freq !== 'none' ? (\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className={LABEL_CLASS}>{t('customers.calendar.editor.repeat.ends', 'Ends')}</span>\n <SegmentGroup\n ariaLabel={t('customers.calendar.editor.repeat.ends', 'Ends')}\n value={endType}\n onChange={onEndTypeChange}\n options={[\n { value: 'never', label: t('customers.calendar.editor.repeat.never', 'Never') },\n { value: 'date', label: t('customers.calendar.editor.repeat.onDate', 'On date') },\n { value: 'count', label: t('customers.calendar.editor.repeat.after', 'After') },\n ]}\n />\n {endType === 'date' ? (\n <DateControl\n className=\"w-44\"\n value={untilDate || ''}\n onChange={onUntilDateChange}\n ariaLabel={t('customers.calendar.editor.repeat.onDate', 'On date')}\n />\n ) : null}\n {endType === 'count' ? (\n <span className=\"flex items-center gap-2\">\n <Input\n type=\"number\"\n min={1}\n max={365}\n value={count}\n onChange={(event) => {\n const next = Number(event.target.value)\n if (Number.isInteger(next) && next >= 1) onCountChange(next)\n }}\n aria-label={t('customers.calendar.editor.repeat.after', 'After')}\n className=\"h-9 w-20\"\n />\n <span className={LABEL_CLASS}>{t('customers.calendar.editor.repeat.times', 'times')}</span>\n </span>\n ) : null}\n </div>\n ) : null}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -15,14 +15,13 @@ function DateTimeRow({
|
|
|
15
15
|
date,
|
|
16
16
|
time,
|
|
17
17
|
showTime,
|
|
18
|
-
locale,
|
|
19
18
|
onDateChange,
|
|
20
19
|
onTimeChange
|
|
21
20
|
}) {
|
|
22
21
|
return /* @__PURE__ */ jsxs("div", { className: "flex w-full items-end gap-2.5", children: [
|
|
23
22
|
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1.5", children: [
|
|
24
23
|
/* @__PURE__ */ jsx("span", { className: LABEL_CLASS, children: label }),
|
|
25
|
-
/* @__PURE__ */ jsx(DateControl, { value: date, onChange: onDateChange, ariaLabel: label
|
|
24
|
+
/* @__PURE__ */ jsx(DateControl, { value: date, onChange: onDateChange, ariaLabel: label })
|
|
26
25
|
] }),
|
|
27
26
|
showTime ? /* @__PURE__ */ jsx(TimeControl, { value: time, onChange: onTimeChange, ariaLabel: label }) : null
|
|
28
27
|
] });
|
|
@@ -36,7 +35,6 @@ function ScheduleSection({
|
|
|
36
35
|
startTime,
|
|
37
36
|
endDate,
|
|
38
37
|
endTime,
|
|
39
|
-
locale,
|
|
40
38
|
endsError,
|
|
41
39
|
onAllDayChange,
|
|
42
40
|
onDateChange,
|
|
@@ -59,7 +57,6 @@ function ScheduleSection({
|
|
|
59
57
|
date,
|
|
60
58
|
time: startTime,
|
|
61
59
|
showTime,
|
|
62
|
-
locale,
|
|
63
60
|
onDateChange,
|
|
64
61
|
onTimeChange: onStartTimeChange
|
|
65
62
|
}
|
|
@@ -71,7 +68,6 @@ function ScheduleSection({
|
|
|
71
68
|
date: endDate,
|
|
72
69
|
time: endTime,
|
|
73
70
|
showTime,
|
|
74
|
-
locale,
|
|
75
71
|
onDateChange: onEndDateChange,
|
|
76
72
|
onTimeChange: onEndTimeChange
|
|
77
73
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customers/components/calendar/editor/ScheduleSection.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { EditorDateLabel } from '../../../lib/calendar/editorPayload'\nimport { multiDayEventSpan } from '../../../lib/calendar/labels'\nimport { AllDayToggle, DateControl, LABEL_CLASS, TimeControl } from './inputs'\n\nconst DATE_LABEL_TEXT: Record<EditorDateLabel, { key: string; fallback: string }> = {\n starts: { key: 'customers.calendar.editor.dates.starts', fallback: 'Starts' },\n when: { key: 'customers.calendar.editor.dates.when', fallback: 'When' },\n sent: { key: 'customers.calendar.editor.dates.sent', fallback: 'Sent' },\n logged: { key: 'customers.calendar.editor.dates.logged', fallback: 'Logged' },\n due: { key: 'customers.calendar.editor.dates.due', fallback: 'Due' },\n}\n\nfunction DateTimeRow({\n label,\n date,\n time,\n showTime,\n
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { EditorDateLabel } from '../../../lib/calendar/editorPayload'\nimport { multiDayEventSpan } from '../../../lib/calendar/labels'\nimport { AllDayToggle, DateControl, LABEL_CLASS, TimeControl } from './inputs'\n\nconst DATE_LABEL_TEXT: Record<EditorDateLabel, { key: string; fallback: string }> = {\n starts: { key: 'customers.calendar.editor.dates.starts', fallback: 'Starts' },\n when: { key: 'customers.calendar.editor.dates.when', fallback: 'When' },\n sent: { key: 'customers.calendar.editor.dates.sent', fallback: 'Sent' },\n logged: { key: 'customers.calendar.editor.dates.logged', fallback: 'Logged' },\n due: { key: 'customers.calendar.editor.dates.due', fallback: 'Due' },\n}\n\nfunction DateTimeRow({\n label,\n date,\n time,\n showTime,\n onDateChange,\n onTimeChange,\n}: {\n label: string\n date: string\n time: string\n showTime: boolean\n onDateChange(next: string): void\n onTimeChange(next: string): void\n}) {\n return (\n <div className=\"flex w-full items-end gap-2.5\">\n <div className=\"flex min-w-0 flex-1 flex-col gap-1.5\">\n <span className={LABEL_CLASS}>{label}</span>\n <DateControl value={date} onChange={onDateChange} ariaLabel={label} />\n </div>\n {showTime ? <TimeControl value={time} onChange={onTimeChange} ariaLabel={label} /> : null}\n </div>\n )\n}\n\nexport function ScheduleSection({\n dateLabel,\n hasAllDay,\n hasEnd,\n allDay,\n date,\n startTime,\n endDate,\n endTime,\n endsError,\n onAllDayChange,\n onDateChange,\n onStartTimeChange,\n onEndDateChange,\n onEndTimeChange,\n}: {\n dateLabel: EditorDateLabel\n hasAllDay: boolean\n hasEnd: boolean\n allDay: boolean\n date: string\n startTime: string\n endDate: string\n endTime: string\n endsError?: string | null\n onAllDayChange(next: boolean): void\n onDateChange(next: string): void\n onStartTimeChange(next: string): void\n onEndDateChange(next: string): void\n onEndTimeChange(next: string): void\n}) {\n const t = useT()\n const showTime = !(hasAllDay && allDay)\n const multiDaySpan = hasEnd && !endsError ? multiDayEventSpan(date, endDate) : 0\n return (\n <div className=\"flex w-full flex-col gap-2.5\">\n {hasAllDay ? (\n <div className=\"flex w-full items-center justify-between\">\n <span className={LABEL_CLASS}>{t('customers.calendar.editor.allDay', 'All day')}</span>\n <AllDayToggle checked={allDay} onCheckedChange={onAllDayChange} label={t('customers.calendar.editor.allDay', 'All day')} />\n </div>\n ) : null}\n <DateTimeRow\n label={t(DATE_LABEL_TEXT[dateLabel].key, DATE_LABEL_TEXT[dateLabel].fallback)}\n date={date}\n time={startTime}\n showTime={showTime}\n onDateChange={onDateChange}\n onTimeChange={onStartTimeChange}\n />\n {hasEnd ? (\n <DateTimeRow\n label={t('customers.calendar.editor.dates.ends', 'Ends')}\n date={endDate}\n time={endTime}\n showTime={showTime}\n onDateChange={onEndDateChange}\n onTimeChange={onEndTimeChange}\n />\n ) : null}\n {endsError ? <p className=\"text-xs text-status-error-text\">{endsError}</p> : null}\n {multiDaySpan > 0 ? (\n <p className=\"text-xs text-muted-foreground\">\n {t('customers.calendar.editor.multiDayHint', 'Multi-day event \u00B7 {count} days', { count: multiDaySpan })}\n </p>\n ) : null}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": ";AAiCM,SACE,KADF;AA9BN,SAAS,YAAY;AAErB,SAAS,yBAAyB;AAClC,SAAS,cAAc,aAAa,aAAa,mBAAmB;AAEpE,MAAM,kBAA8E;AAAA,EAClF,QAAQ,EAAE,KAAK,0CAA0C,UAAU,SAAS;AAAA,EAC5E,MAAM,EAAE,KAAK,wCAAwC,UAAU,OAAO;AAAA,EACtE,MAAM,EAAE,KAAK,wCAAwC,UAAU,OAAO;AAAA,EACtE,QAAQ,EAAE,KAAK,0CAA0C,UAAU,SAAS;AAAA,EAC5E,KAAK,EAAE,KAAK,uCAAuC,UAAU,MAAM;AACrE;AAEA,SAAS,YAAY;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOG;AACD,SACE,qBAAC,SAAI,WAAU,iCACb;AAAA,yBAAC,SAAI,WAAU,wCACb;AAAA,0BAAC,UAAK,WAAW,aAAc,iBAAM;AAAA,MACrC,oBAAC,eAAY,OAAO,MAAM,UAAU,cAAc,WAAW,OAAO;AAAA,OACtE;AAAA,IACC,WAAW,oBAAC,eAAY,OAAO,MAAM,UAAU,cAAc,WAAW,OAAO,IAAK;AAAA,KACvF;AAEJ;AAEO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAeG;AACD,QAAM,IAAI,KAAK;AACf,QAAM,WAAW,EAAE,aAAa;AAChC,QAAM,eAAe,UAAU,CAAC,YAAY,kBAAkB,MAAM,OAAO,IAAI;AAC/E,SACE,qBAAC,SAAI,WAAU,gCACZ;AAAA,gBACC,qBAAC,SAAI,WAAU,4CACb;AAAA,0BAAC,UAAK,WAAW,aAAc,YAAE,oCAAoC,SAAS,GAAE;AAAA,MAChF,oBAAC,gBAAa,SAAS,QAAQ,iBAAiB,gBAAgB,OAAO,EAAE,oCAAoC,SAAS,GAAG;AAAA,OAC3H,IACE;AAAA,IACJ;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,gBAAgB,SAAS,EAAE,KAAK,gBAAgB,SAAS,EAAE,QAAQ;AAAA,QAC5E;AAAA,QACA,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA,cAAc;AAAA;AAAA,IAChB;AAAA,IACC,SACC;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,wCAAwC,MAAM;AAAA,QACvD,MAAM;AAAA,QACN,MAAM;AAAA,QACN;AAAA,QACA,cAAc;AAAA,QACd,cAAc;AAAA;AAAA,IAChB,IACE;AAAA,IACH,YAAY,oBAAC,OAAE,WAAU,kCAAkC,qBAAU,IAAO;AAAA,IAC5E,eAAe,IACd,oBAAC,OAAE,WAAU,iCACV,YAAE,0CAA0C,qCAAkC,EAAE,OAAO,aAAa,CAAC,GACxG,IACE;AAAA,KACN;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customers/components/calendar/editor/inputs.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Plus } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { Avatar } from '@open-mercato/ui/primitives/avatar'\nimport { DatePicker } from '@open-mercato/ui/primitives/date-picker'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@open-mercato/ui/primitives/select'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\n\nexport const CONTROL_BORDER = 'border border-input'\nexport const CONTROL_TEXT = 'text-sm text-foreground placeholder:text-muted-foreground'\nexport const LABEL_CLASS = 'text-xs font-medium text-muted-foreground'\nexport const DROPDOWN_PANEL_CLASS =\n 'absolute z-50 mt-1 max-h-56 w-full overflow-y-auto rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md'\n\n/**\n * Closes an editor dropdown on pointer-down outside the component or Escape.\n * Blur alone is unreliable inside the Radix dialog focus trap (#3552 feedback:\n * the Resources list stayed open), so dismissal listens on the document while\n * the dropdown is open. Returns the ref to attach to the component root.\n */\nexport function useDropdownDismiss(open: boolean, onClose: () => void): React.RefObject<HTMLDivElement | null> {\n const rootRef = React.useRef<HTMLDivElement | null>(null)\n React.useEffect(() => {\n if (!open) return\n const handlePointerDown = (event: PointerEvent) => {\n if (rootRef.current && event.target instanceof Node && !rootRef.current.contains(event.target)) onClose()\n }\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n // Swallow Escape so the dialog itself stays open.\n event.stopPropagation()\n onClose()\n }\n }\n document.addEventListener('pointerdown', handlePointerDown, true)\n document.addEventListener('keydown', handleKeyDown, true)\n return () => {\n document.removeEventListener('pointerdown', handlePointerDown, true)\n document.removeEventListener('keydown', handleKeyDown, true)\n }\n }, [open, onClose])\n return rootRef\n}\n\nexport function Field({ label, children, error, className }: { label: string; children: React.ReactNode; error?: string | null; className?: string }) {\n return (\n <div className={cn('flex w-full flex-col gap-1.5', className)}>\n <span className={LABEL_CLASS}>{label}</span>\n {children}\n {error ? <p className=\"text-xs text-status-error-text\">{error}</p> : null}\n </div>\n )\n}\n\nexport function UppercaseBadge({ style, className, children }: { style?: React.CSSProperties; className?: string; children: React.ReactNode }) {\n return (\n <span\n className={cn(\n 'inline-flex shrink-0 items-center justify-center rounded-full bg-muted px-2 py-0.5 text-overline font-medium uppercase text-muted-foreground',\n className,\n )}\n style={style}\n >\n {children}\n </span>\n )\n}\n\nexport function AllDayToggle({ checked, onCheckedChange, label }: { checked: boolean; onCheckedChange(next: boolean): void; label: string }) {\n return (\n <Switch\n checked={checked}\n aria-label={label}\n onCheckedChange={onCheckedChange}\n className=\"h-6 w-10\"\n />\n )\n}\n\nfunction parseDateValue(value: string): Date | null {\n if (!value) return null\n const parsed = new Date(`${value}T00:00:00`)\n return Number.isNaN(parsed.getTime()) ? null : parsed\n}\n\nfunction formatDateValue(date: Date): string {\n return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`\n}\n\n// Close the field's popover when the surrounding editor dialog scrolls, so a\n// portalled DS popover (DatePicker/Select) doesn't float over the form or drift\n// from its field (#3747 feedback). Radix ignores synthetic dismiss events, so\n// this drives the popover's controlled `open` state instead. The editor\n// dispatches `EDITOR_SCROLL_EVENT` on scroll.\nexport const EDITOR_SCROLL_EVENT = 'om-calendar-editor-scroll'\nfunction useCloseOnEditorScroll(setOpen: (open: boolean) => void) {\n React.useEffect(() => {\n const handler = () => setOpen(false)\n document.addEventListener(EDITOR_SCROLL_EVENT, handler)\n return () => document.removeEventListener(EDITOR_SCROLL_EVENT, handler)\n }, [setOpen])\n}\n\n// DS DatePicker (calendar popover) + DS Select (scrolling 30-min list) replace\n// the native `<input type=date|time>` overlays, which only opened via the\n// browser chevron and did not match the design system (#3747 feedback).\nexport function DateControl({\n value,\n onChange,\n ariaLabel,\n className,\n}: {\n value: string\n onChange(next: string): void\n ariaLabel: string\n
|
|
5
|
-
"mappings": ";AAiDI,SACE,KADF;AA/CJ,YAAY,WAAW;AACvB,SAAS,YAAY;AACrB,SAAS,UAAU;AACnB,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;AAC3B,SAAS,QAAQ,eAAe,YAAY,eAAe,mBAAmB;AAC9E,SAAS,cAAc;AAEhB,MAAM,iBAAiB;AACvB,MAAM,eAAe;AACrB,MAAM,cAAc;AACpB,MAAM,uBACX;AAQK,SAAS,mBAAmB,MAAe,SAA6D;AAC7G,QAAM,UAAU,MAAM,OAA8B,IAAI;AACxD,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,KAAM;AACX,UAAM,oBAAoB,CAAC,UAAwB;AACjD,UAAI,QAAQ,WAAW,MAAM,kBAAkB,QAAQ,CAAC,QAAQ,QAAQ,SAAS,MAAM,MAAM,EAAG,SAAQ;AAAA,IAC1G;AACA,UAAM,gBAAgB,CAAC,UAAyB;AAC9C,UAAI,MAAM,QAAQ,UAAU;AAE1B,cAAM,gBAAgB;AACtB,gBAAQ;AAAA,MACV;AAAA,IACF;AACA,aAAS,iBAAiB,eAAe,mBAAmB,IAAI;AAChE,aAAS,iBAAiB,WAAW,eAAe,IAAI;AACxD,WAAO,MAAM;AACX,eAAS,oBAAoB,eAAe,mBAAmB,IAAI;AACnE,eAAS,oBAAoB,WAAW,eAAe,IAAI;AAAA,IAC7D;AAAA,EACF,GAAG,CAAC,MAAM,OAAO,CAAC;AAClB,SAAO;AACT;AAEO,SAAS,MAAM,EAAE,OAAO,UAAU,OAAO,UAAU,GAA4F;AACpJ,SACE,qBAAC,SAAI,WAAW,GAAG,gCAAgC,SAAS,GAC1D;AAAA,wBAAC,UAAK,WAAW,aAAc,iBAAM;AAAA,IACpC;AAAA,IACA,QAAQ,oBAAC,OAAE,WAAU,kCAAkC,iBAAM,IAAO;AAAA,KACvE;AAEJ;AAEO,SAAS,eAAe,EAAE,OAAO,WAAW,SAAS,GAAmF;AAC7I,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACA;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;AAEO,SAAS,aAAa,EAAE,SAAS,iBAAiB,MAAM,GAA8E;AAC3I,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,cAAY;AAAA,MACZ;AAAA,MACA,WAAU;AAAA;AAAA,EACZ;AAEJ;AAEA,SAAS,eAAe,OAA4B;AAClD,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,SAAS,oBAAI,KAAK,GAAG,KAAK,WAAW;AAC3C,SAAO,OAAO,MAAM,OAAO,QAAQ,CAAC,IAAI,OAAO;AACjD;AAEA,SAAS,gBAAgB,MAAoB;AAC3C,SAAO,GAAG,KAAK,YAAY,CAAC,IAAI,OAAO,KAAK,SAAS,IAAI,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,OAAO,KAAK,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC;AACzH;AAOO,MAAM,sBAAsB;AACnC,SAAS,uBAAuB,SAAkC;AAChE,QAAM,UAAU,MAAM;AACpB,UAAM,UAAU,MAAM,QAAQ,KAAK;AACnC,aAAS,iBAAiB,qBAAqB,OAAO;AACtD,WAAO,MAAM,SAAS,oBAAoB,qBAAqB,OAAO;AAAA,EACxE,GAAG,CAAC,OAAO,CAAC;AACd;AAKO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Plus } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { Avatar } from '@open-mercato/ui/primitives/avatar'\nimport { DatePicker } from '@open-mercato/ui/primitives/date-picker'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@open-mercato/ui/primitives/select'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\n\nexport const CONTROL_BORDER = 'border border-input'\nexport const CONTROL_TEXT = 'text-sm text-foreground placeholder:text-muted-foreground'\nexport const LABEL_CLASS = 'text-xs font-medium text-muted-foreground'\nexport const DROPDOWN_PANEL_CLASS =\n 'absolute z-50 mt-1 max-h-56 w-full overflow-y-auto rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md'\n\n/**\n * Closes an editor dropdown on pointer-down outside the component or Escape.\n * Blur alone is unreliable inside the Radix dialog focus trap (#3552 feedback:\n * the Resources list stayed open), so dismissal listens on the document while\n * the dropdown is open. Returns the ref to attach to the component root.\n */\nexport function useDropdownDismiss(open: boolean, onClose: () => void): React.RefObject<HTMLDivElement | null> {\n const rootRef = React.useRef<HTMLDivElement | null>(null)\n React.useEffect(() => {\n if (!open) return\n const handlePointerDown = (event: PointerEvent) => {\n if (rootRef.current && event.target instanceof Node && !rootRef.current.contains(event.target)) onClose()\n }\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n // Swallow Escape so the dialog itself stays open.\n event.stopPropagation()\n onClose()\n }\n }\n document.addEventListener('pointerdown', handlePointerDown, true)\n document.addEventListener('keydown', handleKeyDown, true)\n return () => {\n document.removeEventListener('pointerdown', handlePointerDown, true)\n document.removeEventListener('keydown', handleKeyDown, true)\n }\n }, [open, onClose])\n return rootRef\n}\n\nexport function Field({ label, children, error, className }: { label: string; children: React.ReactNode; error?: string | null; className?: string }) {\n return (\n <div className={cn('flex w-full flex-col gap-1.5', className)}>\n <span className={LABEL_CLASS}>{label}</span>\n {children}\n {error ? <p className=\"text-xs text-status-error-text\">{error}</p> : null}\n </div>\n )\n}\n\nexport function UppercaseBadge({ style, className, children }: { style?: React.CSSProperties; className?: string; children: React.ReactNode }) {\n return (\n <span\n className={cn(\n 'inline-flex shrink-0 items-center justify-center rounded-full bg-muted px-2 py-0.5 text-overline font-medium uppercase text-muted-foreground',\n className,\n )}\n style={style}\n >\n {children}\n </span>\n )\n}\n\nexport function AllDayToggle({ checked, onCheckedChange, label }: { checked: boolean; onCheckedChange(next: boolean): void; label: string }) {\n return (\n <Switch\n checked={checked}\n aria-label={label}\n onCheckedChange={onCheckedChange}\n className=\"h-6 w-10\"\n />\n )\n}\n\nfunction parseDateValue(value: string): Date | null {\n if (!value) return null\n const parsed = new Date(`${value}T00:00:00`)\n return Number.isNaN(parsed.getTime()) ? null : parsed\n}\n\nfunction formatDateValue(date: Date): string {\n return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`\n}\n\n// Close the field's popover when the surrounding editor dialog scrolls, so a\n// portalled DS popover (DatePicker/Select) doesn't float over the form or drift\n// from its field (#3747 feedback). Radix ignores synthetic dismiss events, so\n// this drives the popover's controlled `open` state instead. The editor\n// dispatches `EDITOR_SCROLL_EVENT` on scroll.\nexport const EDITOR_SCROLL_EVENT = 'om-calendar-editor-scroll'\nfunction useCloseOnEditorScroll(setOpen: (open: boolean) => void) {\n React.useEffect(() => {\n const handler = () => setOpen(false)\n document.addEventListener(EDITOR_SCROLL_EVENT, handler)\n return () => document.removeEventListener(EDITOR_SCROLL_EVENT, handler)\n }, [setOpen])\n}\n\n// DS DatePicker (calendar popover) + DS Select (scrolling 30-min list) replace\n// the native `<input type=date|time>` overlays, which only opened via the\n// browser chevron and did not match the design system (#3747 feedback).\nexport function DateControl({\n value,\n onChange,\n ariaLabel,\n className,\n}: {\n value: string\n onChange(next: string): void\n ariaLabel: string\n className?: string\n}) {\n const [open, setOpen] = React.useState(false)\n useCloseOnEditorScroll(setOpen)\n return (\n <DatePicker\n value={parseDateValue(value)}\n onChange={(date) => { if (date) onChange(formatDateValue(date)) }}\n footer=\"none\"\n open={open}\n onOpenChange={setOpen}\n aria-label={ariaLabel}\n className={cn('w-full', className)}\n />\n )\n}\n\n// 24h times at 30-minute steps.\nconst TIME_OPTIONS: string[] = Array.from({ length: 48 }, (_, index) => {\n const hour = Math.floor(index / 2)\n const minute = index % 2 === 0 ? '00' : '30'\n return `${String(hour).padStart(2, '0')}:${minute}`\n})\n\nexport function TimeControl({ value, onChange, ariaLabel }: { value: string; onChange(next: string): void; ariaLabel: string }) {\n const [open, setOpen] = React.useState(false)\n useCloseOnEditorScroll(setOpen)\n // Keep an off-grid value (e.g. an imported 22:15) selectable.\n const options = value && !TIME_OPTIONS.includes(value) ? [value, ...TIME_OPTIONS] : TIME_OPTIONS\n return (\n <Select value={value} onValueChange={onChange} open={open} onOpenChange={setOpen}>\n <SelectTrigger aria-label={ariaLabel} className=\"h-9 w-32 shrink-0\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent className=\"max-h-64\">\n {options.map((time) => (\n <SelectItem key={time} value={time}>{time}</SelectItem>\n ))}\n </SelectContent>\n </Select>\n )\n}\n\nexport function PersonChip({\n name,\n badge,\n compact,\n onRemove,\n removeLabel,\n}: {\n name: string\n badge?: React.ReactNode\n compact?: boolean\n onRemove?: () => void\n removeLabel?: string\n}) {\n return (\n <span\n className={cn(\n 'inline-flex shrink-0 items-center gap-1.5 rounded-full bg-muted pl-1 pr-2',\n compact ? 'py-0.5' : 'py-1',\n )}\n >\n <Avatar size=\"xs\" label={name} />\n <span className=\"max-w-40 truncate text-xs font-medium text-foreground\">{name}</span>\n {badge}\n {onRemove ? (\n <IconButton\n variant=\"ghost\"\n size=\"xs\"\n onClick={(event) => { event.stopPropagation(); onRemove() }}\n aria-label={removeLabel}\n className=\"size-5 shrink-0\"\n >\n <Plus aria-hidden className=\"size-3.5 rotate-45 opacity-50\" />\n </IconButton>\n ) : null}\n </span>\n )\n}\n"],
|
|
5
|
+
"mappings": ";AAiDI,SACE,KADF;AA/CJ,YAAY,WAAW;AACvB,SAAS,YAAY;AACrB,SAAS,UAAU;AACnB,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;AAC3B,SAAS,QAAQ,eAAe,YAAY,eAAe,mBAAmB;AAC9E,SAAS,cAAc;AAEhB,MAAM,iBAAiB;AACvB,MAAM,eAAe;AACrB,MAAM,cAAc;AACpB,MAAM,uBACX;AAQK,SAAS,mBAAmB,MAAe,SAA6D;AAC7G,QAAM,UAAU,MAAM,OAA8B,IAAI;AACxD,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,KAAM;AACX,UAAM,oBAAoB,CAAC,UAAwB;AACjD,UAAI,QAAQ,WAAW,MAAM,kBAAkB,QAAQ,CAAC,QAAQ,QAAQ,SAAS,MAAM,MAAM,EAAG,SAAQ;AAAA,IAC1G;AACA,UAAM,gBAAgB,CAAC,UAAyB;AAC9C,UAAI,MAAM,QAAQ,UAAU;AAE1B,cAAM,gBAAgB;AACtB,gBAAQ;AAAA,MACV;AAAA,IACF;AACA,aAAS,iBAAiB,eAAe,mBAAmB,IAAI;AAChE,aAAS,iBAAiB,WAAW,eAAe,IAAI;AACxD,WAAO,MAAM;AACX,eAAS,oBAAoB,eAAe,mBAAmB,IAAI;AACnE,eAAS,oBAAoB,WAAW,eAAe,IAAI;AAAA,IAC7D;AAAA,EACF,GAAG,CAAC,MAAM,OAAO,CAAC;AAClB,SAAO;AACT;AAEO,SAAS,MAAM,EAAE,OAAO,UAAU,OAAO,UAAU,GAA4F;AACpJ,SACE,qBAAC,SAAI,WAAW,GAAG,gCAAgC,SAAS,GAC1D;AAAA,wBAAC,UAAK,WAAW,aAAc,iBAAM;AAAA,IACpC;AAAA,IACA,QAAQ,oBAAC,OAAE,WAAU,kCAAkC,iBAAM,IAAO;AAAA,KACvE;AAEJ;AAEO,SAAS,eAAe,EAAE,OAAO,WAAW,SAAS,GAAmF;AAC7I,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACA;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;AAEO,SAAS,aAAa,EAAE,SAAS,iBAAiB,MAAM,GAA8E;AAC3I,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,cAAY;AAAA,MACZ;AAAA,MACA,WAAU;AAAA;AAAA,EACZ;AAEJ;AAEA,SAAS,eAAe,OAA4B;AAClD,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,SAAS,oBAAI,KAAK,GAAG,KAAK,WAAW;AAC3C,SAAO,OAAO,MAAM,OAAO,QAAQ,CAAC,IAAI,OAAO;AACjD;AAEA,SAAS,gBAAgB,MAAoB;AAC3C,SAAO,GAAG,KAAK,YAAY,CAAC,IAAI,OAAO,KAAK,SAAS,IAAI,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,OAAO,KAAK,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC;AACzH;AAOO,MAAM,sBAAsB;AACnC,SAAS,uBAAuB,SAAkC;AAChE,QAAM,UAAU,MAAM;AACpB,UAAM,UAAU,MAAM,QAAQ,KAAK;AACnC,aAAS,iBAAiB,qBAAqB,OAAO;AACtD,WAAO,MAAM,SAAS,oBAAoB,qBAAqB,OAAO;AAAA,EACxE,GAAG,CAAC,OAAO,CAAC;AACd;AAKO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,KAAK;AAC5C,yBAAuB,OAAO;AAC9B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,eAAe,KAAK;AAAA,MAC3B,UAAU,CAAC,SAAS;AAAE,YAAI,KAAM,UAAS,gBAAgB,IAAI,CAAC;AAAA,MAAE;AAAA,MAChE,QAAO;AAAA,MACP;AAAA,MACA,cAAc;AAAA,MACd,cAAY;AAAA,MACZ,WAAW,GAAG,UAAU,SAAS;AAAA;AAAA,EACnC;AAEJ;AAGA,MAAM,eAAyB,MAAM,KAAK,EAAE,QAAQ,GAAG,GAAG,CAAC,GAAG,UAAU;AACtE,QAAM,OAAO,KAAK,MAAM,QAAQ,CAAC;AACjC,QAAM,SAAS,QAAQ,MAAM,IAAI,OAAO;AACxC,SAAO,GAAG,OAAO,IAAI,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,MAAM;AACnD,CAAC;AAEM,SAAS,YAAY,EAAE,OAAO,UAAU,UAAU,GAAuE;AAC9H,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,KAAK;AAC5C,yBAAuB,OAAO;AAE9B,QAAM,UAAU,SAAS,CAAC,aAAa,SAAS,KAAK,IAAI,CAAC,OAAO,GAAG,YAAY,IAAI;AACpF,SACE,qBAAC,UAAO,OAAc,eAAe,UAAU,MAAY,cAAc,SACvE;AAAA,wBAAC,iBAAc,cAAY,WAAW,WAAU,qBAC9C,8BAAC,eAAY,GACf;AAAA,IACA,oBAAC,iBAAc,WAAU,YACtB,kBAAQ,IAAI,CAAC,SACZ,oBAAC,cAAsB,OAAO,MAAO,kBAApB,IAAyB,CAC3C,GACH;AAAA,KACF;AAEJ;AAEO,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMG;AACD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,UAAU,WAAW;AAAA,MACvB;AAAA,MAEA;AAAA,4BAAC,UAAO,MAAK,MAAK,OAAO,MAAM;AAAA,QAC/B,oBAAC,UAAK,WAAU,yDAAyD,gBAAK;AAAA,QAC7E;AAAA,QACA,WACC;AAAA,UAAC;AAAA;AAAA,YACC,SAAQ;AAAA,YACR,MAAK;AAAA,YACL,SAAS,CAAC,UAAU;AAAE,oBAAM,gBAAgB;AAAG,uBAAS;AAAA,YAAE;AAAA,YAC1D,cAAY;AAAA,YACZ,WAAU;AAAA,YAEV,8BAAC,QAAK,eAAW,MAAC,WAAU,iCAAgC;AAAA;AAAA,QAC9D,IACE;AAAA;AAAA;AAAA,EACN;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -78,7 +78,7 @@ async function setRecordCustomFields(em, opts) {
|
|
|
78
78
|
const col = encrypted ? "valueText" : def ? columnFromKind(def.kind) : columnFromJsValue(val);
|
|
79
79
|
const cf2 = em.create(CustomFieldValue, { entityId, recordId, organizationId, tenantId, fieldKey, createdAt: /* @__PURE__ */ new Date() });
|
|
80
80
|
clearValueColumns(cf2);
|
|
81
|
-
const stored = encrypted ? await encryptCustomFieldValue(val, tenantId, getEncryptionService(), encryptionCache) : val;
|
|
81
|
+
const stored = encrypted ? await encryptCustomFieldValue(val, tenantId, getEncryptionService(), encryptionCache, { entityId, fieldKey }) : val;
|
|
82
82
|
switch (col) {
|
|
83
83
|
case "valueText":
|
|
84
84
|
cf2.valueText = stored == null ? null : String(stored);
|
|
@@ -104,7 +104,7 @@ async function setRecordCustomFields(em, opts) {
|
|
|
104
104
|
continue;
|
|
105
105
|
}
|
|
106
106
|
const column = encrypted ? "valueText" : def ? columnFromKind(def.kind) : columnFromJsValue(raw);
|
|
107
|
-
const storedValue = encrypted ? await encryptCustomFieldValue(raw, tenantId, getEncryptionService(), encryptionCache) : raw;
|
|
107
|
+
const storedValue = encrypted ? await encryptCustomFieldValue(raw, tenantId, getEncryptionService(), encryptionCache, { entityId, fieldKey }) : raw;
|
|
108
108
|
let cf = await em.findOne(CustomFieldValue, { entityId, recordId, organizationId, tenantId, fieldKey });
|
|
109
109
|
if (!cf) {
|
|
110
110
|
cf = em.create(CustomFieldValue, { entityId, recordId, organizationId, tenantId, fieldKey, createdAt: /* @__PURE__ */ new Date() });
|