@open-mercato/core 0.6.7-develop.6834.1.e76f4b8cbc → 0.6.7-develop.6851.1.c1680b00d8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/customers/components/detail/ActivitiesDayStrip.js +2 -3
  3. package/dist/modules/customers/components/detail/ActivitiesDayStrip.js.map +2 -2
  4. package/dist/modules/dashboards/lib/aggregations.js +20 -6
  5. package/dist/modules/dashboards/lib/aggregations.js.map +2 -2
  6. package/dist/modules/entities/api/definitions.js +2 -0
  7. package/dist/modules/entities/api/definitions.js.map +2 -2
  8. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +18 -3
  9. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  10. package/dist/modules/entities/components/RecordListRelationCell.js +63 -0
  11. package/dist/modules/entities/components/RecordListRelationCell.js.map +7 -0
  12. package/dist/modules/entities/components/recordListRelationDisplays.js +80 -0
  13. package/dist/modules/entities/components/recordListRelationDisplays.js.map +7 -0
  14. package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js +14 -1
  15. package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js.map +2 -2
  16. package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js +22 -17
  17. package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js.map +2 -2
  18. package/dist/modules/query_index/api/reindex.js +10 -0
  19. package/dist/modules/query_index/api/reindex.js.map +2 -2
  20. package/dist/modules/query_index/cli.js +125 -1
  21. package/dist/modules/query_index/cli.js.map +2 -2
  22. package/dist/modules/query_index/lib/coverage.js +19 -7
  23. package/dist/modules/query_index/lib/coverage.js.map +2 -2
  24. package/dist/modules/query_index/lib/reindexer.js +43 -37
  25. package/dist/modules/query_index/lib/reindexer.js.map +2 -2
  26. package/dist/modules/sales/commands/documents.js +27 -0
  27. package/dist/modules/sales/commands/documents.js.map +2 -2
  28. package/dist/modules/sales/data/validators.js +2 -0
  29. package/dist/modules/sales/data/validators.js.map +2 -2
  30. package/package.json +8 -8
  31. package/src/modules/api_keys/i18n/ko.json +42 -0
  32. package/src/modules/attachments/i18n/ko.json +172 -0
  33. package/src/modules/audit_logs/i18n/ko.json +66 -0
  34. package/src/modules/auth/i18n/ko.json +255 -0
  35. package/src/modules/business_rules/i18n/ko.json +396 -0
  36. package/src/modules/catalog/i18n/ko.json +723 -0
  37. package/src/modules/communication_channels/i18n/ko.json +138 -0
  38. package/src/modules/configs/i18n/ko.json +197 -0
  39. package/src/modules/currencies/i18n/de.json +1 -0
  40. package/src/modules/currencies/i18n/en.json +1 -0
  41. package/src/modules/currencies/i18n/es.json +1 -0
  42. package/src/modules/currencies/i18n/ko.json +160 -0
  43. package/src/modules/currencies/i18n/pl.json +1 -0
  44. package/src/modules/customer_accounts/i18n/ko.json +354 -0
  45. package/src/modules/customers/components/detail/ActivitiesDayStrip.tsx +2 -3
  46. package/src/modules/customers/i18n/de.json +10 -0
  47. package/src/modules/customers/i18n/en.json +10 -0
  48. package/src/modules/customers/i18n/es.json +10 -0
  49. package/src/modules/customers/i18n/ko.json +2855 -0
  50. package/src/modules/customers/i18n/pl.json +10 -0
  51. package/src/modules/dashboards/i18n/ko.json +118 -0
  52. package/src/modules/dashboards/lib/aggregations.ts +42 -6
  53. package/src/modules/data_sync/i18n/ko.json +125 -0
  54. package/src/modules/design_system/i18n/ko.json +58 -0
  55. package/src/modules/dictionaries/i18n/ko.json +130 -0
  56. package/src/modules/directory/i18n/ko.json +105 -0
  57. package/src/modules/entities/api/definitions.ts +4 -0
  58. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +94 -77
  59. package/src/modules/entities/components/RecordListRelationCell.tsx +90 -0
  60. package/src/modules/entities/components/recordListRelationDisplays.ts +109 -0
  61. package/src/modules/entities/i18n/ko.json +260 -0
  62. package/src/modules/feature_toggles/i18n/ko.json +96 -0
  63. package/src/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.ts +17 -1
  64. package/src/modules/inbox_ops/components/proposals/EditActionDialog.tsx +21 -16
  65. package/src/modules/inbox_ops/components/proposals/types.ts +1 -0
  66. package/src/modules/inbox_ops/i18n/ko.json +196 -0
  67. package/src/modules/integrations/i18n/ko.json +129 -0
  68. package/src/modules/messages/i18n/ko.json +263 -0
  69. package/src/modules/notifications/i18n/ko.json +55 -0
  70. package/src/modules/payment_gateways/i18n/ko.json +87 -0
  71. package/src/modules/planner/i18n/ko.json +565 -0
  72. package/src/modules/portal/i18n/ko.json +143 -0
  73. package/src/modules/progress/i18n/ko.json +20 -0
  74. package/src/modules/query_index/api/reindex.ts +15 -0
  75. package/src/modules/query_index/cli.ts +180 -1
  76. package/src/modules/query_index/i18n/ko.json +69 -0
  77. package/src/modules/query_index/lib/coverage.ts +38 -8
  78. package/src/modules/query_index/lib/reindexer.ts +62 -40
  79. package/src/modules/resources/i18n/ko.json +770 -0
  80. package/src/modules/sales/commands/documents.ts +49 -0
  81. package/src/modules/sales/data/validators.ts +2 -0
  82. package/src/modules/sales/i18n/ko.json +1442 -0
  83. package/src/modules/shipping_carriers/i18n/ko.json +85 -0
  84. package/src/modules/staff/i18n/ko.json +1162 -0
  85. package/src/modules/sync_excel/i18n/ko.json +107 -0
  86. package/src/modules/translations/i18n/ko.json +45 -0
  87. package/src/modules/wms/i18n/ko.json +1053 -0
  88. package/src/modules/workflows/i18n/ko.json +1203 -0
@@ -1,4 +1,4 @@
1
- [build:core] found 3783 entry points
1
+ [build:core] found 3788 entry points
2
2
  [build:core] built successfully
3
3
  [build:core:generated] found 197 entry points
4
4
  [build:core:generated] built successfully
@@ -180,9 +180,8 @@ function ActivitiesDayStrip({ entityId, selectedDate, onSelectDate, refreshKey =
180
180
  );
181
181
  setFetchedEvents(Array.isArray(payload?.items) ? payload.items : []);
182
182
  } catch (err) {
183
- if (err?.name !== "AbortError") {
184
- logger.warn("failed to load interactions", { component: "ActivitiesDayStrip", err });
185
- }
183
+ if (err?.name === "AbortError") return;
184
+ logger.warn("failed to load interactions", { component: "ActivitiesDayStrip", err });
186
185
  setFetchedEvents([]);
187
186
  }
188
187
  })();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/components/detail/ActivitiesDayStrip.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { ChevronLeft, ChevronRight } from 'lucide-react'\nimport { toZonedTime } from 'date-fns-tz'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { TranslateFn } from '@open-mercato/shared/lib/i18n/context'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport type { InteractionSummary } from './types'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('customers')\n\ninterface ActivitiesDayStripProps {\n entityId: string\n selectedDate: Date\n onSelectDate: (date: Date) => void\n refreshKey?: number\n /**\n * Optional pre-fetched events. When provided, the day strip skips its own fetch\n * and uses the supplied list, ensuring its busyness count agrees with the\n * activity list rendered alongside it (issue #1809 \u2014 E1 status filter alignment).\n */\n events?: InteractionSummary[]\n}\n\nconst USER_TIMEZONE = (() => {\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'\n } catch {\n return 'UTC'\n }\n})()\n\n// Project a UTC ISO timestamp to the user's local timezone before comparing\n// \"same day\" (issue #1809 \u2014 E3). The browser's `new Date(iso)` treats the\n// instant correctly, but `getDate()/getMonth()/getFullYear()` reflect the\n// user's local day, so for activities scheduled at e.g. 23:30 local on a UTC\n// boundary the day-strip and list now agree.\nfunction toLocalZonedDate(value: string | Date): Date {\n return toZonedTime(value, USER_TIMEZONE)\n}\n\nconst VISIBLE_DAYS = 5\nconst BUSYNESS_SLOTS = 10\nconst SLOT_START_HOUR = 7\nconst SLOT_END_HOUR = 22\n\nconst DAY_LABEL_KEYS: Array<[number, string, string]> = [\n [0, 'customers.calendar.day.sun', 'SUN'],\n [1, 'customers.calendar.day.mon', 'MON'],\n [2, 'customers.calendar.day.tue', 'TUE'],\n [3, 'customers.calendar.day.wed', 'WED'],\n [4, 'customers.calendar.day.thu', 'THU'],\n [5, 'customers.calendar.day.fri', 'FRI'],\n [6, 'customers.calendar.day.sat', 'SAT'],\n]\n\nconst MONTH_KEYS: Array<[number, string, string]> = [\n [0, 'customers.calendar.month.january', 'January'],\n [1, 'customers.calendar.month.february', 'February'],\n [2, 'customers.calendar.month.march', 'March'],\n [3, 'customers.calendar.month.april', 'April'],\n [4, 'customers.calendar.month.may', 'May'],\n [5, 'customers.calendar.month.june', 'June'],\n [6, 'customers.calendar.month.july', 'July'],\n [7, 'customers.calendar.month.august', 'August'],\n [8, 'customers.calendar.month.september', 'September'],\n [9, 'customers.calendar.month.october', 'October'],\n [10, 'customers.calendar.month.november', 'November'],\n [11, 'customers.calendar.month.december', 'December'],\n]\n\nfunction startOfDay(date: Date): Date {\n const next = new Date(date)\n next.setHours(0, 0, 0, 0)\n return next\n}\n\nfunction endOfDay(date: Date): Date {\n const next = new Date(date)\n next.setHours(23, 59, 59, 999)\n return next\n}\n\nfunction isSameDay(a: Date, b: Date): boolean {\n return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate()\n}\n\nfunction isWeekend(date: Date): boolean {\n const day = date.getDay()\n return day === 0 || day === 6\n}\n\nfunction addDays(date: Date, delta: number): Date {\n const next = new Date(date)\n next.setDate(date.getDate() + delta)\n return next\n}\n\nfunction buildVisibleDays(anchor: Date): Date[] {\n const start = startOfDay(anchor)\n return Array.from({ length: VISIBLE_DAYS }, (_, index) => addDays(start, index))\n}\n\n// Anchor the visible window so that the given focal date lands at the center slot\n// (position 2 out of 5). Matches Figma 784:809 where the selected day is centered.\nfunction anchorCenteredOn(focalDate: Date): Date {\n return startOfDay(addDays(focalDate, -Math.floor(VISIBLE_DAYS / 2)))\n}\n\ntype SlotState = 'empty' | 'partial' | 'full' | 'conflict'\n\ntype DayBusyness = {\n totalMinutes: number\n eventCount: number\n slots: SlotState[]\n}\n\nfunction emptyBusyness(): DayBusyness {\n return {\n totalMinutes: 0,\n eventCount: 0,\n slots: Array<SlotState>(BUSYNESS_SLOTS).fill('empty'),\n }\n}\n\nfunction computeDayBusyness(events: InteractionSummary[], day: Date): DayBusyness {\n if (events.length === 0) return emptyBusyness()\n const dayStart = startOfDay(day).getTime()\n const slotMs = ((SLOT_END_HOUR - SLOT_START_HOUR) * 60 * 60 * 1000) / BUSYNESS_SLOTS\n const slotMinutes = slotMs / 60000\n const slotCounts: number[] = Array(BUSYNESS_SLOTS).fill(0)\n const slotMinutesUsed: number[] = Array(BUSYNESS_SLOTS).fill(0)\n let totalMinutes = 0\n let eventCount = 0\n\n for (const event of events) {\n const startIso = event.scheduledAt ?? event.occurredAt ?? event.createdAt\n if (!startIso) continue\n const start = new Date(startIso)\n if (Number.isNaN(start.getTime())) continue\n // Compare in the user's local timezone so an activity at 23:30 local time\n // doesn't bleed into the next UTC day's chip (issue #1809 \u2014 E3).\n const localStart = toLocalZonedDate(startIso)\n if (!isSameDay(localStart, day)) continue\n eventCount += 1\n const durationMinutes = typeof event.duration === 'number' && event.duration > 0 ? event.duration : 30\n totalMinutes += durationMinutes\n const eventStartMs = start.getTime()\n const eventEndMs = eventStartMs + durationMinutes * 60000\n const slotsStartMs = dayStart + SLOT_START_HOUR * 60 * 60 * 1000\n for (let slot = 0; slot < BUSYNESS_SLOTS; slot += 1) {\n const slotStart = slotsStartMs + slot * slotMs\n const slotEnd = slotStart + slotMs\n const overlapStart = Math.max(slotStart, eventStartMs)\n const overlapEnd = Math.min(slotEnd, eventEndMs)\n const overlapMinutes = Math.max(0, (overlapEnd - overlapStart) / 60000)\n if (overlapMinutes <= 0) continue\n slotCounts[slot] += 1\n slotMinutesUsed[slot] += overlapMinutes\n }\n }\n\n const slots: SlotState[] = slotCounts.map((count, index) => {\n if (count === 0) return 'empty'\n if (count > 1) return 'conflict'\n const used = slotMinutesUsed[index]\n if (used >= slotMinutes * 0.5) return 'full'\n return 'partial'\n })\n\n return { totalMinutes, eventCount, slots }\n}\n\nfunction formatBusyLabel(busy: DayBusyness, t: TranslateFn): string {\n if (busy.eventCount === 0) return ''\n // Match Figma 784:809 label format: \"Xm\" when under an hour, \"Xh\" otherwise.\n // Mixed \"Xh Ym\" overflows the 101px card and is not part of the visual spec.\n const durationLabel = busy.totalMinutes < 60\n ? t('customers.activities.calendar.minutesShort', '{minutes}m', { minutes: Math.max(Math.round(busy.totalMinutes), 1) })\n : t('customers.activities.calendar.hoursShort', '{hours}h', { hours: Math.floor(busy.totalMinutes / 60) })\n return t('customers.activities.calendar.eventsSummary', '{count} {countLabel} \u00B7 {duration}', {\n count: busy.eventCount,\n countLabel: busy.eventCount === 1\n ? t('customers.activities.calendar.eventSingular', 'event')\n : t('customers.activities.calendar.eventPlural', 'events'),\n duration: durationLabel,\n })\n}\n\nfunction formatMonthLabel(date: Date, t: TranslateFn): string {\n const monthEntry = MONTH_KEYS.find(([index]) => index === date.getMonth())\n const monthName = monthEntry ? t(monthEntry[1], monthEntry[2]) : ''\n return t('customers.activities.calendar.monthYear', '{month} {year}', { month: monthName, year: date.getFullYear() })\n}\n\nfunction formatDayLabel(date: Date, t: TranslateFn): string {\n const entry = DAY_LABEL_KEYS.find(([index]) => index === date.getDay())\n return entry ? t(entry[1], entry[2]) : ''\n}\n\nexport function ActivitiesDayStrip({ entityId, selectedDate, onSelectDate, refreshKey = 0, events: providedEvents }: ActivitiesDayStripProps) {\n const t = useT()\n const [anchor, setAnchor] = React.useState<Date>(() => anchorCenteredOn(selectedDate))\n const [fetchedEvents, setFetchedEvents] = React.useState<InteractionSummary[]>([])\n // When the parent supplies `events` (preferred path \u2014 keeps day strip and\n // the list in lockstep, fixes #1809 E1), skip the local fetch entirely.\n const useProvidedEvents = providedEvents !== undefined\n const events = useProvidedEvents ? providedEvents : fetchedEvents\n\n React.useEffect(() => {\n setAnchor((current) => {\n const days = buildVisibleDays(current)\n const visible = days.some((day) => isSameDay(day, selectedDate))\n if (visible) return current\n return anchorCenteredOn(selectedDate)\n })\n }, [selectedDate])\n\n const visibleDays = React.useMemo(() => buildVisibleDays(anchor), [anchor])\n const headerLabel = React.useMemo(() => formatMonthLabel(visibleDays[0], t), [visibleDays, t])\n\n React.useEffect(() => {\n if (useProvidedEvents) return\n if (!entityId || visibleDays.length === 0) return\n const controller = new AbortController()\n const fromIso = startOfDay(visibleDays[0]).toISOString()\n const toIso = endOfDay(visibleDays[visibleDays.length - 1]).toISOString()\n const params = new URLSearchParams({\n entityId,\n from: fromIso,\n to: toIso,\n limit: '100',\n sortField: 'scheduledAt',\n sortDir: 'asc',\n excludeInteractionType: 'task',\n })\n void (async () => {\n try {\n const payload = await readApiResultOrThrow<{ items?: InteractionSummary[] }>(\n `/api/customers/interactions?${params.toString()}`,\n { signal: controller.signal },\n )\n setFetchedEvents(Array.isArray(payload?.items) ? payload.items : [])\n } catch (err) {\n if ((err as { name?: string } | null)?.name !== 'AbortError') {\n logger.warn('failed to load interactions', { component: 'ActivitiesDayStrip', err })\n }\n setFetchedEvents([])\n }\n })()\n return () => controller.abort()\n }, [entityId, visibleDays, refreshKey, useProvidedEvents])\n\n const todayDate = React.useMemo(() => startOfDay(new Date()), [])\n\n const handlePrev = React.useCallback(() => {\n setAnchor((current) => addDays(current, -VISIBLE_DAYS))\n }, [])\n const handleNext = React.useCallback(() => {\n setAnchor((current) => addDays(current, VISIBLE_DAYS))\n }, [])\n const handleHeaderPrev = React.useCallback(() => {\n setAnchor((current) => {\n const next = new Date(current)\n next.setMonth(current.getMonth() - 1)\n return startOfDay(next)\n })\n }, [])\n const handleHeaderNext = React.useCallback(() => {\n setAnchor((current) => {\n const next = new Date(current)\n next.setMonth(current.getMonth() + 1)\n return startOfDay(next)\n })\n }, [])\n\n return (\n <div className=\"flex flex-col gap-2.5 rounded-md px-3.5 py-3 w-full\">\n <div className=\"flex items-center justify-center gap-1.5 rounded-md bg-muted px-1.5 py-1.5\">\n <button\n type=\"button\"\n onClick={handleHeaderPrev}\n aria-label={t('customers.activities.calendar.prevMonth', 'Previous month')}\n className=\"flex size-6 items-center justify-center rounded-md border border-border bg-card shadow-xs hover:bg-accent/40\"\n >\n <ChevronLeft className=\"size-4 text-foreground\" />\n </button>\n <span className=\"flex-1 text-center text-sm font-medium leading-5 text-foreground\">{headerLabel}</span>\n <button\n type=\"button\"\n onClick={handleHeaderNext}\n aria-label={t('customers.activities.calendar.nextMonth', 'Next month')}\n className=\"flex size-6 items-center justify-center rounded-md border border-border bg-card shadow-xs hover:bg-accent/40\"\n >\n <ChevronRight className=\"size-4 text-foreground\" />\n </button>\n </div>\n <div className=\"flex w-full items-center gap-2\">\n <button\n type=\"button\"\n onClick={handlePrev}\n aria-label={t('customers.activities.calendar.prevWindow', 'Previous days')}\n className=\"flex size-6 shrink-0 items-center justify-center rounded-md border border-border bg-card shadow-xs hover:bg-accent/40\"\n >\n <ChevronLeft className=\"size-4 text-foreground\" />\n </button>\n <div className=\"flex flex-1 items-stretch justify-center gap-1\">\n {visibleDays.map((day) => {\n const busy = computeDayBusyness(events, day)\n const isSelected = isSameDay(day, selectedDate)\n const isToday = isSameDay(day, todayDate)\n const weekend = isWeekend(day)\n const busyLabel = busy.eventCount > 0\n ? formatBusyLabel(busy, t)\n : weekend\n ? t('customers.activities.calendar.weekend', 'Weekend')\n : ''\n return (\n <DayCard\n key={day.toISOString()}\n day={day}\n isActive={isSelected}\n isToday={isToday}\n busyness={busy}\n label={busyLabel}\n dayName={formatDayLabel(day, t)}\n onSelect={() => onSelectDate(day)}\n />\n )\n })}\n </div>\n <button\n type=\"button\"\n onClick={handleNext}\n aria-label={t('customers.activities.calendar.nextWindow', 'Next days')}\n className=\"flex size-6 shrink-0 items-center justify-center rounded-md border border-border bg-card shadow-xs hover:bg-accent/40\"\n >\n <ChevronRight className=\"size-4 text-foreground\" />\n </button>\n </div>\n </div>\n )\n}\n\ninterface DayCardProps {\n day: Date\n isActive: boolean\n isToday: boolean\n busyness: DayBusyness\n label: string\n dayName: string\n onSelect: () => void\n}\n\nfunction DayCard({ day, isActive, isToday, busyness, label, dayName, onSelect }: DayCardProps) {\n const dayNumber = String(day.getDate()).padStart(2, '0')\n return (\n <button\n type=\"button\"\n onClick={onSelect}\n aria-pressed={isActive}\n aria-label={`${dayName} ${dayNumber}`}\n className={cn(\n 'flex h-[104px] w-[101px] flex-col items-center gap-[6px] overflow-hidden rounded-[10px] border p-[12px] transition-colors',\n isActive\n ? 'border-transparent bg-foreground'\n : 'border-border bg-card hover:border-foreground/40',\n )}\n >\n <span className=\"text-[11px] font-medium leading-none tracking-[0.44px] text-muted-foreground\">\n {dayName}\n </span>\n <div className=\"flex items-center gap-[5px]\">\n <span\n className={cn(\n 'text-2xl font-semibold leading-7',\n isActive ? 'text-background' : 'text-foreground',\n )}\n >\n {dayNumber}\n </span>\n {isToday ? (\n <span\n className=\"inline-block size-1.5 rounded-full bg-status-info-icon\"\n aria-hidden\n />\n ) : null}\n </div>\n <div className=\"flex h-4 w-[82px] items-end gap-[1.5px]\">\n {busyness.slots.map((state, index) => (\n <BusySlot key={index} state={state} active={isActive} />\n ))}\n </div>\n <span className=\"text-[11px] leading-[14px] font-normal whitespace-nowrap text-muted-foreground\">\n {label}\n </span>\n </button>\n )\n}\n\nfunction BusySlot({ state, active }: { state: SlotState; active: boolean }) {\n const heightClass = state === 'empty'\n ? 'h-0.5'\n : state === 'partial'\n ? 'h-2'\n : 'h-3.5'\n let bgClass: string\n if (state === 'conflict') {\n bgClass = 'bg-status-error-icon'\n } else if (active) {\n if (state === 'empty') bgClass = 'bg-background/30'\n else if (state === 'partial') bgClass = 'bg-background/60'\n else bgClass = 'bg-background'\n } else {\n if (state === 'empty') bgClass = 'bg-border'\n else if (state === 'partial') bgClass = 'bg-muted-foreground'\n else bgClass = 'bg-foreground'\n }\n return <div className={cn('w-[7px] shrink-0 rounded-[1.5px]', heightClass, bgClass)} aria-hidden />\n}\n\nexport default ActivitiesDayStrip\n"],
5
- "mappings": ";AAyRM,SAOI,KAPJ;AAvRN,YAAY,WAAW;AACvB,SAAS,aAAa,oBAAoB;AAC1C,SAAS,mBAAmB;AAC5B,SAAS,UAAU;AACnB,SAAS,YAAY;AAErB,SAAS,4BAA4B;AAErC,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,WAAW;AAevC,MAAM,iBAAiB,MAAM;AAC3B,MAAI;AACF,WAAO,KAAK,eAAe,EAAE,gBAAgB,EAAE,YAAY;AAAA,EAC7D,QAAQ;AACN,WAAO;AAAA,EACT;AACF,GAAG;AAOH,SAAS,iBAAiB,OAA4B;AACpD,SAAO,YAAY,OAAO,aAAa;AACzC;AAEA,MAAM,eAAe;AACrB,MAAM,iBAAiB;AACvB,MAAM,kBAAkB;AACxB,MAAM,gBAAgB;AAEtB,MAAM,iBAAkD;AAAA,EACtD,CAAC,GAAG,8BAA8B,KAAK;AAAA,EACvC,CAAC,GAAG,8BAA8B,KAAK;AAAA,EACvC,CAAC,GAAG,8BAA8B,KAAK;AAAA,EACvC,CAAC,GAAG,8BAA8B,KAAK;AAAA,EACvC,CAAC,GAAG,8BAA8B,KAAK;AAAA,EACvC,CAAC,GAAG,8BAA8B,KAAK;AAAA,EACvC,CAAC,GAAG,8BAA8B,KAAK;AACzC;AAEA,MAAM,aAA8C;AAAA,EAClD,CAAC,GAAG,oCAAoC,SAAS;AAAA,EACjD,CAAC,GAAG,qCAAqC,UAAU;AAAA,EACnD,CAAC,GAAG,kCAAkC,OAAO;AAAA,EAC7C,CAAC,GAAG,kCAAkC,OAAO;AAAA,EAC7C,CAAC,GAAG,gCAAgC,KAAK;AAAA,EACzC,CAAC,GAAG,iCAAiC,MAAM;AAAA,EAC3C,CAAC,GAAG,iCAAiC,MAAM;AAAA,EAC3C,CAAC,GAAG,mCAAmC,QAAQ;AAAA,EAC/C,CAAC,GAAG,sCAAsC,WAAW;AAAA,EACrD,CAAC,GAAG,oCAAoC,SAAS;AAAA,EACjD,CAAC,IAAI,qCAAqC,UAAU;AAAA,EACpD,CAAC,IAAI,qCAAqC,UAAU;AACtD;AAEA,SAAS,WAAW,MAAkB;AACpC,QAAM,OAAO,IAAI,KAAK,IAAI;AAC1B,OAAK,SAAS,GAAG,GAAG,GAAG,CAAC;AACxB,SAAO;AACT;AAEA,SAAS,SAAS,MAAkB;AAClC,QAAM,OAAO,IAAI,KAAK,IAAI;AAC1B,OAAK,SAAS,IAAI,IAAI,IAAI,GAAG;AAC7B,SAAO;AACT;AAEA,SAAS,UAAU,GAAS,GAAkB;AAC5C,SAAO,EAAE,YAAY,MAAM,EAAE,YAAY,KAAK,EAAE,SAAS,MAAM,EAAE,SAAS,KAAK,EAAE,QAAQ,MAAM,EAAE,QAAQ;AAC3G;AAEA,SAAS,UAAU,MAAqB;AACtC,QAAM,MAAM,KAAK,OAAO;AACxB,SAAO,QAAQ,KAAK,QAAQ;AAC9B;AAEA,SAAS,QAAQ,MAAY,OAAqB;AAChD,QAAM,OAAO,IAAI,KAAK,IAAI;AAC1B,OAAK,QAAQ,KAAK,QAAQ,IAAI,KAAK;AACnC,SAAO;AACT;AAEA,SAAS,iBAAiB,QAAsB;AAC9C,QAAM,QAAQ,WAAW,MAAM;AAC/B,SAAO,MAAM,KAAK,EAAE,QAAQ,aAAa,GAAG,CAAC,GAAG,UAAU,QAAQ,OAAO,KAAK,CAAC;AACjF;AAIA,SAAS,iBAAiB,WAAuB;AAC/C,SAAO,WAAW,QAAQ,WAAW,CAAC,KAAK,MAAM,eAAe,CAAC,CAAC,CAAC;AACrE;AAUA,SAAS,gBAA6B;AACpC,SAAO;AAAA,IACL,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,OAAO,MAAiB,cAAc,EAAE,KAAK,OAAO;AAAA,EACtD;AACF;AAEA,SAAS,mBAAmB,QAA8B,KAAwB;AAChF,MAAI,OAAO,WAAW,EAAG,QAAO,cAAc;AAC9C,QAAM,WAAW,WAAW,GAAG,EAAE,QAAQ;AACzC,QAAM,UAAW,gBAAgB,mBAAmB,KAAK,KAAK,MAAQ;AACtE,QAAM,cAAc,SAAS;AAC7B,QAAM,aAAuB,MAAM,cAAc,EAAE,KAAK,CAAC;AACzD,QAAM,kBAA4B,MAAM,cAAc,EAAE,KAAK,CAAC;AAC9D,MAAI,eAAe;AACnB,MAAI,aAAa;AAEjB,aAAW,SAAS,QAAQ;AAC1B,UAAM,WAAW,MAAM,eAAe,MAAM,cAAc,MAAM;AAChE,QAAI,CAAC,SAAU;AACf,UAAM,QAAQ,IAAI,KAAK,QAAQ;AAC/B,QAAI,OAAO,MAAM,MAAM,QAAQ,CAAC,EAAG;AAGnC,UAAM,aAAa,iBAAiB,QAAQ;AAC5C,QAAI,CAAC,UAAU,YAAY,GAAG,EAAG;AACjC,kBAAc;AACd,UAAM,kBAAkB,OAAO,MAAM,aAAa,YAAY,MAAM,WAAW,IAAI,MAAM,WAAW;AACpG,oBAAgB;AAChB,UAAM,eAAe,MAAM,QAAQ;AACnC,UAAM,aAAa,eAAe,kBAAkB;AACpD,UAAM,eAAe,WAAW,kBAAkB,KAAK,KAAK;AAC5D,aAAS,OAAO,GAAG,OAAO,gBAAgB,QAAQ,GAAG;AACnD,YAAM,YAAY,eAAe,OAAO;AACxC,YAAM,UAAU,YAAY;AAC5B,YAAM,eAAe,KAAK,IAAI,WAAW,YAAY;AACrD,YAAM,aAAa,KAAK,IAAI,SAAS,UAAU;AAC/C,YAAM,iBAAiB,KAAK,IAAI,IAAI,aAAa,gBAAgB,GAAK;AACtE,UAAI,kBAAkB,EAAG;AACzB,iBAAW,IAAI,KAAK;AACpB,sBAAgB,IAAI,KAAK;AAAA,IAC3B;AAAA,EACF;AAEA,QAAM,QAAqB,WAAW,IAAI,CAAC,OAAO,UAAU;AAC1D,QAAI,UAAU,EAAG,QAAO;AACxB,QAAI,QAAQ,EAAG,QAAO;AACtB,UAAM,OAAO,gBAAgB,KAAK;AAClC,QAAI,QAAQ,cAAc,IAAK,QAAO;AACtC,WAAO;AAAA,EACT,CAAC;AAED,SAAO,EAAE,cAAc,YAAY,MAAM;AAC3C;AAEA,SAAS,gBAAgB,MAAmB,GAAwB;AAClE,MAAI,KAAK,eAAe,EAAG,QAAO;AAGlC,QAAM,gBAAgB,KAAK,eAAe,KACtC,EAAE,8CAA8C,cAAc,EAAE,SAAS,KAAK,IAAI,KAAK,MAAM,KAAK,YAAY,GAAG,CAAC,EAAE,CAAC,IACrH,EAAE,4CAA4C,YAAY,EAAE,OAAO,KAAK,MAAM,KAAK,eAAe,EAAE,EAAE,CAAC;AAC3G,SAAO,EAAE,+CAA+C,wCAAqC;AAAA,IAC3F,OAAO,KAAK;AAAA,IACZ,YAAY,KAAK,eAAe,IAC5B,EAAE,+CAA+C,OAAO,IACxD,EAAE,6CAA6C,QAAQ;AAAA,IAC3D,UAAU;AAAA,EACZ,CAAC;AACH;AAEA,SAAS,iBAAiB,MAAY,GAAwB;AAC5D,QAAM,aAAa,WAAW,KAAK,CAAC,CAAC,KAAK,MAAM,UAAU,KAAK,SAAS,CAAC;AACzE,QAAM,YAAY,aAAa,EAAE,WAAW,CAAC,GAAG,WAAW,CAAC,CAAC,IAAI;AACjE,SAAO,EAAE,2CAA2C,kBAAkB,EAAE,OAAO,WAAW,MAAM,KAAK,YAAY,EAAE,CAAC;AACtH;AAEA,SAAS,eAAe,MAAY,GAAwB;AAC1D,QAAM,QAAQ,eAAe,KAAK,CAAC,CAAC,KAAK,MAAM,UAAU,KAAK,OAAO,CAAC;AACtE,SAAO,QAAQ,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI;AACzC;AAEO,SAAS,mBAAmB,EAAE,UAAU,cAAc,cAAc,aAAa,GAAG,QAAQ,eAAe,GAA4B;AAC5I,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAe,MAAM,iBAAiB,YAAY,CAAC;AACrF,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAA+B,CAAC,CAAC;AAGjF,QAAM,oBAAoB,mBAAmB;AAC7C,QAAM,SAAS,oBAAoB,iBAAiB;AAEpD,QAAM,UAAU,MAAM;AACpB,cAAU,CAAC,YAAY;AACrB,YAAM,OAAO,iBAAiB,OAAO;AACrC,YAAM,UAAU,KAAK,KAAK,CAAC,QAAQ,UAAU,KAAK,YAAY,CAAC;AAC/D,UAAI,QAAS,QAAO;AACpB,aAAO,iBAAiB,YAAY;AAAA,IACtC,CAAC;AAAA,EACH,GAAG,CAAC,YAAY,CAAC;AAEjB,QAAM,cAAc,MAAM,QAAQ,MAAM,iBAAiB,MAAM,GAAG,CAAC,MAAM,CAAC;AAC1E,QAAM,cAAc,MAAM,QAAQ,MAAM,iBAAiB,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;AAE7F,QAAM,UAAU,MAAM;AACpB,QAAI,kBAAmB;AACvB,QAAI,CAAC,YAAY,YAAY,WAAW,EAAG;AAC3C,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,UAAU,WAAW,YAAY,CAAC,CAAC,EAAE,YAAY;AACvD,UAAM,QAAQ,SAAS,YAAY,YAAY,SAAS,CAAC,CAAC,EAAE,YAAY;AACxE,UAAM,SAAS,IAAI,gBAAgB;AAAA,MACjC;AAAA,MACA,MAAM;AAAA,MACN,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,WAAW;AAAA,MACX,SAAS;AAAA,MACT,wBAAwB;AAAA,IAC1B,CAAC;AACD,UAAM,YAAY;AAChB,UAAI;AACF,cAAM,UAAU,MAAM;AAAA,UACpB,+BAA+B,OAAO,SAAS,CAAC;AAAA,UAChD,EAAE,QAAQ,WAAW,OAAO;AAAA,QAC9B;AACA,yBAAiB,MAAM,QAAQ,SAAS,KAAK,IAAI,QAAQ,QAAQ,CAAC,CAAC;AAAA,MACrE,SAAS,KAAK;AACZ,YAAK,KAAkC,SAAS,cAAc;AAC5D,iBAAO,KAAK,+BAA+B,EAAE,WAAW,sBAAsB,IAAI,CAAC;AAAA,QACrF;AACA,yBAAiB,CAAC,CAAC;AAAA,MACrB;AAAA,IACF,GAAG;AACH,WAAO,MAAM,WAAW,MAAM;AAAA,EAChC,GAAG,CAAC,UAAU,aAAa,YAAY,iBAAiB,CAAC;AAEzD,QAAM,YAAY,MAAM,QAAQ,MAAM,WAAW,oBAAI,KAAK,CAAC,GAAG,CAAC,CAAC;AAEhE,QAAM,aAAa,MAAM,YAAY,MAAM;AACzC,cAAU,CAAC,YAAY,QAAQ,SAAS,CAAC,YAAY,CAAC;AAAA,EACxD,GAAG,CAAC,CAAC;AACL,QAAM,aAAa,MAAM,YAAY,MAAM;AACzC,cAAU,CAAC,YAAY,QAAQ,SAAS,YAAY,CAAC;AAAA,EACvD,GAAG,CAAC,CAAC;AACL,QAAM,mBAAmB,MAAM,YAAY,MAAM;AAC/C,cAAU,CAAC,YAAY;AACrB,YAAM,OAAO,IAAI,KAAK,OAAO;AAC7B,WAAK,SAAS,QAAQ,SAAS,IAAI,CAAC;AACpC,aAAO,WAAW,IAAI;AAAA,IACxB,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AACL,QAAM,mBAAmB,MAAM,YAAY,MAAM;AAC/C,cAAU,CAAC,YAAY;AACrB,YAAM,OAAO,IAAI,KAAK,OAAO;AAC7B,WAAK,SAAS,QAAQ,SAAS,IAAI,CAAC;AACpC,aAAO,WAAW,IAAI;AAAA,IACxB,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AAEL,SACE,qBAAC,SAAI,WAAU,uDACb;AAAA,yBAAC,SAAI,WAAU,8EACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS;AAAA,UACT,cAAY,EAAE,2CAA2C,gBAAgB;AAAA,UACzE,WAAU;AAAA,UAEV,8BAAC,eAAY,WAAU,0BAAyB;AAAA;AAAA,MAClD;AAAA,MACA,oBAAC,UAAK,WAAU,oEAAoE,uBAAY;AAAA,MAChG;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS;AAAA,UACT,cAAY,EAAE,2CAA2C,YAAY;AAAA,UACrE,WAAU;AAAA,UAEV,8BAAC,gBAAa,WAAU,0BAAyB;AAAA;AAAA,MACnD;AAAA,OACF;AAAA,IACA,qBAAC,SAAI,WAAU,kCACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS;AAAA,UACT,cAAY,EAAE,4CAA4C,eAAe;AAAA,UACzE,WAAU;AAAA,UAEV,8BAAC,eAAY,WAAU,0BAAyB;AAAA;AAAA,MAClD;AAAA,MACA,oBAAC,SAAI,WAAU,kDACZ,sBAAY,IAAI,CAAC,QAAQ;AACxB,cAAM,OAAO,mBAAmB,QAAQ,GAAG;AAC3C,cAAM,aAAa,UAAU,KAAK,YAAY;AAC9C,cAAM,UAAU,UAAU,KAAK,SAAS;AACxC,cAAM,UAAU,UAAU,GAAG;AAC7B,cAAM,YAAY,KAAK,aAAa,IAChC,gBAAgB,MAAM,CAAC,IACvB,UACE,EAAE,yCAAyC,SAAS,IACpD;AACN,eACE;AAAA,UAAC;AAAA;AAAA,YAEC;AAAA,YACA,UAAU;AAAA,YACV;AAAA,YACA,UAAU;AAAA,YACV,OAAO;AAAA,YACP,SAAS,eAAe,KAAK,CAAC;AAAA,YAC9B,UAAU,MAAM,aAAa,GAAG;AAAA;AAAA,UAP3B,IAAI,YAAY;AAAA,QAQvB;AAAA,MAEJ,CAAC,GACH;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS;AAAA,UACT,cAAY,EAAE,4CAA4C,WAAW;AAAA,UACrE,WAAU;AAAA,UAEV,8BAAC,gBAAa,WAAU,0BAAyB;AAAA;AAAA,MACnD;AAAA,OACF;AAAA,KACF;AAEJ;AAYA,SAAS,QAAQ,EAAE,KAAK,UAAU,SAAS,UAAU,OAAO,SAAS,SAAS,GAAiB;AAC7F,QAAM,YAAY,OAAO,IAAI,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG;AACvD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,SAAS;AAAA,MACT,gBAAc;AAAA,MACd,cAAY,GAAG,OAAO,IAAI,SAAS;AAAA,MACnC,WAAW;AAAA,QACT;AAAA,QACA,WACI,qCACA;AAAA,MACN;AAAA,MAEA;AAAA,4BAAC,UAAK,WAAU,gFACb,mBACH;AAAA,QACA,qBAAC,SAAI,WAAU,+BACb;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,WAAW;AAAA,gBACT;AAAA,gBACA,WAAW,oBAAoB;AAAA,cACjC;AAAA,cAEC;AAAA;AAAA,UACH;AAAA,UACC,UACC;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,eAAW;AAAA;AAAA,UACb,IACE;AAAA,WACN;AAAA,QACA,oBAAC,SAAI,WAAU,2CACZ,mBAAS,MAAM,IAAI,CAAC,OAAO,UAC1B,oBAAC,YAAqB,OAAc,QAAQ,YAA7B,KAAuC,CACvD,GACH;AAAA,QACA,oBAAC,UAAK,WAAU,kFACb,iBACH;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,SAAS,SAAS,EAAE,OAAO,OAAO,GAA0C;AAC1E,QAAM,cAAc,UAAU,UAC1B,UACA,UAAU,YACR,QACA;AACN,MAAI;AACJ,MAAI,UAAU,YAAY;AACxB,cAAU;AAAA,EACZ,WAAW,QAAQ;AACjB,QAAI,UAAU,QAAS,WAAU;AAAA,aACxB,UAAU,UAAW,WAAU;AAAA,QACnC,WAAU;AAAA,EACjB,OAAO;AACL,QAAI,UAAU,QAAS,WAAU;AAAA,aACxB,UAAU,UAAW,WAAU;AAAA,QACnC,WAAU;AAAA,EACjB;AACA,SAAO,oBAAC,SAAI,WAAW,GAAG,oCAAoC,aAAa,OAAO,GAAG,eAAW,MAAC;AACnG;AAEA,IAAO,6BAAQ;",
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { ChevronLeft, ChevronRight } from 'lucide-react'\nimport { toZonedTime } from 'date-fns-tz'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { TranslateFn } from '@open-mercato/shared/lib/i18n/context'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport type { InteractionSummary } from './types'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('customers')\n\ninterface ActivitiesDayStripProps {\n entityId: string\n selectedDate: Date\n onSelectDate: (date: Date) => void\n refreshKey?: number\n /**\n * Optional pre-fetched events. When provided, the day strip skips its own fetch\n * and uses the supplied list, ensuring its busyness count agrees with the\n * activity list rendered alongside it (issue #1809 \u2014 E1 status filter alignment).\n */\n events?: InteractionSummary[]\n}\n\nconst USER_TIMEZONE = (() => {\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'\n } catch {\n return 'UTC'\n }\n})()\n\n// Project a UTC ISO timestamp to the user's local timezone before comparing\n// \"same day\" (issue #1809 \u2014 E3). The browser's `new Date(iso)` treats the\n// instant correctly, but `getDate()/getMonth()/getFullYear()` reflect the\n// user's local day, so for activities scheduled at e.g. 23:30 local on a UTC\n// boundary the day-strip and list now agree.\nfunction toLocalZonedDate(value: string | Date): Date {\n return toZonedTime(value, USER_TIMEZONE)\n}\n\nconst VISIBLE_DAYS = 5\nconst BUSYNESS_SLOTS = 10\nconst SLOT_START_HOUR = 7\nconst SLOT_END_HOUR = 22\n\nconst DAY_LABEL_KEYS: Array<[number, string, string]> = [\n [0, 'customers.calendar.day.sun', 'SUN'],\n [1, 'customers.calendar.day.mon', 'MON'],\n [2, 'customers.calendar.day.tue', 'TUE'],\n [3, 'customers.calendar.day.wed', 'WED'],\n [4, 'customers.calendar.day.thu', 'THU'],\n [5, 'customers.calendar.day.fri', 'FRI'],\n [6, 'customers.calendar.day.sat', 'SAT'],\n]\n\nconst MONTH_KEYS: Array<[number, string, string]> = [\n [0, 'customers.calendar.month.january', 'January'],\n [1, 'customers.calendar.month.february', 'February'],\n [2, 'customers.calendar.month.march', 'March'],\n [3, 'customers.calendar.month.april', 'April'],\n [4, 'customers.calendar.month.may', 'May'],\n [5, 'customers.calendar.month.june', 'June'],\n [6, 'customers.calendar.month.july', 'July'],\n [7, 'customers.calendar.month.august', 'August'],\n [8, 'customers.calendar.month.september', 'September'],\n [9, 'customers.calendar.month.october', 'October'],\n [10, 'customers.calendar.month.november', 'November'],\n [11, 'customers.calendar.month.december', 'December'],\n]\n\nfunction startOfDay(date: Date): Date {\n const next = new Date(date)\n next.setHours(0, 0, 0, 0)\n return next\n}\n\nfunction endOfDay(date: Date): Date {\n const next = new Date(date)\n next.setHours(23, 59, 59, 999)\n return next\n}\n\nfunction isSameDay(a: Date, b: Date): boolean {\n return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate()\n}\n\nfunction isWeekend(date: Date): boolean {\n const day = date.getDay()\n return day === 0 || day === 6\n}\n\nfunction addDays(date: Date, delta: number): Date {\n const next = new Date(date)\n next.setDate(date.getDate() + delta)\n return next\n}\n\nfunction buildVisibleDays(anchor: Date): Date[] {\n const start = startOfDay(anchor)\n return Array.from({ length: VISIBLE_DAYS }, (_, index) => addDays(start, index))\n}\n\n// Anchor the visible window so that the given focal date lands at the center slot\n// (position 2 out of 5). Matches Figma 784:809 where the selected day is centered.\nfunction anchorCenteredOn(focalDate: Date): Date {\n return startOfDay(addDays(focalDate, -Math.floor(VISIBLE_DAYS / 2)))\n}\n\ntype SlotState = 'empty' | 'partial' | 'full' | 'conflict'\n\ntype DayBusyness = {\n totalMinutes: number\n eventCount: number\n slots: SlotState[]\n}\n\nfunction emptyBusyness(): DayBusyness {\n return {\n totalMinutes: 0,\n eventCount: 0,\n slots: Array<SlotState>(BUSYNESS_SLOTS).fill('empty'),\n }\n}\n\nfunction computeDayBusyness(events: InteractionSummary[], day: Date): DayBusyness {\n if (events.length === 0) return emptyBusyness()\n const dayStart = startOfDay(day).getTime()\n const slotMs = ((SLOT_END_HOUR - SLOT_START_HOUR) * 60 * 60 * 1000) / BUSYNESS_SLOTS\n const slotMinutes = slotMs / 60000\n const slotCounts: number[] = Array(BUSYNESS_SLOTS).fill(0)\n const slotMinutesUsed: number[] = Array(BUSYNESS_SLOTS).fill(0)\n let totalMinutes = 0\n let eventCount = 0\n\n for (const event of events) {\n const startIso = event.scheduledAt ?? event.occurredAt ?? event.createdAt\n if (!startIso) continue\n const start = new Date(startIso)\n if (Number.isNaN(start.getTime())) continue\n // Compare in the user's local timezone so an activity at 23:30 local time\n // doesn't bleed into the next UTC day's chip (issue #1809 \u2014 E3).\n const localStart = toLocalZonedDate(startIso)\n if (!isSameDay(localStart, day)) continue\n eventCount += 1\n const durationMinutes = typeof event.duration === 'number' && event.duration > 0 ? event.duration : 30\n totalMinutes += durationMinutes\n const eventStartMs = start.getTime()\n const eventEndMs = eventStartMs + durationMinutes * 60000\n const slotsStartMs = dayStart + SLOT_START_HOUR * 60 * 60 * 1000\n for (let slot = 0; slot < BUSYNESS_SLOTS; slot += 1) {\n const slotStart = slotsStartMs + slot * slotMs\n const slotEnd = slotStart + slotMs\n const overlapStart = Math.max(slotStart, eventStartMs)\n const overlapEnd = Math.min(slotEnd, eventEndMs)\n const overlapMinutes = Math.max(0, (overlapEnd - overlapStart) / 60000)\n if (overlapMinutes <= 0) continue\n slotCounts[slot] += 1\n slotMinutesUsed[slot] += overlapMinutes\n }\n }\n\n const slots: SlotState[] = slotCounts.map((count, index) => {\n if (count === 0) return 'empty'\n if (count > 1) return 'conflict'\n const used = slotMinutesUsed[index]\n if (used >= slotMinutes * 0.5) return 'full'\n return 'partial'\n })\n\n return { totalMinutes, eventCount, slots }\n}\n\nfunction formatBusyLabel(busy: DayBusyness, t: TranslateFn): string {\n if (busy.eventCount === 0) return ''\n // Match Figma 784:809 label format: \"Xm\" when under an hour, \"Xh\" otherwise.\n // Mixed \"Xh Ym\" overflows the 101px card and is not part of the visual spec.\n const durationLabel = busy.totalMinutes < 60\n ? t('customers.activities.calendar.minutesShort', '{minutes}m', { minutes: Math.max(Math.round(busy.totalMinutes), 1) })\n : t('customers.activities.calendar.hoursShort', '{hours}h', { hours: Math.floor(busy.totalMinutes / 60) })\n return t('customers.activities.calendar.eventsSummary', '{count} {countLabel} \u00B7 {duration}', {\n count: busy.eventCount,\n countLabel: busy.eventCount === 1\n ? t('customers.activities.calendar.eventSingular', 'event')\n : t('customers.activities.calendar.eventPlural', 'events'),\n duration: durationLabel,\n })\n}\n\nfunction formatMonthLabel(date: Date, t: TranslateFn): string {\n const monthEntry = MONTH_KEYS.find(([index]) => index === date.getMonth())\n const monthName = monthEntry ? t(monthEntry[1], monthEntry[2]) : ''\n return t('customers.activities.calendar.monthYear', '{month} {year}', { month: monthName, year: date.getFullYear() })\n}\n\nfunction formatDayLabel(date: Date, t: TranslateFn): string {\n const entry = DAY_LABEL_KEYS.find(([index]) => index === date.getDay())\n return entry ? t(entry[1], entry[2]) : ''\n}\n\nexport function ActivitiesDayStrip({ entityId, selectedDate, onSelectDate, refreshKey = 0, events: providedEvents }: ActivitiesDayStripProps) {\n const t = useT()\n const [anchor, setAnchor] = React.useState<Date>(() => anchorCenteredOn(selectedDate))\n const [fetchedEvents, setFetchedEvents] = React.useState<InteractionSummary[]>([])\n // When the parent supplies `events` (preferred path \u2014 keeps day strip and\n // the list in lockstep, fixes #1809 E1), skip the local fetch entirely.\n const useProvidedEvents = providedEvents !== undefined\n const events = useProvidedEvents ? providedEvents : fetchedEvents\n\n React.useEffect(() => {\n setAnchor((current) => {\n const days = buildVisibleDays(current)\n const visible = days.some((day) => isSameDay(day, selectedDate))\n if (visible) return current\n return anchorCenteredOn(selectedDate)\n })\n }, [selectedDate])\n\n const visibleDays = React.useMemo(() => buildVisibleDays(anchor), [anchor])\n const headerLabel = React.useMemo(() => formatMonthLabel(visibleDays[0], t), [visibleDays, t])\n\n React.useEffect(() => {\n if (useProvidedEvents) return\n if (!entityId || visibleDays.length === 0) return\n const controller = new AbortController()\n const fromIso = startOfDay(visibleDays[0]).toISOString()\n const toIso = endOfDay(visibleDays[visibleDays.length - 1]).toISOString()\n const params = new URLSearchParams({\n entityId,\n from: fromIso,\n to: toIso,\n limit: '100',\n sortField: 'scheduledAt',\n sortDir: 'asc',\n excludeInteractionType: 'task',\n })\n void (async () => {\n try {\n const payload = await readApiResultOrThrow<{ items?: InteractionSummary[] }>(\n `/api/customers/interactions?${params.toString()}`,\n { signal: controller.signal },\n )\n setFetchedEvents(Array.isArray(payload?.items) ? payload.items : [])\n } catch (err) {\n if ((err as { name?: string } | null)?.name === 'AbortError') return\n logger.warn('failed to load interactions', { component: 'ActivitiesDayStrip', err })\n setFetchedEvents([])\n }\n })()\n return () => controller.abort()\n }, [entityId, visibleDays, refreshKey, useProvidedEvents])\n\n const todayDate = React.useMemo(() => startOfDay(new Date()), [])\n\n const handlePrev = React.useCallback(() => {\n setAnchor((current) => addDays(current, -VISIBLE_DAYS))\n }, [])\n const handleNext = React.useCallback(() => {\n setAnchor((current) => addDays(current, VISIBLE_DAYS))\n }, [])\n const handleHeaderPrev = React.useCallback(() => {\n setAnchor((current) => {\n const next = new Date(current)\n next.setMonth(current.getMonth() - 1)\n return startOfDay(next)\n })\n }, [])\n const handleHeaderNext = React.useCallback(() => {\n setAnchor((current) => {\n const next = new Date(current)\n next.setMonth(current.getMonth() + 1)\n return startOfDay(next)\n })\n }, [])\n\n return (\n <div className=\"flex flex-col gap-2.5 rounded-md px-3.5 py-3 w-full\">\n <div className=\"flex items-center justify-center gap-1.5 rounded-md bg-muted px-1.5 py-1.5\">\n <button\n type=\"button\"\n onClick={handleHeaderPrev}\n aria-label={t('customers.activities.calendar.prevMonth', 'Previous month')}\n className=\"flex size-6 items-center justify-center rounded-md border border-border bg-card shadow-xs hover:bg-accent/40\"\n >\n <ChevronLeft className=\"size-4 text-foreground\" />\n </button>\n <span className=\"flex-1 text-center text-sm font-medium leading-5 text-foreground\">{headerLabel}</span>\n <button\n type=\"button\"\n onClick={handleHeaderNext}\n aria-label={t('customers.activities.calendar.nextMonth', 'Next month')}\n className=\"flex size-6 items-center justify-center rounded-md border border-border bg-card shadow-xs hover:bg-accent/40\"\n >\n <ChevronRight className=\"size-4 text-foreground\" />\n </button>\n </div>\n <div className=\"flex w-full items-center gap-2\">\n <button\n type=\"button\"\n onClick={handlePrev}\n aria-label={t('customers.activities.calendar.prevWindow', 'Previous days')}\n className=\"flex size-6 shrink-0 items-center justify-center rounded-md border border-border bg-card shadow-xs hover:bg-accent/40\"\n >\n <ChevronLeft className=\"size-4 text-foreground\" />\n </button>\n <div className=\"flex flex-1 items-stretch justify-center gap-1\">\n {visibleDays.map((day) => {\n const busy = computeDayBusyness(events, day)\n const isSelected = isSameDay(day, selectedDate)\n const isToday = isSameDay(day, todayDate)\n const weekend = isWeekend(day)\n const busyLabel = busy.eventCount > 0\n ? formatBusyLabel(busy, t)\n : weekend\n ? t('customers.activities.calendar.weekend', 'Weekend')\n : ''\n return (\n <DayCard\n key={day.toISOString()}\n day={day}\n isActive={isSelected}\n isToday={isToday}\n busyness={busy}\n label={busyLabel}\n dayName={formatDayLabel(day, t)}\n onSelect={() => onSelectDate(day)}\n />\n )\n })}\n </div>\n <button\n type=\"button\"\n onClick={handleNext}\n aria-label={t('customers.activities.calendar.nextWindow', 'Next days')}\n className=\"flex size-6 shrink-0 items-center justify-center rounded-md border border-border bg-card shadow-xs hover:bg-accent/40\"\n >\n <ChevronRight className=\"size-4 text-foreground\" />\n </button>\n </div>\n </div>\n )\n}\n\ninterface DayCardProps {\n day: Date\n isActive: boolean\n isToday: boolean\n busyness: DayBusyness\n label: string\n dayName: string\n onSelect: () => void\n}\n\nfunction DayCard({ day, isActive, isToday, busyness, label, dayName, onSelect }: DayCardProps) {\n const dayNumber = String(day.getDate()).padStart(2, '0')\n return (\n <button\n type=\"button\"\n onClick={onSelect}\n aria-pressed={isActive}\n aria-label={`${dayName} ${dayNumber}`}\n className={cn(\n 'flex h-[104px] w-[101px] flex-col items-center gap-[6px] overflow-hidden rounded-[10px] border p-[12px] transition-colors',\n isActive\n ? 'border-transparent bg-foreground'\n : 'border-border bg-card hover:border-foreground/40',\n )}\n >\n <span className=\"text-[11px] font-medium leading-none tracking-[0.44px] text-muted-foreground\">\n {dayName}\n </span>\n <div className=\"flex items-center gap-[5px]\">\n <span\n className={cn(\n 'text-2xl font-semibold leading-7',\n isActive ? 'text-background' : 'text-foreground',\n )}\n >\n {dayNumber}\n </span>\n {isToday ? (\n <span\n className=\"inline-block size-1.5 rounded-full bg-status-info-icon\"\n aria-hidden\n />\n ) : null}\n </div>\n <div className=\"flex h-4 w-[82px] items-end gap-[1.5px]\">\n {busyness.slots.map((state, index) => (\n <BusySlot key={index} state={state} active={isActive} />\n ))}\n </div>\n <span className=\"text-[11px] leading-[14px] font-normal whitespace-nowrap text-muted-foreground\">\n {label}\n </span>\n </button>\n )\n}\n\nfunction BusySlot({ state, active }: { state: SlotState; active: boolean }) {\n const heightClass = state === 'empty'\n ? 'h-0.5'\n : state === 'partial'\n ? 'h-2'\n : 'h-3.5'\n let bgClass: string\n if (state === 'conflict') {\n bgClass = 'bg-status-error-icon'\n } else if (active) {\n if (state === 'empty') bgClass = 'bg-background/30'\n else if (state === 'partial') bgClass = 'bg-background/60'\n else bgClass = 'bg-background'\n } else {\n if (state === 'empty') bgClass = 'bg-border'\n else if (state === 'partial') bgClass = 'bg-muted-foreground'\n else bgClass = 'bg-foreground'\n }\n return <div className={cn('w-[7px] shrink-0 rounded-[1.5px]', heightClass, bgClass)} aria-hidden />\n}\n\nexport default ActivitiesDayStrip\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -69,6 +69,15 @@ function resolveGroupExpression(registry, entityType, groupBy) {
69
69
  }
70
70
  return { expression: groupMapping.dbColumn, dbColumn: groupMapping.dbColumn, jsonPath: null };
71
71
  }
72
+ function normalizeSetFilterValues(value) {
73
+ const values = Array.isArray(value) ? value : value === void 0 ? [] : [value];
74
+ if (values.some((member) => member === null || member === void 0)) {
75
+ throw new Error(
76
+ "[internal] Set filter members must not be null or undefined \u2014 use the is_null / is_not_null operators to filter on nullness"
77
+ );
78
+ }
79
+ return values;
80
+ }
72
81
  function buildWhereClause(options) {
73
82
  const { registry } = options;
74
83
  const params = [];
@@ -119,13 +128,18 @@ function buildWhereClause(options) {
119
128
  params.push(filter.value);
120
129
  break;
121
130
  case "in":
122
- whereClauses.push(`${filterMapping.dbColumn} = ANY(?)`);
123
- params.push(filter.value);
124
- break;
125
- case "not_in":
126
- whereClauses.push(`${filterMapping.dbColumn} != ALL(?)`);
127
- params.push(filter.value);
131
+ case "not_in": {
132
+ const values = normalizeSetFilterValues(filter.value);
133
+ if (values.length === 0) {
134
+ whereClauses.push(filter.operator === "in" ? "FALSE" : "TRUE");
135
+ break;
136
+ }
137
+ const keyword = filter.operator === "in" ? "IN" : "NOT IN";
138
+ const placeholders = values.map(() => "?").join(", ");
139
+ whereClauses.push(`${filterMapping.dbColumn} ${keyword} (${placeholders})`);
140
+ params.push(...values);
128
141
  break;
142
+ }
129
143
  case "is_null":
130
144
  whereClauses.push(`${filterMapping.dbColumn} IS NULL`);
131
145
  break;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/dashboards/lib/aggregations.ts"],
4
- "sourcesContent": ["import type { AnalyticsRegistry } from '../services/analyticsRegistry'\nimport type { AnalyticsEntityTypeConfig, AnalyticsFieldMapping } from '@open-mercato/shared/modules/analytics'\n\nexport type AggregateFunction = 'count' | 'sum' | 'avg' | 'min' | 'max'\nexport type DateGranularity = 'day' | 'week' | 'month' | 'quarter' | 'year'\n\nconst VALID_GRANULARITIES: readonly DateGranularity[] = ['day', 'week', 'month', 'quarter', 'year']\nconst VALID_AGGREGATES: readonly AggregateFunction[] = ['count', 'sum', 'avg', 'min', 'max']\nconst SAFE_IDENTIFIER_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/\n\nexport function isValidGranularity(value: unknown): value is DateGranularity {\n return typeof value === 'string' && VALID_GRANULARITIES.includes(value as DateGranularity)\n}\n\nexport function isValidAggregate(value: unknown): value is AggregateFunction {\n return typeof value === 'string' && VALID_AGGREGATES.includes(value as AggregateFunction)\n}\n\nfunction isSafeIdentifier(value: string): boolean {\n return SAFE_IDENTIFIER_PATTERN.test(value)\n}\n\n// Re-export types from shared module for convenience\nexport type EntityTypeConfig = AnalyticsEntityTypeConfig\nexport type FieldMapping = AnalyticsFieldMapping\n\nexport function buildAggregateExpression(aggregate: AggregateFunction, column: string): string {\n switch (aggregate) {\n case 'count':\n return column === 'id' ? 'COUNT(*)' : `COUNT(${column})`\n case 'sum':\n return `COALESCE(SUM(${column}::numeric), 0)`\n case 'avg':\n return `COALESCE(AVG(${column}::numeric), 0)`\n case 'min':\n return `MIN(${column}::numeric)`\n case 'max':\n return `MAX(${column}::numeric)`\n default:\n return `COUNT(*)`\n }\n}\n\nexport function buildDateTruncExpression(column: string, granularity: DateGranularity): string {\n if (!isValidGranularity(granularity)) {\n throw new Error(`Invalid granularity: ${granularity}`)\n }\n return `DATE_TRUNC('${granularity}', ${column})`\n}\n\nexport function buildJsonbFieldExpression(column: string, path: string): string {\n const parts = path.split('.')\n for (const part of parts) {\n if (!isSafeIdentifier(part)) {\n throw new Error(`Invalid JSONB path part: ${part}`)\n }\n }\n if (parts.length === 1) {\n return `${column}->>'${parts[0]}'`\n }\n const intermediate = parts.slice(0, -1).map((p) => `'${p}'`).join('->')\n const lastPart = parts[parts.length - 1]\n return `${column}->${intermediate}->>'${lastPart}'`\n}\n\nexport type AggregationQuery = {\n sql: string\n params: unknown[]\n}\n\nexport type BuildAggregationQueryOptions = {\n entityType: string\n metric: {\n field: string\n aggregate: AggregateFunction\n }\n groupBy?: {\n field: string\n granularity?: DateGranularity\n limit?: number\n }\n dateRange?: {\n field: string\n start: Date\n end: Date\n }\n filters?: Array<{\n field: string\n operator: 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'not_in' | 'is_null' | 'is_not_null'\n value?: unknown\n }>\n scope: {\n tenantId: string\n organizationIds?: string[]\n }\n /** Analytics registry for resolving entity and field configurations */\n registry: AnalyticsRegistry\n}\n\nexport type ResolvedGroupExpression = {\n expression: string\n /** Column the group expression reads from \u2014 the encryption map is keyed by this. */\n dbColumn: string\n /** JSONB path below `dbColumn`, when the groupBy field used path notation. */\n jsonPath: string | null\n}\n\n/**\n * Resolves the SQL expression a groupBy field maps to, together with the underlying column so\n * callers can decide whether the source is encrypted at rest before grouping over it (#4622).\n */\nexport function resolveGroupExpression(\n registry: AnalyticsRegistry,\n entityType: string,\n groupBy: { field: string; granularity?: DateGranularity },\n): ResolvedGroupExpression | null {\n const groupMapping = registry.getFieldMapping(entityType, groupBy.field)\n\n // Handle JSONB path notation (e.g., shippingAddressSnapshot.region)\n if (!groupMapping && groupBy.field.includes('.')) {\n const [baseField, ...pathParts] = groupBy.field.split('.')\n const baseMapping = registry.getFieldMapping(entityType, baseField)\n if (baseMapping?.type !== 'jsonb') return null\n const jsonPath = pathParts.join('.')\n return {\n expression: buildJsonbFieldExpression(baseMapping.dbColumn, jsonPath),\n dbColumn: baseMapping.dbColumn,\n jsonPath,\n }\n }\n\n if (!groupMapping) return null\n\n if (groupMapping.type === 'timestamp' && groupBy.granularity) {\n return {\n expression: buildDateTruncExpression(groupMapping.dbColumn, groupBy.granularity),\n dbColumn: groupMapping.dbColumn,\n jsonPath: null,\n }\n }\n\n return { expression: groupMapping.dbColumn, dbColumn: groupMapping.dbColumn, jsonPath: null }\n}\n\nfunction buildWhereClause(\n options: Pick<BuildAggregationQueryOptions, 'entityType' | 'dateRange' | 'filters' | 'scope' | 'registry'>,\n): { clause: string; params: unknown[] } {\n const { registry } = options\n const params: unknown[] = []\n const whereClauses: string[] = []\n\n whereClauses.push(`tenant_id = ?`)\n params.push(options.scope.tenantId)\n\n if (options.scope.organizationIds && options.scope.organizationIds.length > 0) {\n whereClauses.push(`organization_id = ANY(?::uuid[])`)\n params.push(`{${options.scope.organizationIds.join(',')}}`)\n }\n\n whereClauses.push(`deleted_at IS NULL`)\n\n if (options.dateRange) {\n const dateMapping = registry.getFieldMapping(options.entityType, options.dateRange.field)\n if (dateMapping) {\n whereClauses.push(`${dateMapping.dbColumn} >= ?`)\n params.push(options.dateRange.start)\n whereClauses.push(`${dateMapping.dbColumn} <= ?`)\n params.push(options.dateRange.end)\n }\n }\n\n if (options.filters) {\n for (const filter of options.filters) {\n const filterMapping = registry.getFieldMapping(options.entityType, filter.field)\n if (!filterMapping) continue\n\n switch (filter.operator) {\n case 'eq':\n whereClauses.push(`${filterMapping.dbColumn} = ?`)\n params.push(filter.value)\n break\n case 'neq':\n whereClauses.push(`${filterMapping.dbColumn} != ?`)\n params.push(filter.value)\n break\n case 'gt':\n whereClauses.push(`${filterMapping.dbColumn} > ?`)\n params.push(filter.value)\n break\n case 'gte':\n whereClauses.push(`${filterMapping.dbColumn} >= ?`)\n params.push(filter.value)\n break\n case 'lt':\n whereClauses.push(`${filterMapping.dbColumn} < ?`)\n params.push(filter.value)\n break\n case 'lte':\n whereClauses.push(`${filterMapping.dbColumn} <= ?`)\n params.push(filter.value)\n break\n case 'in':\n whereClauses.push(`${filterMapping.dbColumn} = ANY(?)`)\n params.push(filter.value)\n break\n case 'not_in':\n whereClauses.push(`${filterMapping.dbColumn} != ALL(?)`)\n params.push(filter.value)\n break\n case 'is_null':\n whereClauses.push(`${filterMapping.dbColumn} IS NULL`)\n break\n case 'is_not_null':\n whereClauses.push(`${filterMapping.dbColumn} IS NOT NULL`)\n break\n }\n }\n }\n\n return { clause: whereClauses.length > 0 ? `WHERE ${whereClauses.join(' AND ')}` : '', params }\n}\n\nfunction resolveTableName(config: EntityTypeConfig): string {\n return config.schema ? `\"${config.schema}\".\"${config.tableName}\"` : `\"${config.tableName}\"`\n}\n\nexport type BuildDistinctCurrencyQueryOptions = Pick<\n BuildAggregationQueryOptions,\n 'entityType' | 'dateRange' | 'filters' | 'scope' | 'registry'\n>\n\n/**\n * Builds the query that reads the distinct per-row currencies of the rows an aggregation\n * would sum, over exactly the same tenant/organization scope, date range and filters.\n * Returns `null` when the entity declares no `currencyField`, which means its amounts are\n * not per-row denominated and there is nothing to verify (#4676).\n */\nexport function buildDistinctCurrencyQuery(\n options: BuildDistinctCurrencyQueryOptions,\n): AggregationQuery | null {\n const { registry } = options\n const config = registry.getEntityTypeConfig(options.entityType)\n if (!config?.currencyField) return null\n\n const currencyMapping = registry.getFieldMapping(options.entityType, config.currencyField)\n if (!currencyMapping || !isSafeIdentifier(currencyMapping.dbColumn)) return null\n\n const where = buildWhereClause(options)\n const normalizedCurrencyExpression = `UPPER(NULLIF(BTRIM(${currencyMapping.dbColumn}), ''))`\n const sql = [\n `SELECT DISTINCT ${normalizedCurrencyExpression} AS code`,\n `FROM ${resolveTableName(config)}`,\n where.clause,\n 'LIMIT 2',\n ]\n .filter(Boolean)\n .join(' ')\n\n return { sql, params: where.params }\n}\n\nexport function buildAggregationQuery(options: BuildAggregationQueryOptions): AggregationQuery | null {\n const { registry } = options\n const config = registry.getEntityTypeConfig(options.entityType)\n if (!config) return null\n\n const metricMapping = registry.getFieldMapping(options.entityType, options.metric.field)\n if (!metricMapping) return null\n\n const tableName = resolveTableName(config)\n const aggregateExpr = buildAggregateExpression(options.metric.aggregate, metricMapping.dbColumn)\n\n let selectClause = `SELECT ${aggregateExpr} AS value`\n let groupByClause = ''\n let orderByClause = ''\n let limitClause = ''\n\n if (options.groupBy) {\n const resolved = resolveGroupExpression(registry, options.entityType, options.groupBy)\n\n if (resolved) {\n selectClause = `SELECT ${resolved.expression} AS group_key, ${aggregateExpr} AS value`\n groupByClause = `GROUP BY ${resolved.expression}`\n // NULLS LAST is stated explicitly (PostgreSQL defaults DESC to NULLS FIRST) so a group limit\n // keeps the highest-value buckets instead of the empty ones, and so the application-side\n // encrypted path can mirror the same ordering (#4622).\n orderByClause = `ORDER BY value DESC NULLS LAST`\n\n if (options.groupBy.limit && options.groupBy.limit > 0) {\n limitClause = `LIMIT ${Math.min(options.groupBy.limit, 100)}`\n }\n }\n }\n\n const where = buildWhereClause(options)\n\n const sql = [selectClause, `FROM ${tableName}`, where.clause, groupByClause, orderByClause, limitClause]\n .filter(Boolean)\n .join(' ')\n\n return { sql, params: where.params }\n}\n\nexport type BuildGroupSourceRowsQueryOptions = Omit<BuildAggregationQueryOptions, 'groupBy'> & {\n /** Column holding the (encrypted) group source, resolved via `resolveGroupExpression`. */\n groupColumn: string\n /** Hard cap on scanned rows; callers fetch `rowLimit + 1` to detect overflow. */\n rowLimit: number\n}\n\n/**\n * Builds a per-row query for group sources that cannot be grouped in SQL because the column is\n * encrypted at rest. Returns the raw group source alongside the metric value so the caller can\n * decrypt, then aggregate in application code (#4622).\n */\nexport function buildGroupSourceRowsQuery(options: BuildGroupSourceRowsQueryOptions): AggregationQuery | null {\n const { registry } = options\n const config = registry.getEntityTypeConfig(options.entityType)\n if (!config) return null\n\n const metricMapping = registry.getFieldMapping(options.entityType, options.metric.field)\n if (!metricMapping) return null\n\n if (!isSafeIdentifier(options.groupColumn)) {\n throw new Error(`Invalid group column: ${options.groupColumn}`)\n }\n\n const where = buildWhereClause(options)\n const sql = [\n `SELECT ${options.groupColumn} AS group_source, ${metricMapping.dbColumn} AS metric_value`,\n `FROM ${resolveTableName(config)}`,\n where.clause,\n `LIMIT ${Math.max(1, Math.floor(options.rowLimit)) + 1}`,\n ]\n .filter(Boolean)\n .join(' ')\n\n return { sql, params: where.params }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { AnalyticsRegistry } from '../services/analyticsRegistry'\nimport type { AnalyticsEntityTypeConfig, AnalyticsFieldMapping } from '@open-mercato/shared/modules/analytics'\n\nexport type AggregateFunction = 'count' | 'sum' | 'avg' | 'min' | 'max'\nexport type DateGranularity = 'day' | 'week' | 'month' | 'quarter' | 'year'\n\nconst VALID_GRANULARITIES: readonly DateGranularity[] = ['day', 'week', 'month', 'quarter', 'year']\nconst VALID_AGGREGATES: readonly AggregateFunction[] = ['count', 'sum', 'avg', 'min', 'max']\nconst SAFE_IDENTIFIER_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/\n\nexport function isValidGranularity(value: unknown): value is DateGranularity {\n return typeof value === 'string' && VALID_GRANULARITIES.includes(value as DateGranularity)\n}\n\nexport function isValidAggregate(value: unknown): value is AggregateFunction {\n return typeof value === 'string' && VALID_AGGREGATES.includes(value as AggregateFunction)\n}\n\nfunction isSafeIdentifier(value: string): boolean {\n return SAFE_IDENTIFIER_PATTERN.test(value)\n}\n\n// Re-export types from shared module for convenience\nexport type EntityTypeConfig = AnalyticsEntityTypeConfig\nexport type FieldMapping = AnalyticsFieldMapping\n\nexport function buildAggregateExpression(aggregate: AggregateFunction, column: string): string {\n switch (aggregate) {\n case 'count':\n return column === 'id' ? 'COUNT(*)' : `COUNT(${column})`\n case 'sum':\n return `COALESCE(SUM(${column}::numeric), 0)`\n case 'avg':\n return `COALESCE(AVG(${column}::numeric), 0)`\n case 'min':\n return `MIN(${column}::numeric)`\n case 'max':\n return `MAX(${column}::numeric)`\n default:\n return `COUNT(*)`\n }\n}\n\nexport function buildDateTruncExpression(column: string, granularity: DateGranularity): string {\n if (!isValidGranularity(granularity)) {\n throw new Error(`Invalid granularity: ${granularity}`)\n }\n return `DATE_TRUNC('${granularity}', ${column})`\n}\n\nexport function buildJsonbFieldExpression(column: string, path: string): string {\n const parts = path.split('.')\n for (const part of parts) {\n if (!isSafeIdentifier(part)) {\n throw new Error(`Invalid JSONB path part: ${part}`)\n }\n }\n if (parts.length === 1) {\n return `${column}->>'${parts[0]}'`\n }\n const intermediate = parts.slice(0, -1).map((p) => `'${p}'`).join('->')\n const lastPart = parts[parts.length - 1]\n return `${column}->${intermediate}->>'${lastPart}'`\n}\n\nexport type AggregationQuery = {\n sql: string\n params: unknown[]\n}\n\nexport type BuildAggregationQueryOptions = {\n entityType: string\n metric: {\n field: string\n aggregate: AggregateFunction\n }\n groupBy?: {\n field: string\n granularity?: DateGranularity\n limit?: number\n }\n dateRange?: {\n field: string\n start: Date\n end: Date\n }\n filters?: Array<{\n field: string\n operator: 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'not_in' | 'is_null' | 'is_not_null'\n value?: unknown\n }>\n scope: {\n tenantId: string\n organizationIds?: string[]\n }\n /** Analytics registry for resolving entity and field configurations */\n registry: AnalyticsRegistry\n}\n\nexport type ResolvedGroupExpression = {\n expression: string\n /** Column the group expression reads from \u2014 the encryption map is keyed by this. */\n dbColumn: string\n /** JSONB path below `dbColumn`, when the groupBy field used path notation. */\n jsonPath: string | null\n}\n\n/**\n * Resolves the SQL expression a groupBy field maps to, together with the underlying column so\n * callers can decide whether the source is encrypted at rest before grouping over it (#4622).\n */\nexport function resolveGroupExpression(\n registry: AnalyticsRegistry,\n entityType: string,\n groupBy: { field: string; granularity?: DateGranularity },\n): ResolvedGroupExpression | null {\n const groupMapping = registry.getFieldMapping(entityType, groupBy.field)\n\n // Handle JSONB path notation (e.g., shippingAddressSnapshot.region)\n if (!groupMapping && groupBy.field.includes('.')) {\n const [baseField, ...pathParts] = groupBy.field.split('.')\n const baseMapping = registry.getFieldMapping(entityType, baseField)\n if (baseMapping?.type !== 'jsonb') return null\n const jsonPath = pathParts.join('.')\n return {\n expression: buildJsonbFieldExpression(baseMapping.dbColumn, jsonPath),\n dbColumn: baseMapping.dbColumn,\n jsonPath,\n }\n }\n\n if (!groupMapping) return null\n\n if (groupMapping.type === 'timestamp' && groupBy.granularity) {\n return {\n expression: buildDateTruncExpression(groupMapping.dbColumn, groupBy.granularity),\n dbColumn: groupMapping.dbColumn,\n jsonPath: null,\n }\n }\n\n return { expression: groupMapping.dbColumn, dbColumn: groupMapping.dbColumn, jsonPath: null }\n}\n\n/**\n * Normalizes the value of a set filter (`in` / `not_in`) into the list of members it selects.\n *\n * The list is rendered as one placeholder per member rather than as a single array parameter:\n * MikroORM does not bind parameters at the driver level \u2014 `AbstractSqlConnection.execute` calls\n * `platform.formatQuery`, which interpolates each value through `BasePostgreSqlPlatform.escape`,\n * and that renders a JavaScript array as a bare comma-separated list. The previous\n * `= ANY(?)` / `!= ALL(?)` form therefore produced `= ANY('a', 'b')`, which PostgreSQL rejects\n * for every shape of value (#4669).\n *\n * A missing value yields an empty set rather than a dropped filter, so an `in` filter the caller\n * failed to populate selects nothing instead of silently widening the result.\n *\n * A null or undefined *member* is refused instead of rendered. `column IN (NULL)` matches no row\n * and `column NOT IN ('a', NULL)` is NULL for every row, so such a member would return an empty\n * aggregation with no error \u2014 a silent zero on a reporting surface, which is worse than a loud\n * failure. Requests are already rejected at the API boundary by `widgetDataFilterSchema` in\n * `api/widgets/data/schema.ts`; this throw keeps in-process callers, whose filter values are\n * typed as `unknown`, from reaching the same silent path. Nullness is queried with the dedicated\n * `is_null` / `is_not_null` operators.\n */\nfunction normalizeSetFilterValues(value: unknown): unknown[] {\n const values = Array.isArray(value) ? value : value === undefined ? [] : [value]\n if (values.some((member) => member === null || member === undefined)) {\n throw new Error(\n '[internal] Set filter members must not be null or undefined \u2014 use the is_null / is_not_null operators to filter on nullness',\n )\n }\n return values\n}\n\nfunction buildWhereClause(\n options: Pick<BuildAggregationQueryOptions, 'entityType' | 'dateRange' | 'filters' | 'scope' | 'registry'>,\n): { clause: string; params: unknown[] } {\n const { registry } = options\n const params: unknown[] = []\n const whereClauses: string[] = []\n\n whereClauses.push(`tenant_id = ?`)\n params.push(options.scope.tenantId)\n\n if (options.scope.organizationIds && options.scope.organizationIds.length > 0) {\n whereClauses.push(`organization_id = ANY(?::uuid[])`)\n params.push(`{${options.scope.organizationIds.join(',')}}`)\n }\n\n whereClauses.push(`deleted_at IS NULL`)\n\n if (options.dateRange) {\n const dateMapping = registry.getFieldMapping(options.entityType, options.dateRange.field)\n if (dateMapping) {\n whereClauses.push(`${dateMapping.dbColumn} >= ?`)\n params.push(options.dateRange.start)\n whereClauses.push(`${dateMapping.dbColumn} <= ?`)\n params.push(options.dateRange.end)\n }\n }\n\n if (options.filters) {\n for (const filter of options.filters) {\n const filterMapping = registry.getFieldMapping(options.entityType, filter.field)\n if (!filterMapping) continue\n\n switch (filter.operator) {\n case 'eq':\n whereClauses.push(`${filterMapping.dbColumn} = ?`)\n params.push(filter.value)\n break\n case 'neq':\n whereClauses.push(`${filterMapping.dbColumn} != ?`)\n params.push(filter.value)\n break\n case 'gt':\n whereClauses.push(`${filterMapping.dbColumn} > ?`)\n params.push(filter.value)\n break\n case 'gte':\n whereClauses.push(`${filterMapping.dbColumn} >= ?`)\n params.push(filter.value)\n break\n case 'lt':\n whereClauses.push(`${filterMapping.dbColumn} < ?`)\n params.push(filter.value)\n break\n case 'lte':\n whereClauses.push(`${filterMapping.dbColumn} <= ?`)\n params.push(filter.value)\n break\n case 'in':\n case 'not_in': {\n const values = normalizeSetFilterValues(filter.value)\n if (values.length === 0) {\n whereClauses.push(filter.operator === 'in' ? 'FALSE' : 'TRUE')\n break\n }\n const keyword = filter.operator === 'in' ? 'IN' : 'NOT IN'\n const placeholders = values.map(() => '?').join(', ')\n whereClauses.push(`${filterMapping.dbColumn} ${keyword} (${placeholders})`)\n params.push(...values)\n break\n }\n case 'is_null':\n whereClauses.push(`${filterMapping.dbColumn} IS NULL`)\n break\n case 'is_not_null':\n whereClauses.push(`${filterMapping.dbColumn} IS NOT NULL`)\n break\n }\n }\n }\n\n return { clause: whereClauses.length > 0 ? `WHERE ${whereClauses.join(' AND ')}` : '', params }\n}\n\nfunction resolveTableName(config: EntityTypeConfig): string {\n return config.schema ? `\"${config.schema}\".\"${config.tableName}\"` : `\"${config.tableName}\"`\n}\n\nexport type BuildDistinctCurrencyQueryOptions = Pick<\n BuildAggregationQueryOptions,\n 'entityType' | 'dateRange' | 'filters' | 'scope' | 'registry'\n>\n\n/**\n * Builds the query that reads the distinct per-row currencies of the rows an aggregation\n * would sum, over exactly the same tenant/organization scope, date range and filters.\n * Returns `null` when the entity declares no `currencyField`, which means its amounts are\n * not per-row denominated and there is nothing to verify (#4676).\n */\nexport function buildDistinctCurrencyQuery(\n options: BuildDistinctCurrencyQueryOptions,\n): AggregationQuery | null {\n const { registry } = options\n const config = registry.getEntityTypeConfig(options.entityType)\n if (!config?.currencyField) return null\n\n const currencyMapping = registry.getFieldMapping(options.entityType, config.currencyField)\n if (!currencyMapping || !isSafeIdentifier(currencyMapping.dbColumn)) return null\n\n const where = buildWhereClause(options)\n const normalizedCurrencyExpression = `UPPER(NULLIF(BTRIM(${currencyMapping.dbColumn}), ''))`\n const sql = [\n `SELECT DISTINCT ${normalizedCurrencyExpression} AS code`,\n `FROM ${resolveTableName(config)}`,\n where.clause,\n 'LIMIT 2',\n ]\n .filter(Boolean)\n .join(' ')\n\n return { sql, params: where.params }\n}\n\nexport function buildAggregationQuery(options: BuildAggregationQueryOptions): AggregationQuery | null {\n const { registry } = options\n const config = registry.getEntityTypeConfig(options.entityType)\n if (!config) return null\n\n const metricMapping = registry.getFieldMapping(options.entityType, options.metric.field)\n if (!metricMapping) return null\n\n const tableName = resolveTableName(config)\n const aggregateExpr = buildAggregateExpression(options.metric.aggregate, metricMapping.dbColumn)\n\n let selectClause = `SELECT ${aggregateExpr} AS value`\n let groupByClause = ''\n let orderByClause = ''\n let limitClause = ''\n\n if (options.groupBy) {\n const resolved = resolveGroupExpression(registry, options.entityType, options.groupBy)\n\n if (resolved) {\n selectClause = `SELECT ${resolved.expression} AS group_key, ${aggregateExpr} AS value`\n groupByClause = `GROUP BY ${resolved.expression}`\n // NULLS LAST is stated explicitly (PostgreSQL defaults DESC to NULLS FIRST) so a group limit\n // keeps the highest-value buckets instead of the empty ones, and so the application-side\n // encrypted path can mirror the same ordering (#4622).\n orderByClause = `ORDER BY value DESC NULLS LAST`\n\n if (options.groupBy.limit && options.groupBy.limit > 0) {\n limitClause = `LIMIT ${Math.min(options.groupBy.limit, 100)}`\n }\n }\n }\n\n const where = buildWhereClause(options)\n\n const sql = [selectClause, `FROM ${tableName}`, where.clause, groupByClause, orderByClause, limitClause]\n .filter(Boolean)\n .join(' ')\n\n return { sql, params: where.params }\n}\n\nexport type BuildGroupSourceRowsQueryOptions = Omit<BuildAggregationQueryOptions, 'groupBy'> & {\n /** Column holding the (encrypted) group source, resolved via `resolveGroupExpression`. */\n groupColumn: string\n /** Hard cap on scanned rows; callers fetch `rowLimit + 1` to detect overflow. */\n rowLimit: number\n}\n\n/**\n * Builds a per-row query for group sources that cannot be grouped in SQL because the column is\n * encrypted at rest. Returns the raw group source alongside the metric value so the caller can\n * decrypt, then aggregate in application code (#4622).\n */\nexport function buildGroupSourceRowsQuery(options: BuildGroupSourceRowsQueryOptions): AggregationQuery | null {\n const { registry } = options\n const config = registry.getEntityTypeConfig(options.entityType)\n if (!config) return null\n\n const metricMapping = registry.getFieldMapping(options.entityType, options.metric.field)\n if (!metricMapping) return null\n\n if (!isSafeIdentifier(options.groupColumn)) {\n throw new Error(`Invalid group column: ${options.groupColumn}`)\n }\n\n const where = buildWhereClause(options)\n const sql = [\n `SELECT ${options.groupColumn} AS group_source, ${metricMapping.dbColumn} AS metric_value`,\n `FROM ${resolveTableName(config)}`,\n where.clause,\n `LIMIT ${Math.max(1, Math.floor(options.rowLimit)) + 1}`,\n ]\n .filter(Boolean)\n .join(' ')\n\n return { sql, params: where.params }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -384,6 +384,7 @@ async function GET(req) {
384
384
  label: d.configJson?.label || d.key,
385
385
  description: d.configJson?.description || void 0,
386
386
  multi: Boolean(d.configJson?.multi),
387
+ relatedEntityId: d.kind === "relation" && typeof d.configJson?.relatedEntityId === "string" ? d.configJson.relatedEntityId.trim() || void 0 : void 0,
387
388
  options: (() => {
388
389
  if (d.kind === "currency") return void 0;
389
390
  const normalizedOptions = normalizeCustomFieldOptions(d.configJson?.options);
@@ -630,6 +631,7 @@ const customFieldDefinitionSchema = z.object({
630
631
  label: z.string(),
631
632
  description: z.string().optional(),
632
633
  multi: z.boolean().optional(),
634
+ relatedEntityId: z.string().optional(),
633
635
  options: z.array(customFieldOptionValueSchema).optional(),
634
636
  optionsUrl: z.string().optional(),
635
637
  filterable: z.boolean().optional(),
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/entities/api/definitions.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { CacheStrategy } from '@open-mercato/cache'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { CustomEntity, CustomFieldDef } from '@open-mercato/core/modules/entities/data/entities'\nimport { upsertCustomFieldDefSchema, fieldsetCodeRegex } from '@open-mercato/core/modules/entities/data/validators'\nimport {\n createDefinitionsCacheKey,\n createDefinitionsCacheTags,\n invalidateDefinitionsCache,\n ENTITY_DEFINITIONS_CACHE_TTL_MS,\n} from './definitions.cache'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { filterSelectableSystemEntityIds, isSystemEntitySelectable } from '@open-mercato/shared/lib/entities/system-entities'\nimport { isOrmBackedSystemEntityId } from '@open-mercato/shared/lib/data/engine'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { loadEntityFieldsetConfigs, CustomFieldsetDefinition } from '../lib/fieldsets'\nimport { installCustomEntitiesFromModules } from '../lib/install-from-ce'\nimport { normalizeCustomFieldOptions } from '@open-mercato/shared/modules/entities/options'\nimport { CURRENCY_OPTIONS_URL } from '@open-mercato/shared/modules/entities/kinds'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport {\n beginEntitiesMutationGuard,\n FIELD_DEFINITION_RESOURCE_KIND,\n} from './definitions.mutation-guard'\nimport {\n canReadAllEntityMetadata,\n canReadEntityMetadata,\n getDeclaredCustomEntityRestriction,\n resolveEntityAclRequirement,\n} from '../lib/entityAcl'\nimport {\n createExactDefinitionWhere,\n createScopedDefinitionTombstone,\n createVisibleDefinitionWhere,\n markDefinitionTombstoned,\n resolveDefinitionScopeFromOrganizationScope,\n resolveDefinitionMutationScope,\n selectVisibleDefinitionWinner,\n} from '../lib/definition-scope'\nimport { resolveEntityDefinitionsVersion } from '../lib/definitions-version'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('entities').child({ component: 'definitions' })\n\n/**\n * Validate defaultValue against the field kind. Returns an error message string\n * if invalid, or null if valid. For dictionary and currency kinds, queries the\n * database to verify the token exists.\n */\nasync function validateDefaultValueByKind(\n value: unknown,\n kind: string,\n cfg: Record<string, unknown>,\n em: EntityManager,\n tenantContext: { tenantId: string | null; organizationId: string | null },\n): Promise<string | null> {\n switch (kind) {\n case 'boolean':\n if (typeof value !== 'boolean') return 'defaultValue for boolean fields must be true or false'\n return null\n case 'integer':\n if (typeof value !== 'number' || !Number.isInteger(value))\n return 'defaultValue for integer fields must be a whole number'\n return null\n case 'float':\n if (typeof value !== 'number' || !isFinite(value))\n return 'defaultValue for float fields must be a finite number'\n return null\n case 'text':\n case 'multiline':\n case 'date':\n case 'datetime':\n if (typeof value !== 'string')\n return `defaultValue for ${kind} fields must be a string`\n return null\n case 'dictionary': {\n if (typeof value !== 'string')\n return 'defaultValue for dictionary fields must be a string'\n const dictionaryId = typeof cfg.dictionaryId === 'string' ? cfg.dictionaryId.trim() : ''\n if (dictionaryId) {\n try {\n const { DictionaryEntry } = await import('@open-mercato/core/modules/dictionaries/data/entities')\n const entry = await em.findOne(DictionaryEntry, {\n dictionary: dictionaryId,\n value: value,\n ...(tenantContext.organizationId ? { organizationId: tenantContext.organizationId } : {}),\n ...(tenantContext.tenantId ? { tenantId: tenantContext.tenantId } : {}),\n })\n if (!entry) return `defaultValue \"${value}\" does not match any entry in the configured dictionary`\n } catch (err) {\n // If the dictionaries module is not available, skip entry validation\n logger.debug('Dictionary validation skipped \u2014 module not available', { err })\n }\n }\n return null\n }\n case 'currency': {\n if (typeof value !== 'string')\n return 'defaultValue for currency fields must be a string'\n try {\n const { Currency } = await import('@open-mercato/core/modules/currencies/data/entities')\n const where: Record<string, string> = { code: value as string }\n if (tenantContext.tenantId) where.tenantId = tenantContext.tenantId\n if (tenantContext.organizationId) where.organizationId = tenantContext.organizationId\n const currency = await em.findOne(Currency, where)\n if (!currency) return `defaultValue \"${value}\" does not match any available currency`\n } catch (err) {\n // If the currencies module is not available, skip currency validation\n logger.debug('Currency validation skipped \u2014 module not available', { err })\n }\n return null\n }\n case 'select': {\n if (typeof value !== 'string' && typeof value !== 'number')\n return 'defaultValue for select fields must be a string or number'\n const options = normalizeCustomFieldOptions(\n Array.isArray(cfg.options) ? cfg.options : [],\n )\n if (options.length > 0) {\n const match = options.some(\n (o) => o.value === value || String(o.value) === String(value),\n )\n if (!match) return 'defaultValue does not match any of the configured options'\n }\n return null\n }\n case 'relation':\n case 'attachment':\n return `defaultValue is not supported for ${kind} fields`\n default:\n return null\n }\n}\n\n/**\n * Normalize defaultValue to the canonical storage shape for the given kind.\n */\nfunction normalizeDefaultValueByKind(value: unknown, kind: string): unknown {\n switch (kind) {\n case 'boolean':\n return value === true\n case 'integer':\n return Math.round(Number(value))\n case 'float':\n return Number(value)\n default:\n return value\n }\n}\n\nexport const metadata = {\n // Reading definitions is needed by record forms; keep it auth-protected but accessible to all authenticated users\n GET: { requireAuth: true },\n // Mutations remain admin-only\n POST: { requireAuth: true, requireFeatures: ['entities.definitions.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['entities.definitions.manage'] },\n}\n\nfunction parseEntityIds(url: URL): string[] {\n const direct = url.searchParams.getAll('entityId').filter((id) => typeof id === 'string' && id.trim().length > 0)\n const combined = url.searchParams.get('entityIds')\n if (combined) {\n combined\n .split(',')\n .map((id) => id.trim())\n .filter((id) => id.length > 0)\n .forEach((id) => direct.push(id))\n }\n const unique: string[] = []\n const seen = new Set<string>()\n for (const id of direct) {\n if (seen.has(id)) continue\n seen.add(id)\n unique.push(id)\n }\n return unique\n}\n\nasync function resolveEntityDefaultEditor(em: any, entityId: string, tenantId: string | null | undefined): Promise<string | undefined> {\n try {\n const ent = await em.findOne(CustomEntity, {\n entityId,\n $and: [\n { $or: [ { tenantId: tenantId ?? undefined as any }, { tenantId: null } ] },\n ],\n } as any)\n if (ent && typeof (ent as any).defaultEditor === 'string') return (ent as any).defaultEditor\n } catch {}\n return undefined\n}\n\nfunction normalizeFieldGroup(raw: unknown): { code: string; title?: string; hint?: string } | undefined {\n if (!raw) return undefined\n if (typeof raw === 'string') {\n const code = raw.trim()\n return code ? { code } : undefined\n }\n if (typeof raw !== 'object') return undefined\n const entry = raw as Record<string, unknown>\n const code = typeof entry.code === 'string' ? entry.code.trim() : ''\n if (!code) return undefined\n const group: { code: string; title?: string; hint?: string } = { code }\n if (typeof entry.title === 'string' && entry.title.trim()) group.title = entry.title.trim()\n if (typeof entry.hint === 'string' && entry.hint.trim()) group.hint = entry.hint.trim()\n return group\n}\n\nfunction definitionMatchesReadScope(\n definition: { tenantId?: string | null; organizationId?: string | null },\n scope: { tenantId: string | null; organizationId: string | null },\n) {\n const definitionTenantId = definition.tenantId ?? null\n const definitionOrganizationId = definition.organizationId ?? null\n const tenantMatches = definitionTenantId === null || definitionTenantId === scope.tenantId\n const organizationMatches =\n definitionOrganizationId === null ||\n (scope.organizationId !== null && definitionOrganizationId === scope.organizationId)\n return tenantMatches && organizationMatches\n}\n\nexport async function GET(req: Request) {\n const url = new URL(req.url)\n const requestedEntityIds = parseEntityIds(url)\n if (!requestedEntityIds.length) {\n return NextResponse.json({ error: 'entityId is required' }, { status: 400 })\n }\n const requestedFieldset = url.searchParams.get('fieldset')\n const fieldsetFilter = requestedFieldset && requestedFieldset.trim().length ? requestedFieldset.trim() : null\n\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const selectableEntityIds = filterSelectableSystemEntityIds(requestedEntityIds)\n if (!selectableEntityIds.length) {\n return NextResponse.json({ items: [] })\n }\n\n const container = await createRequestContainer()\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const definitionScope = resolveDefinitionScopeFromOrganizationScope(auth, scope)\n const tenantId = definitionScope.tenantId\n if (!tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n const organizationId = definitionScope.organizationId\n const { resolve } = container\n const em = resolve('em') as any\n const rbac = resolve('rbacService') as RbacService\n const acl = await rbac.loadAcl(auth.sub ?? '', {\n tenantId,\n organizationId,\n })\n const designerCanRead = canReadAllEntityMetadata(acl)\n const customEntityRestrictions = new Map<string, boolean>()\n for (const entityId of selectableEntityIds) {\n if (isOrmBackedSystemEntityId(em, entityId)) continue\n const declaredRestriction = getDeclaredCustomEntityRestriction(entityId)\n if (declaredRestriction !== undefined) customEntityRestrictions.set(entityId, declaredRestriction)\n }\n if (!designerCanRead) {\n const unresolvedEntityIds = selectableEntityIds.filter(\n (entityId) => !resolveEntityAclRequirement(entityId) && !customEntityRestrictions.has(entityId),\n )\n if (unresolvedEntityIds.length) {\n const registrations = await em.find(CustomEntity as any, {\n entityId: { $in: unresolvedEntityIds },\n isActive: true,\n $and: [\n { $or: [{ tenantId }, { tenantId: null }] },\n { $or: [{ organizationId }, { organizationId: null }] },\n ],\n } as any)\n for (const registration of registrations as any[]) {\n const entityId = String(registration.entityId)\n if (!isOrmBackedSystemEntityId(em, entityId)) {\n customEntityRestrictions.set(entityId, registration.accessRestricted === true)\n }\n }\n }\n }\n const entityIds = selectableEntityIds.filter((entityId) => canReadEntityMetadata({\n entityId,\n isCustomEntity: customEntityRestrictions.has(entityId),\n isRestricted: customEntityRestrictions.get(entityId) === true,\n acl,\n }))\n if (!entityIds.length) {\n return NextResponse.json({ items: [], fieldsetsByEntity: {}, entitySettings: {} })\n }\n let cache: CacheStrategy | undefined\n try {\n cache = resolve('cache') as CacheStrategy\n } catch {}\n\n let canManageDefinitions = false\n if (typeof auth.sub === 'string' && auth.sub.length > 0) {\n try {\n canManageDefinitions = await rbac.userHasAllFeatures(auth.sub, ['entities.definitions.manage'], {\n tenantId,\n organizationId,\n })\n } catch {\n // RBAC service unavailable \u2014 fall back to read-only (no definition sync)\n }\n }\n\n if (canManageDefinitions) {\n try {\n await installCustomEntitiesFromModules(em, cache, {\n tenantIds: [tenantId],\n entityIds,\n includeGlobal: true,\n createOnly: true,\n })\n } catch (err) {\n logger.warn('Failed to synchronize module-backed definitions', {\n tenantId,\n entityIds,\n err,\n })\n }\n }\n\n let cacheKey: string | null = null\n if (cache && !fieldsetFilter) {\n cacheKey = createDefinitionsCacheKey({\n tenantId,\n organizationId,\n entityIds,\n })\n try {\n const cached = await cache.get(cacheKey)\n if (cached) {\n return NextResponse.json(cached)\n }\n } catch (err) {\n logger.warn('Failed to read cache', { err })\n }\n }\n\n const fieldsetConfigs = await loadEntityFieldsetConfigs(em, {\n entityIds,\n tenantId,\n organizationId,\n mode: 'public',\n })\n\n const tenantCandidates = [{ tenantId }, { tenantId: null as string | null }]\n const organizationCandidates = [{ organizationId: null as string | null }]\n if (organizationId) organizationCandidates.unshift({ organizationId })\n const readScope = { tenantId, organizationId }\n\n const whereActive = {\n entityId: { $in: entityIds as any },\n deletedAt: null,\n $and: [\n { $or: tenantCandidates },\n { $or: organizationCandidates },\n ],\n } as any\n const defs = (await em.find(CustomFieldDef, whereActive as any))\n .filter((definition: any) => definitionMatchesReadScope(definition, readScope))\n const tombstones = (await em.find(CustomFieldDef, {\n entityId: { $in: entityIds as any },\n deletedAt: { $ne: null } as any,\n $and: [\n { $or: tenantCandidates },\n { $or: organizationCandidates },\n ],\n } as any)).filter((definition: any) => definitionMatchesReadScope(definition, readScope))\n\n const tombstonedByEntity = new Map<string, Set<string>>()\n for (const entry of tombstones as any[]) {\n const eid = String(entry.entityId)\n if (!tombstonedByEntity.has(eid)) tombstonedByEntity.set(eid, new Set())\n tombstonedByEntity.get(eid)!.add(entry.key)\n }\n\n const scopeScore = (x: any) => (x.tenantId ? 2 : 0) + (x.organizationId ? 1 : 0)\n\n const definitionsByEntity = new Map<string, any[]>()\n for (const d of defs) {\n const eid = String(d.entityId)\n if (!definitionsByEntity.has(eid)) definitionsByEntity.set(eid, [])\n definitionsByEntity.get(eid)!.push(d)\n }\n\n const entityDefaultEditors = new Map<string, string | undefined>()\n for (const entityId of entityIds) {\n const editor = await resolveEntityDefaultEditor(em, entityId, tenantId ?? null)\n entityDefaultEditors.set(entityId, editor)\n }\n\n const items: any[] = []\n\n const entityOrder = new Map<string, number>()\n entityIds.forEach((id, idx) => entityOrder.set(id, idx))\n\n for (const entityId of entityIds) {\n const defsForEntity = definitionsByEntity.get(entityId) ?? []\n if (!defsForEntity.length) continue\n const tombstonedKeys = tombstonedByEntity.get(entityId) ?? new Set<string>()\n const byKey = new Map<string, any>()\n for (const d of defsForEntity) {\n const existing = byKey.get(d.key)\n if (!existing) { byKey.set(d.key, d); continue }\n const sNew = scopeScore(d)\n const sOld = scopeScore(existing)\n if (sNew > sOld) { byKey.set(d.key, d); continue }\n if (sNew < sOld) continue\n const tNew = (d.updatedAt instanceof Date) ? d.updatedAt.getTime() : new Date(d.updatedAt).getTime()\n const tOld = (existing.updatedAt instanceof Date) ? existing.updatedAt.getTime() : new Date(existing.updatedAt).getTime()\n if (tNew >= tOld) byKey.set(d.key, d)\n }\n\n const defaultEditor = entityDefaultEditors.get(entityId)\n const winning = Array.from(byKey.values()).filter((d: any) => (d.isActive !== false) && !tombstonedKeys.has(d.key))\n winning.sort((a: any, b: any) => ((a.configJson?.priority ?? 0) - (b.configJson?.priority ?? 0)))\n\n for (const d of winning) {\n const rawFieldset = typeof d.configJson?.fieldset === 'string' ? d.configJson.fieldset.trim() : ''\n const normalizedFieldset = rawFieldset.length > 0 ? rawFieldset : undefined\n const normalizedFieldsets = Array.isArray(d.configJson?.fieldsets)\n ? d.configJson.fieldsets\n .filter((entry: unknown): entry is string => typeof entry === 'string')\n .map((entry: string) => entry.trim())\n .filter((entry: string) => entry.length > 0)\n : []\n const effectiveFieldsets = normalizedFieldsets.length > 0\n ? normalizedFieldsets\n : (normalizedFieldset ? [normalizedFieldset] : [])\n if (fieldsetFilter && !effectiveFieldsets.includes(fieldsetFilter)) continue\n const groupInfo = normalizeFieldGroup(d.configJson?.group)\n const keyLower = String(d.key).toLowerCase()\n const candidateBase = {\n key: d.key,\n kind: d.kind,\n label: d.configJson?.label || d.key,\n description: d.configJson?.description || undefined,\n multi: Boolean(d.configJson?.multi),\n options: (() => {\n if (d.kind === 'currency') return undefined\n const normalizedOptions = normalizeCustomFieldOptions(d.configJson?.options)\n return normalizedOptions.length ? normalizedOptions : undefined\n })(),\n optionsUrl: (() => {\n if (d.kind === 'currency') return CURRENCY_OPTIONS_URL\n const dictionaryId = typeof d.configJson?.dictionaryId === 'string' ? d.configJson.dictionaryId : undefined\n if (dictionaryId) return `/api/dictionaries/${dictionaryId}/entries`\n return typeof d.configJson?.optionsUrl === 'string' ? d.configJson.optionsUrl : undefined\n })(),\n filterable: Boolean(d.configJson?.filterable),\n formEditable: d.configJson?.formEditable !== undefined ? Boolean(d.configJson.formEditable) : true,\n listVisible: d.configJson?.listVisible !== undefined ? Boolean(d.configJson.listVisible) : true,\n editor: typeof d.configJson?.editor === 'string'\n ? d.configJson.editor\n : (d.kind === 'multiline' ? defaultEditor : undefined),\n input: typeof d.configJson?.input === 'string' ? d.configJson.input : undefined,\n dictionaryId: typeof d.configJson?.dictionaryId === 'string' ? d.configJson.dictionaryId : undefined,\n dictionaryInlineCreate: d.configJson?.dictionaryInlineCreate !== undefined\n ? Boolean(d.configJson.dictionaryInlineCreate)\n : undefined,\n priority: typeof d.configJson?.priority === 'number' ? d.configJson.priority : 0,\n validation: Array.isArray(d.configJson?.validation) ? d.configJson.validation : undefined,\n defaultValue: d.configJson?.defaultValue !== undefined ? d.configJson.defaultValue : undefined,\n // attachments config passthrough\n maxAttachmentSizeMb: typeof d.configJson?.maxAttachmentSizeMb === 'number' ? d.configJson.maxAttachmentSizeMb : undefined,\n acceptExtensions: Array.isArray(d.configJson?.acceptExtensions) ? d.configJson.acceptExtensions : undefined,\n // phone config passthrough\n defaultCountryIso2: typeof d.configJson?.defaultCountryIso2 === 'string' ? d.configJson.defaultCountryIso2 : undefined,\n entityId,\n fieldset: normalizedFieldset ?? effectiveFieldsets[0],\n fieldsets: effectiveFieldsets.length > 0 ? effectiveFieldsets : undefined,\n group: groupInfo,\n } as any\n const metrics = computeDefinitionScore(d, candidateBase, entityOrder.get(entityId) ?? Number.MAX_SAFE_INTEGER)\n const candidate = { ...candidateBase, __score: metrics }\n const existing = (items as any[]).find((entry) => entry.key.toLowerCase() === keyLower)\n if (!existing) {\n items.push(candidate)\n continue\n }\n const existingScore = existing.__score as { base: number; penalty: number; entityIndex: number }\n const candidateScoreInfo = candidate.__score as { base: number; penalty: number; entityIndex: number }\n const better =\n candidateScoreInfo.base > existingScore.base ||\n (candidateScoreInfo.base === existingScore.base && candidateScoreInfo.penalty < existingScore.penalty) ||\n (candidateScoreInfo.base === existingScore.base && candidateScoreInfo.penalty === existingScore.penalty && candidateScoreInfo.entityIndex < existingScore.entityIndex)\n if (better) {\n const index = items.findIndex((entry) => entry.key.toLowerCase() === keyLower)\n if (index >= 0) items[index] = candidate\n }\n }\n }\n\n const sanitized = items.map((item: any) => {\n const { __score, ...rest } = item\n return rest\n })\n sanitized.sort((a: any, b: any) => ((a.priority ?? 0) - (b.priority ?? 0)))\n\n const fieldsetsByEntity: Record<string, CustomFieldsetDefinition[]> = {}\n const entitySettings: Record<string, { singleFieldsetPerRecord: boolean }> = {}\n for (const entityId of entityIds) {\n const cfg = fieldsetConfigs.get(entityId) ?? { fieldsets: [], singleFieldsetPerRecord: true }\n fieldsetsByEntity[entityId] = cfg.fieldsets\n entitySettings[entityId] = { singleFieldsetPerRecord: cfg.singleFieldsetPerRecord }\n }\n\n const responseBody = { items: sanitized, fieldsetsByEntity, entitySettings }\n\n if (cache && cacheKey && !fieldsetFilter) {\n const tags = createDefinitionsCacheTags({\n tenantId,\n organizationId,\n entityIds,\n })\n try {\n await cache.set(cacheKey, responseBody, {\n ttl: ENTITY_DEFINITIONS_CACHE_TTL_MS,\n tags,\n })\n } catch (err) {\n logger.warn('Failed to store cache entry', { err })\n }\n }\n\n return NextResponse.json(responseBody)\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n let body: any\n try { body = await req.json() } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }) }\n const parsed = upsertCustomFieldDefSchema.safeParse(body)\n if (!parsed.success) return NextResponse.json({ error: 'Validation failed', details: parsed.error.flatten() }, { status: 400 })\n const input = parsed.data\n\n if (!isSystemEntitySelectable(input.entityId)) {\n return NextResponse.json({ error: 'Custom fields are not supported for this entity' }, { status: 400 })\n }\n\n if (input.kind === 'dictionary') {\n const dictionaryId = input.configJson?.dictionaryId\n if (typeof dictionaryId !== 'string' || dictionaryId.trim().length === 0) {\n return NextResponse.json({ error: 'dictionaryId is required for dictionary custom fields' }, { status: 400 })\n }\n }\n\n const container = await createRequestContainer()\n const scope = await resolveDefinitionMutationScope({ auth, container, request: req })\n const { resolve } = container\n const em = resolve('em') as any\n let cache: CacheStrategy | undefined\n try {\n cache = resolve('cache') as CacheStrategy\n } catch {}\n\n const where: any = createExactDefinitionWhere(input.entityId, input.key, scope)\n let def = await em.findOne(CustomFieldDef, where)\n\n const guard = await beginEntitiesMutationGuard({\n container,\n auth,\n req,\n resourceKind: FIELD_DEFINITION_RESOURCE_KIND,\n resourceId: def ? def.id : `${input.entityId}:${input.key}`,\n operation: def ? 'update' : 'create',\n mutationPayload: input as unknown as Record<string, unknown>,\n })\n if (guard.blockedResponse) return guard.blockedResponse\n\n if (!def) def = em.create(CustomFieldDef, { ...where, createdAt: new Date() })\n def.kind = input.kind\n const inCfg = (input as any).configJson ?? {}\n const cfg: Record<string, any> = { ...inCfg }\n if (cfg.label == null || String(cfg.label).trim() === '') cfg.label = input.key\n if (cfg.formEditable === undefined) cfg.formEditable = true\n if (cfg.listVisible === undefined) cfg.listVisible = true\n if (input.kind === 'dictionary') {\n const dictionaryId = typeof cfg.dictionaryId === 'string' ? cfg.dictionaryId.trim() : ''\n cfg.dictionaryId = dictionaryId\n cfg.dictionaryInlineCreate = cfg.dictionaryInlineCreate !== false\n }\n if (input.kind === 'currency') {\n cfg.optionsUrl = CURRENCY_OPTIONS_URL\n if (Array.isArray(cfg.options)) delete cfg.options\n }\n if (input.kind === 'multiline' && (cfg.editor == null || String(cfg.editor).trim() === '')) {\n cfg.editor = 'markdown'\n }\n // Validate and normalize defaultValue by kind before persisting\n if (cfg.defaultValue !== undefined && cfg.defaultValue !== null) {\n const validationError = await validateDefaultValueByKind(\n cfg.defaultValue, input.kind, cfg, em,\n { tenantId: scope.tenantId, organizationId: scope.organizationId },\n )\n if (validationError) {\n return NextResponse.json({ error: validationError }, { status: 400 })\n }\n cfg.defaultValue = normalizeDefaultValueByKind(cfg.defaultValue, input.kind)\n }\n def.configJson = cfg\n def.isActive = input.isActive ?? true\n def.deletedAt = def.isActive === false ? (def.deletedAt ?? new Date()) : null\n def.updatedAt = new Date()\n em.persist(def)\n await em.flush()\n await guard.runAfterSuccess()\n await invalidateDefinitionsCache(cache, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n entityIds: [input.entityId],\n })\n // Changing field definitions may impact forms but not sidebar items; no nav cache touch\n return NextResponse.json({ ok: true, item: { id: def.id, key: def.key, kind: def.kind, configJson: def.configJson, isActive: def.isActive } })\n}\n\nexport async function DELETE(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n let body: any\n try { body = await req.json() } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }) }\n const { entityId, key } = body || {}\n if (!entityId || !key) return NextResponse.json({ error: 'entityId and key are required' }, { status: 400 })\n\n const container = await createRequestContainer()\n const scope = await resolveDefinitionMutationScope({ auth, container, request: req })\n const { resolve } = container\n const em = resolve('em') as any\n let cache: CacheStrategy | undefined\n try {\n cache = resolve('cache') as CacheStrategy\n } catch {}\n const where: any = createExactDefinitionWhere(entityId, key, scope)\n let def = await em.findOne(CustomFieldDef, where)\n let inherited: any | null = null\n if (!def) {\n inherited = selectVisibleDefinitionWinner(await em.find(CustomFieldDef, createVisibleDefinitionWhere(\n entityId,\n key,\n scope,\n { deletedAt: null, isActive: true },\n )))\n if (!inherited) return NextResponse.json({ error: 'Not found' }, { status: 404 })\n }\n\n const guard = await beginEntitiesMutationGuard({\n container,\n auth,\n req,\n resourceKind: FIELD_DEFINITION_RESOURCE_KIND,\n resourceId: def?.id ?? inherited?.id ?? `${entityId}:${key}`,\n operation: 'delete',\n mutationPayload: { entityId, key },\n })\n if (guard.blockedResponse) return guard.blockedResponse\n\n if (!def) {\n def = createScopedDefinitionTombstone(em, inherited, scope)\n } else {\n markDefinitionTombstoned(def)\n }\n em.persist(def)\n await em.flush()\n await guard.runAfterSuccess()\n await invalidateDefinitionsCache(cache, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n entityIds: [entityId],\n })\n // Changing field definitions may impact forms but not sidebar items; no nav cache touch\n // Return the post-delete aggregate version so the edit form keeps its optimistic-lock\n // token in sync after removing a field out-of-band (issue #3152).\n const version = await resolveEntityDefinitionsVersion(em, {\n entityId,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n return NextResponse.json({ ok: true, version })\n}\n\nconst definitionsQuerySchema = z\n .object({\n entityId: z.union([z.string(), z.array(z.string())]).optional(),\n entityIds: z.string().optional(),\n fieldset: z.string().regex(fieldsetCodeRegex).optional(),\n })\n .refine(\n (value) => {\n if (value.entityId && typeof value.entityId === 'string' && value.entityId.trim().length > 0) return true\n if (Array.isArray(value.entityId) && value.entityId.length > 0) return true\n return typeof value.entityIds === 'string' && value.entityIds.trim().length > 0\n },\n { message: 'Provide at least one entityId or an entityIds list.' }\n )\n\nconst customFieldOptionValueSchema = z.union([z.string(), z.number()])\n\nconst customFieldDefinitionSchema = z.object({\n key: z.string(),\n kind: z.string(),\n label: z.string(),\n description: z.string().optional(),\n multi: z.boolean().optional(),\n options: z.array(customFieldOptionValueSchema).optional(),\n optionsUrl: z.string().optional(),\n filterable: z.boolean().optional(),\n formEditable: z.boolean().optional(),\n listVisible: z.boolean().optional(),\n editor: z.string().optional(),\n input: z.string().optional(),\n dictionaryId: z.string().optional(),\n dictionaryInlineCreate: z.boolean().optional(),\n priority: z.number().optional(),\n validation: z.array(z.any()).optional(),\n defaultValue: z.union([z.string(), z.number(), z.boolean(), z.null()]).optional(),\n maxAttachmentSizeMb: z.number().optional(),\n acceptExtensions: z.array(z.any()).optional(),\n entityId: z.string(),\n fieldset: z.string().optional(),\n group: z\n .object({\n code: z.string(),\n title: z.string().optional(),\n hint: z.string().optional(),\n })\n .optional(),\n})\n\nconst customFieldsetGroupResponseSchema = z.object({\n code: z.string(),\n title: z.string().optional(),\n hint: z.string().optional(),\n})\n\nconst entityFieldsetResponseSchema = z.object({\n code: z.string(),\n label: z.string(),\n icon: z.string().optional(),\n description: z.string().optional(),\n groups: z.array(customFieldsetGroupResponseSchema).optional(),\n})\n\nconst definitionsResponseSchema = z.object({\n items: z.array(customFieldDefinitionSchema),\n fieldsetsByEntity: z.record(z.string(), z.array(entityFieldsetResponseSchema)).optional(),\n entitySettings: z\n .record(\n z.string(),\n z.object({\n singleFieldsetPerRecord: z.boolean().optional(),\n })\n )\n .optional(),\n})\n\nconst upsertDefinitionResponseSchema = z.object({\n ok: z.literal(true),\n item: z.object({\n id: z.string().uuid(),\n key: z.string(),\n kind: z.string(),\n configJson: z.record(z.string(), z.any()),\n isActive: z.boolean().optional(),\n }),\n})\n\nconst deleteDefinitionRequestSchema = z.object({\n entityId: z.string(),\n key: z.string(),\n})\n\nconst deleteDefinitionResponseSchema = z.object({\n ok: z.literal(true),\n version: z.string().nullable().optional(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Entities',\n summary: 'Manage custom field definitions',\n methods: {\n GET: {\n summary: 'List active custom field definitions',\n description: 'Returns active custom field definitions for the supplied entity ids, respecting tenant scope and tombstones.',\n query: definitionsQuerySchema,\n responses: [\n {\n status: 200,\n description: 'Definition list',\n schema: definitionsResponseSchema,\n },\n {\n status: 400,\n description: 'Missing entity id',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n POST: {\n summary: 'Upsert custom field definition',\n description: 'Creates or updates a custom field definition for the current tenant/org scope.',\n requestBody: {\n contentType: 'application/json',\n schema: upsertCustomFieldDefSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Definition saved',\n schema: upsertDefinitionResponseSchema,\n },\n {\n status: 400,\n description: 'Validation failed',\n schema: z.object({\n error: z.string(),\n details: z.any().optional(),\n }),\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n DELETE: {\n summary: 'Soft delete custom field definition',\n description: 'Marks the specified definition inactive and tombstones it for the current scope.',\n requestBody: {\n contentType: 'application/json',\n schema: deleteDefinitionRequestSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Definition deleted',\n schema: deleteDefinitionResponseSchema,\n },\n {\n status: 400,\n description: 'Missing entity id or key',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 404,\n description: 'Definition not found',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n },\n}\nconst computeDefinitionScore = (def: any, cfg: Record<string, any>, entityIndex: number) => {\n const listVisibleScore = cfg.listVisible === false ? 0 : 1\n const formEditableScore = cfg.formEditable === false ? 0 : 1\n const filterableScore = cfg.filterable ? 1 : 0\n const kindScore = (() => {\n switch (def.kind) {\n case 'dictionary':\n return 8\n case 'relation':\n return 6\n case 'select':\n case 'currency':\n return 4\n case 'multiline':\n return 3\n case 'boolean':\n case 'integer':\n case 'float':\n return 2\n default:\n return 1\n }\n })()\n const optionsBonus = Array.isArray(cfg.options) && cfg.options.length ? 2 : 0\n const dictionaryBonus = typeof cfg.dictionaryId === 'string' && cfg.dictionaryId.trim().length ? 5 : 0\n const base = (listVisibleScore * 16) + (formEditableScore * 8) + (filterableScore * 4) + kindScore + optionsBonus + dictionaryBonus\n const penalty = typeof cfg.priority === 'number' ? cfg.priority : 0\n return { base, penalty, entityIndex }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { CacheStrategy } from '@open-mercato/cache'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { CustomEntity, CustomFieldDef } from '@open-mercato/core/modules/entities/data/entities'\nimport { upsertCustomFieldDefSchema, fieldsetCodeRegex } from '@open-mercato/core/modules/entities/data/validators'\nimport {\n createDefinitionsCacheKey,\n createDefinitionsCacheTags,\n invalidateDefinitionsCache,\n ENTITY_DEFINITIONS_CACHE_TTL_MS,\n} from './definitions.cache'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { filterSelectableSystemEntityIds, isSystemEntitySelectable } from '@open-mercato/shared/lib/entities/system-entities'\nimport { isOrmBackedSystemEntityId } from '@open-mercato/shared/lib/data/engine'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { loadEntityFieldsetConfigs, CustomFieldsetDefinition } from '../lib/fieldsets'\nimport { installCustomEntitiesFromModules } from '../lib/install-from-ce'\nimport { normalizeCustomFieldOptions } from '@open-mercato/shared/modules/entities/options'\nimport { CURRENCY_OPTIONS_URL } from '@open-mercato/shared/modules/entities/kinds'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport {\n beginEntitiesMutationGuard,\n FIELD_DEFINITION_RESOURCE_KIND,\n} from './definitions.mutation-guard'\nimport {\n canReadAllEntityMetadata,\n canReadEntityMetadata,\n getDeclaredCustomEntityRestriction,\n resolveEntityAclRequirement,\n} from '../lib/entityAcl'\nimport {\n createExactDefinitionWhere,\n createScopedDefinitionTombstone,\n createVisibleDefinitionWhere,\n markDefinitionTombstoned,\n resolveDefinitionScopeFromOrganizationScope,\n resolveDefinitionMutationScope,\n selectVisibleDefinitionWinner,\n} from '../lib/definition-scope'\nimport { resolveEntityDefinitionsVersion } from '../lib/definitions-version'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('entities').child({ component: 'definitions' })\n\n/**\n * Validate defaultValue against the field kind. Returns an error message string\n * if invalid, or null if valid. For dictionary and currency kinds, queries the\n * database to verify the token exists.\n */\nasync function validateDefaultValueByKind(\n value: unknown,\n kind: string,\n cfg: Record<string, unknown>,\n em: EntityManager,\n tenantContext: { tenantId: string | null; organizationId: string | null },\n): Promise<string | null> {\n switch (kind) {\n case 'boolean':\n if (typeof value !== 'boolean') return 'defaultValue for boolean fields must be true or false'\n return null\n case 'integer':\n if (typeof value !== 'number' || !Number.isInteger(value))\n return 'defaultValue for integer fields must be a whole number'\n return null\n case 'float':\n if (typeof value !== 'number' || !isFinite(value))\n return 'defaultValue for float fields must be a finite number'\n return null\n case 'text':\n case 'multiline':\n case 'date':\n case 'datetime':\n if (typeof value !== 'string')\n return `defaultValue for ${kind} fields must be a string`\n return null\n case 'dictionary': {\n if (typeof value !== 'string')\n return 'defaultValue for dictionary fields must be a string'\n const dictionaryId = typeof cfg.dictionaryId === 'string' ? cfg.dictionaryId.trim() : ''\n if (dictionaryId) {\n try {\n const { DictionaryEntry } = await import('@open-mercato/core/modules/dictionaries/data/entities')\n const entry = await em.findOne(DictionaryEntry, {\n dictionary: dictionaryId,\n value: value,\n ...(tenantContext.organizationId ? { organizationId: tenantContext.organizationId } : {}),\n ...(tenantContext.tenantId ? { tenantId: tenantContext.tenantId } : {}),\n })\n if (!entry) return `defaultValue \"${value}\" does not match any entry in the configured dictionary`\n } catch (err) {\n // If the dictionaries module is not available, skip entry validation\n logger.debug('Dictionary validation skipped \u2014 module not available', { err })\n }\n }\n return null\n }\n case 'currency': {\n if (typeof value !== 'string')\n return 'defaultValue for currency fields must be a string'\n try {\n const { Currency } = await import('@open-mercato/core/modules/currencies/data/entities')\n const where: Record<string, string> = { code: value as string }\n if (tenantContext.tenantId) where.tenantId = tenantContext.tenantId\n if (tenantContext.organizationId) where.organizationId = tenantContext.organizationId\n const currency = await em.findOne(Currency, where)\n if (!currency) return `defaultValue \"${value}\" does not match any available currency`\n } catch (err) {\n // If the currencies module is not available, skip currency validation\n logger.debug('Currency validation skipped \u2014 module not available', { err })\n }\n return null\n }\n case 'select': {\n if (typeof value !== 'string' && typeof value !== 'number')\n return 'defaultValue for select fields must be a string or number'\n const options = normalizeCustomFieldOptions(\n Array.isArray(cfg.options) ? cfg.options : [],\n )\n if (options.length > 0) {\n const match = options.some(\n (o) => o.value === value || String(o.value) === String(value),\n )\n if (!match) return 'defaultValue does not match any of the configured options'\n }\n return null\n }\n case 'relation':\n case 'attachment':\n return `defaultValue is not supported for ${kind} fields`\n default:\n return null\n }\n}\n\n/**\n * Normalize defaultValue to the canonical storage shape for the given kind.\n */\nfunction normalizeDefaultValueByKind(value: unknown, kind: string): unknown {\n switch (kind) {\n case 'boolean':\n return value === true\n case 'integer':\n return Math.round(Number(value))\n case 'float':\n return Number(value)\n default:\n return value\n }\n}\n\nexport const metadata = {\n // Reading definitions is needed by record forms; keep it auth-protected but accessible to all authenticated users\n GET: { requireAuth: true },\n // Mutations remain admin-only\n POST: { requireAuth: true, requireFeatures: ['entities.definitions.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['entities.definitions.manage'] },\n}\n\nfunction parseEntityIds(url: URL): string[] {\n const direct = url.searchParams.getAll('entityId').filter((id) => typeof id === 'string' && id.trim().length > 0)\n const combined = url.searchParams.get('entityIds')\n if (combined) {\n combined\n .split(',')\n .map((id) => id.trim())\n .filter((id) => id.length > 0)\n .forEach((id) => direct.push(id))\n }\n const unique: string[] = []\n const seen = new Set<string>()\n for (const id of direct) {\n if (seen.has(id)) continue\n seen.add(id)\n unique.push(id)\n }\n return unique\n}\n\nasync function resolveEntityDefaultEditor(em: any, entityId: string, tenantId: string | null | undefined): Promise<string | undefined> {\n try {\n const ent = await em.findOne(CustomEntity, {\n entityId,\n $and: [\n { $or: [ { tenantId: tenantId ?? undefined as any }, { tenantId: null } ] },\n ],\n } as any)\n if (ent && typeof (ent as any).defaultEditor === 'string') return (ent as any).defaultEditor\n } catch {}\n return undefined\n}\n\nfunction normalizeFieldGroup(raw: unknown): { code: string; title?: string; hint?: string } | undefined {\n if (!raw) return undefined\n if (typeof raw === 'string') {\n const code = raw.trim()\n return code ? { code } : undefined\n }\n if (typeof raw !== 'object') return undefined\n const entry = raw as Record<string, unknown>\n const code = typeof entry.code === 'string' ? entry.code.trim() : ''\n if (!code) return undefined\n const group: { code: string; title?: string; hint?: string } = { code }\n if (typeof entry.title === 'string' && entry.title.trim()) group.title = entry.title.trim()\n if (typeof entry.hint === 'string' && entry.hint.trim()) group.hint = entry.hint.trim()\n return group\n}\n\nfunction definitionMatchesReadScope(\n definition: { tenantId?: string | null; organizationId?: string | null },\n scope: { tenantId: string | null; organizationId: string | null },\n) {\n const definitionTenantId = definition.tenantId ?? null\n const definitionOrganizationId = definition.organizationId ?? null\n const tenantMatches = definitionTenantId === null || definitionTenantId === scope.tenantId\n const organizationMatches =\n definitionOrganizationId === null ||\n (scope.organizationId !== null && definitionOrganizationId === scope.organizationId)\n return tenantMatches && organizationMatches\n}\n\nexport async function GET(req: Request) {\n const url = new URL(req.url)\n const requestedEntityIds = parseEntityIds(url)\n if (!requestedEntityIds.length) {\n return NextResponse.json({ error: 'entityId is required' }, { status: 400 })\n }\n const requestedFieldset = url.searchParams.get('fieldset')\n const fieldsetFilter = requestedFieldset && requestedFieldset.trim().length ? requestedFieldset.trim() : null\n\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const selectableEntityIds = filterSelectableSystemEntityIds(requestedEntityIds)\n if (!selectableEntityIds.length) {\n return NextResponse.json({ items: [] })\n }\n\n const container = await createRequestContainer()\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const definitionScope = resolveDefinitionScopeFromOrganizationScope(auth, scope)\n const tenantId = definitionScope.tenantId\n if (!tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n const organizationId = definitionScope.organizationId\n const { resolve } = container\n const em = resolve('em') as any\n const rbac = resolve('rbacService') as RbacService\n const acl = await rbac.loadAcl(auth.sub ?? '', {\n tenantId,\n organizationId,\n })\n const designerCanRead = canReadAllEntityMetadata(acl)\n const customEntityRestrictions = new Map<string, boolean>()\n for (const entityId of selectableEntityIds) {\n if (isOrmBackedSystemEntityId(em, entityId)) continue\n const declaredRestriction = getDeclaredCustomEntityRestriction(entityId)\n if (declaredRestriction !== undefined) customEntityRestrictions.set(entityId, declaredRestriction)\n }\n if (!designerCanRead) {\n const unresolvedEntityIds = selectableEntityIds.filter(\n (entityId) => !resolveEntityAclRequirement(entityId) && !customEntityRestrictions.has(entityId),\n )\n if (unresolvedEntityIds.length) {\n const registrations = await em.find(CustomEntity as any, {\n entityId: { $in: unresolvedEntityIds },\n isActive: true,\n $and: [\n { $or: [{ tenantId }, { tenantId: null }] },\n { $or: [{ organizationId }, { organizationId: null }] },\n ],\n } as any)\n for (const registration of registrations as any[]) {\n const entityId = String(registration.entityId)\n if (!isOrmBackedSystemEntityId(em, entityId)) {\n customEntityRestrictions.set(entityId, registration.accessRestricted === true)\n }\n }\n }\n }\n const entityIds = selectableEntityIds.filter((entityId) => canReadEntityMetadata({\n entityId,\n isCustomEntity: customEntityRestrictions.has(entityId),\n isRestricted: customEntityRestrictions.get(entityId) === true,\n acl,\n }))\n if (!entityIds.length) {\n return NextResponse.json({ items: [], fieldsetsByEntity: {}, entitySettings: {} })\n }\n let cache: CacheStrategy | undefined\n try {\n cache = resolve('cache') as CacheStrategy\n } catch {}\n\n let canManageDefinitions = false\n if (typeof auth.sub === 'string' && auth.sub.length > 0) {\n try {\n canManageDefinitions = await rbac.userHasAllFeatures(auth.sub, ['entities.definitions.manage'], {\n tenantId,\n organizationId,\n })\n } catch {\n // RBAC service unavailable \u2014 fall back to read-only (no definition sync)\n }\n }\n\n if (canManageDefinitions) {\n try {\n await installCustomEntitiesFromModules(em, cache, {\n tenantIds: [tenantId],\n entityIds,\n includeGlobal: true,\n createOnly: true,\n })\n } catch (err) {\n logger.warn('Failed to synchronize module-backed definitions', {\n tenantId,\n entityIds,\n err,\n })\n }\n }\n\n let cacheKey: string | null = null\n if (cache && !fieldsetFilter) {\n cacheKey = createDefinitionsCacheKey({\n tenantId,\n organizationId,\n entityIds,\n })\n try {\n const cached = await cache.get(cacheKey)\n if (cached) {\n return NextResponse.json(cached)\n }\n } catch (err) {\n logger.warn('Failed to read cache', { err })\n }\n }\n\n const fieldsetConfigs = await loadEntityFieldsetConfigs(em, {\n entityIds,\n tenantId,\n organizationId,\n mode: 'public',\n })\n\n const tenantCandidates = [{ tenantId }, { tenantId: null as string | null }]\n const organizationCandidates = [{ organizationId: null as string | null }]\n if (organizationId) organizationCandidates.unshift({ organizationId })\n const readScope = { tenantId, organizationId }\n\n const whereActive = {\n entityId: { $in: entityIds as any },\n deletedAt: null,\n $and: [\n { $or: tenantCandidates },\n { $or: organizationCandidates },\n ],\n } as any\n const defs = (await em.find(CustomFieldDef, whereActive as any))\n .filter((definition: any) => definitionMatchesReadScope(definition, readScope))\n const tombstones = (await em.find(CustomFieldDef, {\n entityId: { $in: entityIds as any },\n deletedAt: { $ne: null } as any,\n $and: [\n { $or: tenantCandidates },\n { $or: organizationCandidates },\n ],\n } as any)).filter((definition: any) => definitionMatchesReadScope(definition, readScope))\n\n const tombstonedByEntity = new Map<string, Set<string>>()\n for (const entry of tombstones as any[]) {\n const eid = String(entry.entityId)\n if (!tombstonedByEntity.has(eid)) tombstonedByEntity.set(eid, new Set())\n tombstonedByEntity.get(eid)!.add(entry.key)\n }\n\n const scopeScore = (x: any) => (x.tenantId ? 2 : 0) + (x.organizationId ? 1 : 0)\n\n const definitionsByEntity = new Map<string, any[]>()\n for (const d of defs) {\n const eid = String(d.entityId)\n if (!definitionsByEntity.has(eid)) definitionsByEntity.set(eid, [])\n definitionsByEntity.get(eid)!.push(d)\n }\n\n const entityDefaultEditors = new Map<string, string | undefined>()\n for (const entityId of entityIds) {\n const editor = await resolveEntityDefaultEditor(em, entityId, tenantId ?? null)\n entityDefaultEditors.set(entityId, editor)\n }\n\n const items: any[] = []\n\n const entityOrder = new Map<string, number>()\n entityIds.forEach((id, idx) => entityOrder.set(id, idx))\n\n for (const entityId of entityIds) {\n const defsForEntity = definitionsByEntity.get(entityId) ?? []\n if (!defsForEntity.length) continue\n const tombstonedKeys = tombstonedByEntity.get(entityId) ?? new Set<string>()\n const byKey = new Map<string, any>()\n for (const d of defsForEntity) {\n const existing = byKey.get(d.key)\n if (!existing) { byKey.set(d.key, d); continue }\n const sNew = scopeScore(d)\n const sOld = scopeScore(existing)\n if (sNew > sOld) { byKey.set(d.key, d); continue }\n if (sNew < sOld) continue\n const tNew = (d.updatedAt instanceof Date) ? d.updatedAt.getTime() : new Date(d.updatedAt).getTime()\n const tOld = (existing.updatedAt instanceof Date) ? existing.updatedAt.getTime() : new Date(existing.updatedAt).getTime()\n if (tNew >= tOld) byKey.set(d.key, d)\n }\n\n const defaultEditor = entityDefaultEditors.get(entityId)\n const winning = Array.from(byKey.values()).filter((d: any) => (d.isActive !== false) && !tombstonedKeys.has(d.key))\n winning.sort((a: any, b: any) => ((a.configJson?.priority ?? 0) - (b.configJson?.priority ?? 0)))\n\n for (const d of winning) {\n const rawFieldset = typeof d.configJson?.fieldset === 'string' ? d.configJson.fieldset.trim() : ''\n const normalizedFieldset = rawFieldset.length > 0 ? rawFieldset : undefined\n const normalizedFieldsets = Array.isArray(d.configJson?.fieldsets)\n ? d.configJson.fieldsets\n .filter((entry: unknown): entry is string => typeof entry === 'string')\n .map((entry: string) => entry.trim())\n .filter((entry: string) => entry.length > 0)\n : []\n const effectiveFieldsets = normalizedFieldsets.length > 0\n ? normalizedFieldsets\n : (normalizedFieldset ? [normalizedFieldset] : [])\n if (fieldsetFilter && !effectiveFieldsets.includes(fieldsetFilter)) continue\n const groupInfo = normalizeFieldGroup(d.configJson?.group)\n const keyLower = String(d.key).toLowerCase()\n const candidateBase = {\n key: d.key,\n kind: d.kind,\n label: d.configJson?.label || d.key,\n description: d.configJson?.description || undefined,\n multi: Boolean(d.configJson?.multi),\n relatedEntityId: d.kind === 'relation' && typeof d.configJson?.relatedEntityId === 'string'\n ? d.configJson.relatedEntityId.trim() || undefined\n : undefined,\n options: (() => {\n if (d.kind === 'currency') return undefined\n const normalizedOptions = normalizeCustomFieldOptions(d.configJson?.options)\n return normalizedOptions.length ? normalizedOptions : undefined\n })(),\n optionsUrl: (() => {\n if (d.kind === 'currency') return CURRENCY_OPTIONS_URL\n const dictionaryId = typeof d.configJson?.dictionaryId === 'string' ? d.configJson.dictionaryId : undefined\n if (dictionaryId) return `/api/dictionaries/${dictionaryId}/entries`\n return typeof d.configJson?.optionsUrl === 'string' ? d.configJson.optionsUrl : undefined\n })(),\n filterable: Boolean(d.configJson?.filterable),\n formEditable: d.configJson?.formEditable !== undefined ? Boolean(d.configJson.formEditable) : true,\n listVisible: d.configJson?.listVisible !== undefined ? Boolean(d.configJson.listVisible) : true,\n editor: typeof d.configJson?.editor === 'string'\n ? d.configJson.editor\n : (d.kind === 'multiline' ? defaultEditor : undefined),\n input: typeof d.configJson?.input === 'string' ? d.configJson.input : undefined,\n dictionaryId: typeof d.configJson?.dictionaryId === 'string' ? d.configJson.dictionaryId : undefined,\n dictionaryInlineCreate: d.configJson?.dictionaryInlineCreate !== undefined\n ? Boolean(d.configJson.dictionaryInlineCreate)\n : undefined,\n priority: typeof d.configJson?.priority === 'number' ? d.configJson.priority : 0,\n validation: Array.isArray(d.configJson?.validation) ? d.configJson.validation : undefined,\n defaultValue: d.configJson?.defaultValue !== undefined ? d.configJson.defaultValue : undefined,\n // attachments config passthrough\n maxAttachmentSizeMb: typeof d.configJson?.maxAttachmentSizeMb === 'number' ? d.configJson.maxAttachmentSizeMb : undefined,\n acceptExtensions: Array.isArray(d.configJson?.acceptExtensions) ? d.configJson.acceptExtensions : undefined,\n // phone config passthrough\n defaultCountryIso2: typeof d.configJson?.defaultCountryIso2 === 'string' ? d.configJson.defaultCountryIso2 : undefined,\n entityId,\n fieldset: normalizedFieldset ?? effectiveFieldsets[0],\n fieldsets: effectiveFieldsets.length > 0 ? effectiveFieldsets : undefined,\n group: groupInfo,\n } as any\n const metrics = computeDefinitionScore(d, candidateBase, entityOrder.get(entityId) ?? Number.MAX_SAFE_INTEGER)\n const candidate = { ...candidateBase, __score: metrics }\n const existing = (items as any[]).find((entry) => entry.key.toLowerCase() === keyLower)\n if (!existing) {\n items.push(candidate)\n continue\n }\n const existingScore = existing.__score as { base: number; penalty: number; entityIndex: number }\n const candidateScoreInfo = candidate.__score as { base: number; penalty: number; entityIndex: number }\n const better =\n candidateScoreInfo.base > existingScore.base ||\n (candidateScoreInfo.base === existingScore.base && candidateScoreInfo.penalty < existingScore.penalty) ||\n (candidateScoreInfo.base === existingScore.base && candidateScoreInfo.penalty === existingScore.penalty && candidateScoreInfo.entityIndex < existingScore.entityIndex)\n if (better) {\n const index = items.findIndex((entry) => entry.key.toLowerCase() === keyLower)\n if (index >= 0) items[index] = candidate\n }\n }\n }\n\n const sanitized = items.map((item: any) => {\n const { __score, ...rest } = item\n return rest\n })\n sanitized.sort((a: any, b: any) => ((a.priority ?? 0) - (b.priority ?? 0)))\n\n const fieldsetsByEntity: Record<string, CustomFieldsetDefinition[]> = {}\n const entitySettings: Record<string, { singleFieldsetPerRecord: boolean }> = {}\n for (const entityId of entityIds) {\n const cfg = fieldsetConfigs.get(entityId) ?? { fieldsets: [], singleFieldsetPerRecord: true }\n fieldsetsByEntity[entityId] = cfg.fieldsets\n entitySettings[entityId] = { singleFieldsetPerRecord: cfg.singleFieldsetPerRecord }\n }\n\n const responseBody = { items: sanitized, fieldsetsByEntity, entitySettings }\n\n if (cache && cacheKey && !fieldsetFilter) {\n const tags = createDefinitionsCacheTags({\n tenantId,\n organizationId,\n entityIds,\n })\n try {\n await cache.set(cacheKey, responseBody, {\n ttl: ENTITY_DEFINITIONS_CACHE_TTL_MS,\n tags,\n })\n } catch (err) {\n logger.warn('Failed to store cache entry', { err })\n }\n }\n\n return NextResponse.json(responseBody)\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n let body: any\n try { body = await req.json() } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }) }\n const parsed = upsertCustomFieldDefSchema.safeParse(body)\n if (!parsed.success) return NextResponse.json({ error: 'Validation failed', details: parsed.error.flatten() }, { status: 400 })\n const input = parsed.data\n\n if (!isSystemEntitySelectable(input.entityId)) {\n return NextResponse.json({ error: 'Custom fields are not supported for this entity' }, { status: 400 })\n }\n\n if (input.kind === 'dictionary') {\n const dictionaryId = input.configJson?.dictionaryId\n if (typeof dictionaryId !== 'string' || dictionaryId.trim().length === 0) {\n return NextResponse.json({ error: 'dictionaryId is required for dictionary custom fields' }, { status: 400 })\n }\n }\n\n const container = await createRequestContainer()\n const scope = await resolveDefinitionMutationScope({ auth, container, request: req })\n const { resolve } = container\n const em = resolve('em') as any\n let cache: CacheStrategy | undefined\n try {\n cache = resolve('cache') as CacheStrategy\n } catch {}\n\n const where: any = createExactDefinitionWhere(input.entityId, input.key, scope)\n let def = await em.findOne(CustomFieldDef, where)\n\n const guard = await beginEntitiesMutationGuard({\n container,\n auth,\n req,\n resourceKind: FIELD_DEFINITION_RESOURCE_KIND,\n resourceId: def ? def.id : `${input.entityId}:${input.key}`,\n operation: def ? 'update' : 'create',\n mutationPayload: input as unknown as Record<string, unknown>,\n })\n if (guard.blockedResponse) return guard.blockedResponse\n\n if (!def) def = em.create(CustomFieldDef, { ...where, createdAt: new Date() })\n def.kind = input.kind\n const inCfg = (input as any).configJson ?? {}\n const cfg: Record<string, any> = { ...inCfg }\n if (cfg.label == null || String(cfg.label).trim() === '') cfg.label = input.key\n if (cfg.formEditable === undefined) cfg.formEditable = true\n if (cfg.listVisible === undefined) cfg.listVisible = true\n if (input.kind === 'dictionary') {\n const dictionaryId = typeof cfg.dictionaryId === 'string' ? cfg.dictionaryId.trim() : ''\n cfg.dictionaryId = dictionaryId\n cfg.dictionaryInlineCreate = cfg.dictionaryInlineCreate !== false\n }\n if (input.kind === 'currency') {\n cfg.optionsUrl = CURRENCY_OPTIONS_URL\n if (Array.isArray(cfg.options)) delete cfg.options\n }\n if (input.kind === 'multiline' && (cfg.editor == null || String(cfg.editor).trim() === '')) {\n cfg.editor = 'markdown'\n }\n // Validate and normalize defaultValue by kind before persisting\n if (cfg.defaultValue !== undefined && cfg.defaultValue !== null) {\n const validationError = await validateDefaultValueByKind(\n cfg.defaultValue, input.kind, cfg, em,\n { tenantId: scope.tenantId, organizationId: scope.organizationId },\n )\n if (validationError) {\n return NextResponse.json({ error: validationError }, { status: 400 })\n }\n cfg.defaultValue = normalizeDefaultValueByKind(cfg.defaultValue, input.kind)\n }\n def.configJson = cfg\n def.isActive = input.isActive ?? true\n def.deletedAt = def.isActive === false ? (def.deletedAt ?? new Date()) : null\n def.updatedAt = new Date()\n em.persist(def)\n await em.flush()\n await guard.runAfterSuccess()\n await invalidateDefinitionsCache(cache, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n entityIds: [input.entityId],\n })\n // Changing field definitions may impact forms but not sidebar items; no nav cache touch\n return NextResponse.json({ ok: true, item: { id: def.id, key: def.key, kind: def.kind, configJson: def.configJson, isActive: def.isActive } })\n}\n\nexport async function DELETE(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n let body: any\n try { body = await req.json() } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }) }\n const { entityId, key } = body || {}\n if (!entityId || !key) return NextResponse.json({ error: 'entityId and key are required' }, { status: 400 })\n\n const container = await createRequestContainer()\n const scope = await resolveDefinitionMutationScope({ auth, container, request: req })\n const { resolve } = container\n const em = resolve('em') as any\n let cache: CacheStrategy | undefined\n try {\n cache = resolve('cache') as CacheStrategy\n } catch {}\n const where: any = createExactDefinitionWhere(entityId, key, scope)\n let def = await em.findOne(CustomFieldDef, where)\n let inherited: any | null = null\n if (!def) {\n inherited = selectVisibleDefinitionWinner(await em.find(CustomFieldDef, createVisibleDefinitionWhere(\n entityId,\n key,\n scope,\n { deletedAt: null, isActive: true },\n )))\n if (!inherited) return NextResponse.json({ error: 'Not found' }, { status: 404 })\n }\n\n const guard = await beginEntitiesMutationGuard({\n container,\n auth,\n req,\n resourceKind: FIELD_DEFINITION_RESOURCE_KIND,\n resourceId: def?.id ?? inherited?.id ?? `${entityId}:${key}`,\n operation: 'delete',\n mutationPayload: { entityId, key },\n })\n if (guard.blockedResponse) return guard.blockedResponse\n\n if (!def) {\n def = createScopedDefinitionTombstone(em, inherited, scope)\n } else {\n markDefinitionTombstoned(def)\n }\n em.persist(def)\n await em.flush()\n await guard.runAfterSuccess()\n await invalidateDefinitionsCache(cache, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n entityIds: [entityId],\n })\n // Changing field definitions may impact forms but not sidebar items; no nav cache touch\n // Return the post-delete aggregate version so the edit form keeps its optimistic-lock\n // token in sync after removing a field out-of-band (issue #3152).\n const version = await resolveEntityDefinitionsVersion(em, {\n entityId,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n return NextResponse.json({ ok: true, version })\n}\n\nconst definitionsQuerySchema = z\n .object({\n entityId: z.union([z.string(), z.array(z.string())]).optional(),\n entityIds: z.string().optional(),\n fieldset: z.string().regex(fieldsetCodeRegex).optional(),\n })\n .refine(\n (value) => {\n if (value.entityId && typeof value.entityId === 'string' && value.entityId.trim().length > 0) return true\n if (Array.isArray(value.entityId) && value.entityId.length > 0) return true\n return typeof value.entityIds === 'string' && value.entityIds.trim().length > 0\n },\n { message: 'Provide at least one entityId or an entityIds list.' }\n )\n\nconst customFieldOptionValueSchema = z.union([z.string(), z.number()])\n\nconst customFieldDefinitionSchema = z.object({\n key: z.string(),\n kind: z.string(),\n label: z.string(),\n description: z.string().optional(),\n multi: z.boolean().optional(),\n relatedEntityId: z.string().optional(),\n options: z.array(customFieldOptionValueSchema).optional(),\n optionsUrl: z.string().optional(),\n filterable: z.boolean().optional(),\n formEditable: z.boolean().optional(),\n listVisible: z.boolean().optional(),\n editor: z.string().optional(),\n input: z.string().optional(),\n dictionaryId: z.string().optional(),\n dictionaryInlineCreate: z.boolean().optional(),\n priority: z.number().optional(),\n validation: z.array(z.any()).optional(),\n defaultValue: z.union([z.string(), z.number(), z.boolean(), z.null()]).optional(),\n maxAttachmentSizeMb: z.number().optional(),\n acceptExtensions: z.array(z.any()).optional(),\n entityId: z.string(),\n fieldset: z.string().optional(),\n group: z\n .object({\n code: z.string(),\n title: z.string().optional(),\n hint: z.string().optional(),\n })\n .optional(),\n})\n\nconst customFieldsetGroupResponseSchema = z.object({\n code: z.string(),\n title: z.string().optional(),\n hint: z.string().optional(),\n})\n\nconst entityFieldsetResponseSchema = z.object({\n code: z.string(),\n label: z.string(),\n icon: z.string().optional(),\n description: z.string().optional(),\n groups: z.array(customFieldsetGroupResponseSchema).optional(),\n})\n\nconst definitionsResponseSchema = z.object({\n items: z.array(customFieldDefinitionSchema),\n fieldsetsByEntity: z.record(z.string(), z.array(entityFieldsetResponseSchema)).optional(),\n entitySettings: z\n .record(\n z.string(),\n z.object({\n singleFieldsetPerRecord: z.boolean().optional(),\n })\n )\n .optional(),\n})\n\nconst upsertDefinitionResponseSchema = z.object({\n ok: z.literal(true),\n item: z.object({\n id: z.string().uuid(),\n key: z.string(),\n kind: z.string(),\n configJson: z.record(z.string(), z.any()),\n isActive: z.boolean().optional(),\n }),\n})\n\nconst deleteDefinitionRequestSchema = z.object({\n entityId: z.string(),\n key: z.string(),\n})\n\nconst deleteDefinitionResponseSchema = z.object({\n ok: z.literal(true),\n version: z.string().nullable().optional(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Entities',\n summary: 'Manage custom field definitions',\n methods: {\n GET: {\n summary: 'List active custom field definitions',\n description: 'Returns active custom field definitions for the supplied entity ids, respecting tenant scope and tombstones.',\n query: definitionsQuerySchema,\n responses: [\n {\n status: 200,\n description: 'Definition list',\n schema: definitionsResponseSchema,\n },\n {\n status: 400,\n description: 'Missing entity id',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n POST: {\n summary: 'Upsert custom field definition',\n description: 'Creates or updates a custom field definition for the current tenant/org scope.',\n requestBody: {\n contentType: 'application/json',\n schema: upsertCustomFieldDefSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Definition saved',\n schema: upsertDefinitionResponseSchema,\n },\n {\n status: 400,\n description: 'Validation failed',\n schema: z.object({\n error: z.string(),\n details: z.any().optional(),\n }),\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n DELETE: {\n summary: 'Soft delete custom field definition',\n description: 'Marks the specified definition inactive and tombstones it for the current scope.',\n requestBody: {\n contentType: 'application/json',\n schema: deleteDefinitionRequestSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Definition deleted',\n schema: deleteDefinitionResponseSchema,\n },\n {\n status: 400,\n description: 'Missing entity id or key',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 404,\n description: 'Definition not found',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n },\n}\nconst computeDefinitionScore = (def: any, cfg: Record<string, any>, entityIndex: number) => {\n const listVisibleScore = cfg.listVisible === false ? 0 : 1\n const formEditableScore = cfg.formEditable === false ? 0 : 1\n const filterableScore = cfg.filterable ? 1 : 0\n const kindScore = (() => {\n switch (def.kind) {\n case 'dictionary':\n return 8\n case 'relation':\n return 6\n case 'select':\n case 'currency':\n return 4\n case 'multiline':\n return 3\n case 'boolean':\n case 'integer':\n case 'float':\n return 2\n default:\n return 1\n }\n })()\n const optionsBonus = Array.isArray(cfg.options) && cfg.options.length ? 2 : 0\n const dictionaryBonus = typeof cfg.dictionaryId === 'string' && cfg.dictionaryId.trim().length ? 5 : 0\n const base = (listVisibleScore * 16) + (formEditableScore * 8) + (filterableScore * 4) + kindScore + optionsBonus + dictionaryBonus\n const penalty = typeof cfg.priority === 'number' ? cfg.priority : 0\n return { base, penalty, entityIndex }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["code"]
7
7
  }