@open-mercato/core 0.6.6-develop.6519.1.bdc91fb755 → 0.6.6-develop.6520.1.55df6df792

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 (70) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/customers/backend/calendar/page.js +9 -1
  3. package/dist/modules/customers/backend/calendar/page.js.map +2 -2
  4. package/dist/modules/customers/components/calendar/AgendaList.js +0 -11
  5. package/dist/modules/customers/components/calendar/AgendaList.js.map +2 -2
  6. package/dist/modules/customers/components/calendar/CalendarEventEditor.js +391 -235
  7. package/dist/modules/customers/components/calendar/CalendarEventEditor.js.map +2 -2
  8. package/dist/modules/customers/components/calendar/CalendarScreen.js +5 -5
  9. package/dist/modules/customers/components/calendar/CalendarScreen.js.map +2 -2
  10. package/dist/modules/customers/components/calendar/CalendarSettingsModal.js +2 -2
  11. package/dist/modules/customers/components/calendar/CalendarSettingsModal.js.map +2 -2
  12. package/dist/modules/customers/components/calendar/MonthGrid.js +1 -11
  13. package/dist/modules/customers/components/calendar/MonthGrid.js.map +2 -2
  14. package/dist/modules/customers/components/calendar/TimeGrid.js +162 -176
  15. package/dist/modules/customers/components/calendar/TimeGrid.js.map +2 -2
  16. package/dist/modules/customers/components/calendar/editor/LocationField.js +61 -14
  17. package/dist/modules/customers/components/calendar/editor/LocationField.js.map +2 -2
  18. package/dist/modules/customers/components/calendar/editor/PeopleField.js +10 -5
  19. package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
  20. package/dist/modules/customers/components/calendar/editor/PriorityField.js +85 -0
  21. package/dist/modules/customers/components/calendar/editor/PriorityField.js.map +7 -0
  22. package/dist/modules/customers/components/calendar/editor/RelatedToField.js +8 -3
  23. package/dist/modules/customers/components/calendar/editor/RelatedToField.js.map +2 -2
  24. package/dist/modules/customers/components/calendar/editor/ResourcesField.js +225 -0
  25. package/dist/modules/customers/components/calendar/editor/ResourcesField.js.map +7 -0
  26. package/dist/modules/customers/components/calendar/editor/SegmentGroup.js +12 -7
  27. package/dist/modules/customers/components/calendar/editor/SegmentGroup.js.map +2 -2
  28. package/dist/modules/customers/components/calendar/editor/inputs.js +68 -57
  29. package/dist/modules/customers/components/calendar/editor/inputs.js.map +2 -2
  30. package/dist/modules/customers/components/calendar/editor/lookups.js +82 -2
  31. package/dist/modules/customers/components/calendar/editor/lookups.js.map +2 -2
  32. package/dist/modules/customers/components/calendar/useCalendarItems.js +6 -1
  33. package/dist/modules/customers/components/calendar/useCalendarItems.js.map +2 -2
  34. package/dist/modules/customers/data/validators.js +3 -1
  35. package/dist/modules/customers/data/validators.js.map +2 -2
  36. package/dist/modules/customers/lib/calendar/editorPayload.js +53 -22
  37. package/dist/modules/customers/lib/calendar/editorPayload.js.map +2 -2
  38. package/dist/modules/entities/backend/entities/user/[entityId]/page.js +1 -1
  39. package/dist/modules/entities/backend/entities/user/[entityId]/page.js.map +2 -2
  40. package/package.json +7 -7
  41. package/src/modules/customers/backend/calendar/page.tsx +8 -1
  42. package/src/modules/customers/components/calendar/AgendaList.tsx +0 -10
  43. package/src/modules/customers/components/calendar/CalendarEventEditor.tsx +444 -261
  44. package/src/modules/customers/components/calendar/CalendarScreen.tsx +12 -5
  45. package/src/modules/customers/components/calendar/CalendarSettingsModal.tsx +6 -6
  46. package/src/modules/customers/components/calendar/MonthGrid.tsx +0 -11
  47. package/src/modules/customers/components/calendar/TimeGrid.tsx +1 -24
  48. package/src/modules/customers/components/calendar/editor/LocationField.tsx +64 -2
  49. package/src/modules/customers/components/calendar/editor/PeopleField.tsx +11 -4
  50. package/src/modules/customers/components/calendar/editor/PriorityField.tsx +90 -0
  51. package/src/modules/customers/components/calendar/editor/RelatedToField.tsx +8 -3
  52. package/src/modules/customers/components/calendar/editor/ResourcesField.tsx +264 -0
  53. package/src/modules/customers/components/calendar/editor/SegmentGroup.tsx +14 -6
  54. package/src/modules/customers/components/calendar/editor/inputs.tsx +91 -50
  55. package/src/modules/customers/components/calendar/editor/lookups.ts +115 -2
  56. package/src/modules/customers/components/calendar/useCalendarItems.ts +8 -1
  57. package/src/modules/customers/data/validators.ts +3 -1
  58. package/src/modules/customers/i18n/de.json +12 -2
  59. package/src/modules/customers/i18n/en.json +12 -2
  60. package/src/modules/customers/i18n/es.json +12 -2
  61. package/src/modules/customers/i18n/pl.json +12 -2
  62. package/src/modules/customers/lib/calendar/editorPayload.ts +94 -37
  63. package/src/modules/entities/backend/entities/user/[entityId]/page.tsx +1 -1
  64. package/src/modules/entities/i18n/de.json +120 -0
  65. package/src/modules/entities/i18n/en.json +120 -0
  66. package/src/modules/entities/i18n/es.json +120 -0
  67. package/src/modules/entities/i18n/pl.json +120 -0
  68. package/dist/modules/customers/components/calendar/editor/CategoryField.js +0 -75
  69. package/dist/modules/customers/components/calendar/editor/CategoryField.js.map +0 -7
  70. package/src/modules/customers/components/calendar/editor/CategoryField.tsx +0 -79
@@ -23,26 +23,25 @@ const KIND_BY_INTERACTION_TYPE = {
23
23
  function editorKindOfInteractionType(interactionType) {
24
24
  return KIND_BY_INTERACTION_TYPE[interactionType] ?? "meeting";
25
25
  }
26
- function buildEditorCategoryOptions(params) {
27
- const { typeLabels, surfacedTypes, eventCategories, selectedValue, selectedFallbackLabel } = params;
28
- const dictionaryOptions = Object.entries(typeLabels).map(([value, label]) => ({ value, label }));
29
- const surfacedSet = new Set(surfacedTypes);
30
- const filtered = surfacedTypes.length > 0 ? dictionaryOptions.filter((option) => surfacedSet.has(option.label)) : dictionaryOptions;
31
- const options = [...filtered];
32
- const knownLabels = new Set(options.map((option) => option.label));
33
- const knownValues = new Set(options.map((option) => option.value));
34
- const customLabels = [];
35
- for (const label of [...eventCategories, ...surfacedTypes]) {
36
- const trimmed = label.trim();
37
- if (!trimmed || knownLabels.has(trimmed) || customLabels.includes(trimmed)) continue;
38
- customLabels.push(trimmed);
39
- }
40
- for (const label of customLabels) {
41
- options.push({ value: label, label });
42
- knownValues.add(label);
43
- }
44
- if (!knownValues.has(selectedValue)) {
45
- options.unshift({ value: selectedValue, label: typeLabels[selectedValue] ?? selectedFallbackLabel });
26
+ const EDITOR_KIND_ICONS = {
27
+ meeting: "lucide:users",
28
+ call: "lucide:phone-call",
29
+ email: "lucide:mail",
30
+ note: "lucide:notebook",
31
+ event: "lucide:calendar",
32
+ task: "lucide:check-square"
33
+ };
34
+ function buildEditorTypeOptions(params) {
35
+ const { typeLabels, typeIcons, selectedValue, kindLabels } = params;
36
+ const iconOf = (value) => typeIcons?.[value] ?? EDITOR_KIND_ICONS[editorKindOfInteractionType(value)] ?? null;
37
+ const dictionaryEntries = Object.entries(typeLabels);
38
+ const options = dictionaryEntries.length ? dictionaryEntries.map(([value, label]) => ({ value, label, icon: iconOf(value) })) : EDITOR_KINDS.map((kind) => ({ value: kind, label: kindLabels[kind], icon: EDITOR_KIND_ICONS[kind] }));
39
+ if (!options.some((option) => option.value === selectedValue)) {
40
+ options.unshift({
41
+ value: selectedValue,
42
+ label: typeLabels[selectedValue] ?? kindLabels[editorKindOfInteractionType(selectedValue)] ?? selectedValue,
43
+ icon: iconOf(selectedValue)
44
+ });
46
45
  }
47
46
  return options;
48
47
  }
@@ -53,6 +52,21 @@ function priorityFromNumber(value) {
53
52
  if (value <= 66) return "medium";
54
53
  return "high";
55
54
  }
55
+ const RESOURCE_LINK_TYPE = "resource";
56
+ function parseLinkedEntities(raw) {
57
+ const resources = [];
58
+ const preservedLinkedEntities = [];
59
+ if (!Array.isArray(raw)) return { resources, preservedLinkedEntities };
60
+ for (const entry of raw) {
61
+ if (!entry || typeof entry !== "object") continue;
62
+ const { id, type, label } = entry;
63
+ if (typeof id !== "string" || !id.length || typeof type !== "string" || !type.length) continue;
64
+ const safeLabel = typeof label === "string" ? label : "";
65
+ if (type === RESOURCE_LINK_TYPE) resources.push({ id, label: safeLabel || id });
66
+ else preservedLinkedEntities.push({ id, type, label: safeLabel });
67
+ }
68
+ return { resources, preservedLinkedEntities };
69
+ }
56
70
  const MO_FIRST_DAY_TOKENS = ["MO", "TU", "WE", "TH", "FR", "SA", "SU"];
57
71
  function padDatePart(value) {
58
72
  return String(value).padStart(2, "0");
@@ -110,6 +124,8 @@ function createDefaultFormState(defaultDate, now = /* @__PURE__ */ new Date(), r
110
124
  category: null,
111
125
  location: "",
112
126
  participants: [],
127
+ resources: [],
128
+ preservedLinkedEntities: [],
113
129
  assigneeUserId: null,
114
130
  assigneeName: null,
115
131
  priority: "medium",
@@ -168,8 +184,17 @@ function buildInteractionPayload(state, options) {
168
184
  status: participant.isCustomer ? "customer" : "pending"
169
185
  })) : null
170
186
  };
171
- if (config.people === "assignee") payload.ownerUserId = state.assigneeUserId ?? null;
187
+ if (config.people === "assignee" && (options.staffEnabled ?? true)) {
188
+ payload.ownerUserId = state.assigneeUserId ?? null;
189
+ }
172
190
  if (config.hasPriority) payload.priority = PRIORITY_NUMBER[state.priority];
191
+ if (options.resourcesEnabled) {
192
+ const linkedEntities = [
193
+ ...state.preservedLinkedEntities,
194
+ ...state.resources.map((resource) => ({ id: resource.id, type: RESOURCE_LINK_TYPE, label: resource.label }))
195
+ ];
196
+ payload.linkedEntities = linkedEntities.length > 0 ? linkedEntities : null;
197
+ }
173
198
  return payload;
174
199
  }
175
200
  function readUnknownString(value) {
@@ -229,6 +254,7 @@ function parseParticipants(item) {
229
254
  function parseItemToFormState(item) {
230
255
  const kind = editorKindOfInteractionType(item.interactionType);
231
256
  const raw = item.raw;
257
+ const { resources, preservedLinkedEntities } = parseLinkedEntities(raw.linkedEntities);
232
258
  return {
233
259
  kind,
234
260
  title: item.title,
@@ -244,6 +270,8 @@ function parseItemToFormState(item) {
244
270
  category: item.interactionType,
245
271
  location: item.location ?? "",
246
272
  participants: parseParticipants(item),
273
+ resources,
274
+ preservedLinkedEntities,
247
275
  assigneeUserId: item.ownerUserId,
248
276
  assigneeName: null,
249
277
  priority: priorityFromNumber(readUnknownNumber(raw.priority)),
@@ -253,9 +281,11 @@ function parseItemToFormState(item) {
253
281
  }
254
282
  export {
255
283
  EDITOR_KINDS,
284
+ EDITOR_KIND_ICONS,
256
285
  KIND_CONFIG,
257
286
  PRIORITY_NUMBER,
258
- buildEditorCategoryOptions,
287
+ RESOURCE_LINK_TYPE,
288
+ buildEditorTypeOptions,
259
289
  buildInteractionPayload,
260
290
  buildRecurrenceRule,
261
291
  computeDurationMinutes,
@@ -265,6 +295,7 @@ export {
265
295
  formatLocalDateInput,
266
296
  formatLocalTimeInput,
267
297
  parseItemToFormState,
298
+ parseLinkedEntities,
268
299
  priorityFromNumber,
269
300
  resolveSavedOwnerUserId
270
301
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/lib/calendar/editorPayload.ts"],
4
- "sourcesContent": ["import type { CalendarItem } from '../../components/calendar/types'\nimport { parseRecurrenceRule } from './recurrence'\n\nexport type EditorKind = 'meeting' | 'call' | 'email' | 'note' | 'event' | 'task'\n\nexport const EDITOR_KINDS: EditorKind[] = ['meeting', 'call', 'email', 'note', 'event', 'task']\n\nexport type EditorDateLabel = 'starts' | 'when' | 'sent' | 'logged' | 'due'\n\nexport type EditorPeopleMode = 'attendees' | 'participants' | 'to' | 'assignee'\n\nexport type EditorKindConfig = {\n dateLabel: EditorDateLabel\n hasEnd: boolean\n hasAllDay: boolean\n hasRepeat: boolean\n location: 'location' | 'phoneLink' | null\n people: EditorPeopleMode | null\n hasPriority: boolean\n}\n\nexport const KIND_CONFIG: Record<EditorKind, EditorKindConfig> = {\n meeting: { dateLabel: 'starts', hasEnd: true, hasAllDay: true, hasRepeat: true, location: 'location', people: 'attendees', hasPriority: false },\n call: { dateLabel: 'when', hasEnd: false, hasAllDay: true, hasRepeat: true, location: 'phoneLink', people: 'participants', hasPriority: false },\n email: { dateLabel: 'sent', hasEnd: false, hasAllDay: true, hasRepeat: true, location: null, people: 'to', hasPriority: false },\n note: { dateLabel: 'logged', hasEnd: false, hasAllDay: false, hasRepeat: false, location: null, people: null, hasPriority: false },\n event: { dateLabel: 'starts', hasEnd: true, hasAllDay: true, hasRepeat: true, location: 'location', people: 'attendees', hasPriority: false },\n task: { dateLabel: 'due', hasEnd: false, hasAllDay: true, hasRepeat: true, location: null, people: 'assignee', hasPriority: true },\n}\n\nconst KIND_BY_INTERACTION_TYPE: Record<string, EditorKind> = {\n meeting: 'meeting',\n call: 'call',\n 'video-call': 'call',\n email: 'email',\n note: 'note',\n event: 'event',\n webinar: 'event',\n task: 'task',\n todo: 'task',\n deadline: 'task',\n}\n\nexport function editorKindOfInteractionType(interactionType: string): EditorKind {\n return KIND_BY_INTERACTION_TYPE[interactionType] ?? 'meeting'\n}\n\nexport type EditorCategoryOption = { value: string; label: string }\n\n/**\n * Builds the editor's Category quick-pick options from the tenant dictionary and\n * the user's calendar preferences:\n * - `surfacedTypes` (settings \"Activity Types\") filters which dictionary types are\n * offered \u2014 a non-empty list keeps only matching labels; empty means \"show all\".\n * - `eventCategories` (settings \"Event Categories\") add custom quick-pick labels.\n * Dictionary entries keep their canonical key as the value (so category\u2192tab/tint\n * mapping is preserved); custom labels use the label as the value.\n */\nexport function buildEditorCategoryOptions(params: {\n typeLabels: Record<string, string>\n surfacedTypes: string[]\n eventCategories: string[]\n selectedValue: string\n selectedFallbackLabel: string\n}): EditorCategoryOption[] {\n const { typeLabels, surfacedTypes, eventCategories, selectedValue, selectedFallbackLabel } = params\n const dictionaryOptions: EditorCategoryOption[] = Object.entries(typeLabels).map(([value, label]) => ({ value, label }))\n const surfacedSet = new Set(surfacedTypes)\n const filtered =\n surfacedTypes.length > 0\n ? dictionaryOptions.filter((option) => surfacedSet.has(option.label))\n : dictionaryOptions\n const options = [...filtered]\n const knownLabels = new Set(options.map((option) => option.label))\n const knownValues = new Set(options.map((option) => option.value))\n const customLabels: string[] = []\n for (const label of [...eventCategories, ...surfacedTypes]) {\n const trimmed = label.trim()\n if (!trimmed || knownLabels.has(trimmed) || customLabels.includes(trimmed)) continue\n customLabels.push(trimmed)\n }\n for (const label of customLabels) {\n options.push({ value: label, label })\n knownValues.add(label)\n }\n if (!knownValues.has(selectedValue)) {\n options.unshift({ value: selectedValue, label: typeLabels[selectedValue] ?? selectedFallbackLabel })\n }\n return options\n}\n\nexport type EditorRepeatFreq = 'none' | 'daily' | 'weekly'\n\nexport type EditorRepeatEndType = 'never' | 'date' | 'count'\n\nexport type EditorPriority = 'low' | 'medium' | 'high'\n\nexport const PRIORITY_NUMBER: Record<EditorPriority, number> = { low: 10, medium: 50, high: 90 }\n\nexport function priorityFromNumber(value: number | null | undefined): EditorPriority {\n if (typeof value !== 'number' || Number.isNaN(value)) return 'medium'\n if (value <= 33) return 'low'\n if (value <= 66) return 'medium'\n return 'high'\n}\n\nexport type EditorRelatedTo = {\n id: string\n kind: 'person' | 'company' | 'unknown'\n label: string\n}\n\nexport type EditorParticipant = {\n userId: string\n name: string\n email?: string\n isCustomer: boolean\n}\n\nexport type EditorFormState = {\n kind: EditorKind\n title: string\n relatedTo: EditorRelatedTo | null\n dealId: string | null\n dealLabel: string | null\n allDay: boolean\n date: string\n startTime: string\n endDate: string\n endTime: string\n repeatFreq: EditorRepeatFreq\n repeatDays: boolean[]\n repeatEndType: EditorRepeatEndType\n repeatCount: number\n repeatUntilDate: string\n category: string | null\n location: string\n participants: EditorParticipant[]\n assigneeUserId: string | null\n assigneeName: string | null\n priority: EditorPriority\n description: string\n status: 'planned' | 'done' | 'canceled'\n}\n\nconst MO_FIRST_DAY_TOKENS = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU'] as const\n\nfunction padDatePart(value: number): string {\n return String(value).padStart(2, '0')\n}\n\nexport function formatLocalDateInput(date: Date): string {\n return `${date.getFullYear()}-${padDatePart(date.getMonth() + 1)}-${padDatePart(date.getDate())}`\n}\n\nexport function formatLocalTimeInput(date: Date): string {\n return `${padDatePart(date.getHours())}:${padDatePart(date.getMinutes())}`\n}\n\nfunction moFirstIndexOfDate(date: Date): number {\n return (date.getDay() + 6) % 7\n}\n\nexport function defaultRepeatDaysForDateInput(dateInput: string): boolean[] {\n const parsed = new Date(`${dateInput}T00:00:00`)\n return defaultRepeatDays(Number.isNaN(parsed.getTime()) ? new Date() : parsed)\n}\n\nfunction defaultRepeatDays(start: Date): boolean[] {\n const days = [false, false, false, false, false, false, false]\n days[moFirstIndexOfDate(start)] = true\n return days\n}\n\n/**\n * Resolves the `ownerUserId` an interaction will actually be SAVED with, so the\n * editor's conflict probe keys on the same owner the grid sees after save (see\n * buildInteractionPayload + the create/update command):\n * - Task (people === 'assignee'): owner = the assignee.\n * - Other kinds: the payload omits owner, so edit preserves the existing owner\n * and create is ownerless (null) \u2014 conflicts then come from participants only.\n */\nexport function resolveSavedOwnerUserId(\n config: EditorKindConfig,\n form: EditorFormState,\n isEdit: boolean,\n existingOwnerUserId: string | null,\n): string | null {\n if (config.people === 'assignee') return form.assigneeUserId ?? null\n return isEdit ? existingOwnerUserId : null\n}\n\nexport function createDefaultFormState(\n defaultDate?: Date | null,\n now: Date = new Date(),\n range?: { start: Date; end: Date } | null,\n): EditorFormState {\n let base: Date\n let end: Date\n if (range) {\n base = new Date(range.start)\n end = new Date(range.end)\n } else {\n base = defaultDate ? new Date(defaultDate) : new Date(now)\n base.setHours(now.getHours(), 0, 0, 0)\n base.setHours(base.getHours() + 1)\n end = new Date(base.getTime() + 90 * 60_000)\n }\n return {\n kind: 'meeting',\n title: '',\n relatedTo: null,\n dealId: null,\n dealLabel: null,\n allDay: false,\n date: formatLocalDateInput(base),\n startTime: formatLocalTimeInput(base),\n endDate: formatLocalDateInput(end),\n endTime: formatLocalTimeInput(end),\n repeatFreq: 'none',\n repeatDays: defaultRepeatDays(base),\n repeatEndType: 'never',\n repeatCount: 8,\n repeatUntilDate: '',\n category: null,\n location: '',\n participants: [],\n assigneeUserId: null,\n assigneeName: null,\n priority: 'medium',\n description: '',\n status: 'planned',\n }\n}\n\nexport function buildRecurrenceRule(state: EditorFormState): string | null {\n if (state.repeatFreq === 'none') return null\n let rule: string\n if (state.repeatFreq === 'daily') {\n rule = 'FREQ=DAILY'\n } else {\n const selected = MO_FIRST_DAY_TOKENS.filter((_, index) => state.repeatDays[index])\n const tokens = selected.length > 0\n ? selected\n : [MO_FIRST_DAY_TOKENS[moFirstIndexOfDate(new Date(`${state.date}T00:00:00`))]]\n rule = `FREQ=WEEKLY;BYDAY=${tokens.join(',')}`\n }\n if (state.repeatEndType === 'count') rule += `;COUNT=${state.repeatCount}`\n if (state.repeatEndType === 'date' && state.repeatUntilDate) {\n rule += `;UNTIL=${state.repeatUntilDate.replace(/-/g, '')}T235959Z`\n }\n return rule\n}\n\nexport function computeDurationMinutes(state: EditorFormState): number | null {\n const start = new Date(`${state.date}T${state.startTime}:00`)\n const end = new Date(`${state.endDate}T${state.endTime}:00`)\n if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) return null\n const minutes = Math.round((end.getTime() - start.getTime()) / 60_000)\n return minutes > 0 ? minutes : null\n}\n\nexport type BuildPayloadOptions = { mode: 'create' | 'edit'; id?: string }\n\nexport function buildInteractionPayload(state: EditorFormState, options: BuildPayloadOptions): Record<string, unknown> {\n const config = KIND_CONFIG[state.kind]\n const time = state.allDay && config.hasAllDay ? '00:00' : state.startTime\n const scheduledAt = new Date(`${state.date}T${time}:00`).toISOString()\n const recurrenceRule = config.hasRepeat ? buildRecurrenceRule(state) : null\n const payload: Record<string, unknown> = {\n ...(options.mode === 'edit' && options.id ? { id: options.id } : {}),\n entityId: state.relatedTo?.id ?? null,\n dealId: state.dealId ?? null,\n interactionType: state.category ?? state.kind,\n title: state.title.trim(),\n body: state.description.trim() || null,\n status: options.mode === 'create' ? 'planned' : state.status,\n date: state.date,\n time,\n scheduledAt,\n durationMinutes: config.hasEnd && !(state.allDay && config.hasAllDay) ? computeDurationMinutes(state) : null,\n allDay: config.hasAllDay ? state.allDay : null,\n location: config.location ? state.location.trim() || null : null,\n recurrenceRule,\n recurrenceEnd:\n recurrenceRule && state.repeatEndType === 'date' && state.repeatUntilDate\n ? new Date(state.repeatUntilDate).toISOString()\n : null,\n participants:\n config.people && config.people !== 'assignee' && state.participants.length > 0\n ? state.participants.map((participant) => ({\n userId: participant.userId,\n name: participant.name,\n email: participant.email,\n status: participant.isCustomer ? 'customer' : 'pending',\n }))\n : null,\n }\n if (config.people === 'assignee') payload.ownerUserId = state.assigneeUserId ?? null\n if (config.hasPriority) payload.priority = PRIORITY_NUMBER[state.priority]\n return payload\n}\n\nfunction readUnknownString(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null\n}\n\nfunction readUnknownNumber(value: unknown): number | null {\n return typeof value === 'number' && Number.isFinite(value) ? value : null\n}\n\ntype ParsedRepeat = Pick<EditorFormState, 'repeatFreq' | 'repeatDays' | 'repeatEndType' | 'repeatCount' | 'repeatUntilDate'>\n\nfunction parseRepeatFromRule(rawRule: unknown, start: Date): ParsedRepeat {\n const fallback: ParsedRepeat = {\n repeatFreq: 'none',\n repeatDays: defaultRepeatDays(start),\n repeatEndType: 'never',\n repeatCount: 8,\n repeatUntilDate: '',\n }\n const ruleText = readUnknownString(rawRule)\n if (!ruleText) return fallback\n const parsed = parseRecurrenceRule(ruleText)\n if (!parsed) return fallback\n const repeatDays = defaultRepeatDays(start)\n if (parsed.byDay) {\n repeatDays.fill(false)\n for (const jsWeekday of parsed.byDay) repeatDays[(jsWeekday + 6) % 7] = true\n }\n let repeatEndType: EditorRepeatEndType = 'never'\n let repeatCount = 8\n let repeatUntilDate = ''\n if (parsed.count !== null) {\n repeatEndType = 'count'\n repeatCount = parsed.count\n } else if (parsed.until) {\n repeatEndType = 'date'\n repeatUntilDate = `${parsed.until.getUTCFullYear()}-${padDatePart(parsed.until.getUTCMonth() + 1)}-${padDatePart(parsed.until.getUTCDate())}`\n }\n return {\n repeatFreq: parsed.freq === 'DAILY' ? 'daily' : 'weekly',\n repeatDays,\n repeatEndType,\n repeatCount,\n repeatUntilDate,\n }\n}\n\nfunction parseParticipants(item: CalendarItem): EditorParticipant[] {\n const rawParticipants = Array.isArray(item.raw.participants) ? item.raw.participants : []\n const statusByUserId = new Map<string, string | null>()\n for (const raw of rawParticipants) {\n statusByUserId.set(raw.userId, readUnknownString((raw as Record<string, unknown>).status))\n }\n return item.participants.map((participant) => ({\n userId: participant.userId,\n name: participant.name ?? participant.email ?? participant.userId,\n email: participant.email,\n isCustomer: statusByUserId.get(participant.userId) === 'customer',\n }))\n}\n\nexport function parseItemToFormState(item: CalendarItem): EditorFormState {\n const kind = editorKindOfInteractionType(item.interactionType)\n const raw = item.raw as Record<string, unknown>\n return {\n kind,\n title: item.title,\n relatedTo: item.entityId ? { id: item.entityId, kind: 'unknown', label: '' } : null,\n dealId: item.dealId,\n dealLabel: null,\n allDay: item.allDay,\n date: formatLocalDateInput(item.start),\n startTime: formatLocalTimeInput(item.start),\n endDate: formatLocalDateInput(item.end),\n endTime: formatLocalTimeInput(item.end),\n ...parseRepeatFromRule(item.raw.recurrenceRule, item.start),\n category: item.interactionType,\n location: item.location ?? '',\n participants: parseParticipants(item),\n assigneeUserId: item.ownerUserId,\n assigneeName: null,\n priority: priorityFromNumber(readUnknownNumber(raw.priority)),\n description: readUnknownString(raw.body) ?? '',\n status: item.status,\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { CalendarItem } from '../../components/calendar/types'\nimport { parseRecurrenceRule } from './recurrence'\n\nexport type EditorKind = 'meeting' | 'call' | 'email' | 'note' | 'event' | 'task'\n\nexport const EDITOR_KINDS: EditorKind[] = ['meeting', 'call', 'email', 'note', 'event', 'task']\n\nexport type EditorDateLabel = 'starts' | 'when' | 'sent' | 'logged' | 'due'\n\nexport type EditorPeopleMode = 'attendees' | 'participants' | 'to' | 'assignee'\n\nexport type EditorKindConfig = {\n dateLabel: EditorDateLabel\n hasEnd: boolean\n hasAllDay: boolean\n hasRepeat: boolean\n location: 'location' | 'phoneLink' | null\n people: EditorPeopleMode | null\n hasPriority: boolean\n}\n\nexport const KIND_CONFIG: Record<EditorKind, EditorKindConfig> = {\n meeting: { dateLabel: 'starts', hasEnd: true, hasAllDay: true, hasRepeat: true, location: 'location', people: 'attendees', hasPriority: false },\n call: { dateLabel: 'when', hasEnd: false, hasAllDay: true, hasRepeat: true, location: 'phoneLink', people: 'participants', hasPriority: false },\n email: { dateLabel: 'sent', hasEnd: false, hasAllDay: true, hasRepeat: true, location: null, people: 'to', hasPriority: false },\n note: { dateLabel: 'logged', hasEnd: false, hasAllDay: false, hasRepeat: false, location: null, people: null, hasPriority: false },\n event: { dateLabel: 'starts', hasEnd: true, hasAllDay: true, hasRepeat: true, location: 'location', people: 'attendees', hasPriority: false },\n task: { dateLabel: 'due', hasEnd: false, hasAllDay: true, hasRepeat: true, location: null, people: 'assignee', hasPriority: true },\n}\n\nconst KIND_BY_INTERACTION_TYPE: Record<string, EditorKind> = {\n meeting: 'meeting',\n call: 'call',\n 'video-call': 'call',\n email: 'email',\n note: 'note',\n event: 'event',\n webinar: 'event',\n task: 'task',\n todo: 'task',\n deadline: 'task',\n}\n\nexport function editorKindOfInteractionType(interactionType: string): EditorKind {\n return KIND_BY_INTERACTION_TYPE[interactionType] ?? 'meeting'\n}\n\nexport type EditorTypeOption = { value: string; label: string; icon: string | null }\n\n/** Fallback switcher icons matching the seeded dictionary appearance icons. */\nexport const EDITOR_KIND_ICONS: Record<EditorKind, string> = {\n meeting: 'lucide:users',\n call: 'lucide:phone-call',\n email: 'lucide:mail',\n note: 'lucide:notebook',\n event: 'lucide:calendar',\n task: 'lucide:check-square',\n}\n\n/**\n * Builds the editor's type-switcher options from the tenant `activity-types`\n * dictionary (#3552) so tenant-added types are first-class calendar event types,\n * not just Category values. Field morphology per option resolves through\n * `editorKindOfInteractionType` (custom types get the meeting-shaped default).\n * Falls back to the built-in kinds when the dictionary is empty/unavailable,\n * and always includes the currently selected value (e.g. a since-deleted type\n * on an existing event). Icons come from the dictionary entry appearance with\n * the seeded kind icons as fallback.\n */\nexport function buildEditorTypeOptions(params: {\n typeLabels: Record<string, string>\n typeIcons?: Record<string, string | null>\n selectedValue: string\n kindLabels: Record<EditorKind, string>\n}): EditorTypeOption[] {\n const { typeLabels, typeIcons, selectedValue, kindLabels } = params\n const iconOf = (value: string): string | null =>\n typeIcons?.[value] ?? EDITOR_KIND_ICONS[editorKindOfInteractionType(value)] ?? null\n const dictionaryEntries = Object.entries(typeLabels)\n const options: EditorTypeOption[] = dictionaryEntries.length\n ? dictionaryEntries.map(([value, label]) => ({ value, label, icon: iconOf(value) }))\n : EDITOR_KINDS.map((kind) => ({ value: kind, label: kindLabels[kind], icon: EDITOR_KIND_ICONS[kind] }))\n if (!options.some((option) => option.value === selectedValue)) {\n options.unshift({\n value: selectedValue,\n label: typeLabels[selectedValue] ?? kindLabels[editorKindOfInteractionType(selectedValue)] ?? selectedValue,\n icon: iconOf(selectedValue),\n })\n }\n return options\n}\n\nexport type EditorRepeatFreq = 'none' | 'daily' | 'weekly'\n\nexport type EditorRepeatEndType = 'never' | 'date' | 'count'\n\nexport type EditorPriority = 'low' | 'medium' | 'high'\n\nexport const PRIORITY_NUMBER: Record<EditorPriority, number> = { low: 10, medium: 50, high: 90 }\n\nexport function priorityFromNumber(value: number | null | undefined): EditorPriority {\n if (typeof value !== 'number' || Number.isNaN(value)) return 'medium'\n if (value <= 33) return 'low'\n if (value <= 66) return 'medium'\n return 'high'\n}\n\nexport type EditorRelatedTo = {\n id: string\n kind: 'person' | 'company' | 'unknown'\n label: string\n}\n\nexport type EditorParticipant = {\n userId: string\n name: string\n email?: string\n isCustomer: boolean\n}\n\n/** linkedEntities `type` marker for resource assignments (FK-id + label snapshot). */\nexport const RESOURCE_LINK_TYPE = 'resource'\n\nexport type EditorLinkedEntity = { id: string; type: string; label: string }\n\nexport type EditorResource = { id: string; label: string }\n\nexport function parseLinkedEntities(raw: unknown): {\n resources: EditorResource[]\n preservedLinkedEntities: EditorLinkedEntity[]\n} {\n const resources: EditorResource[] = []\n const preservedLinkedEntities: EditorLinkedEntity[] = []\n if (!Array.isArray(raw)) return { resources, preservedLinkedEntities }\n for (const entry of raw) {\n if (!entry || typeof entry !== 'object') continue\n const { id, type, label } = entry as Record<string, unknown>\n if (typeof id !== 'string' || !id.length || typeof type !== 'string' || !type.length) continue\n const safeLabel = typeof label === 'string' ? label : ''\n if (type === RESOURCE_LINK_TYPE) resources.push({ id, label: safeLabel || id })\n else preservedLinkedEntities.push({ id, type, label: safeLabel })\n }\n return { resources, preservedLinkedEntities }\n}\n\nexport type EditorFormState = {\n kind: EditorKind\n title: string\n relatedTo: EditorRelatedTo | null\n dealId: string | null\n dealLabel: string | null\n allDay: boolean\n date: string\n startTime: string\n endDate: string\n endTime: string\n repeatFreq: EditorRepeatFreq\n repeatDays: boolean[]\n repeatEndType: EditorRepeatEndType\n repeatCount: number\n repeatUntilDate: string\n category: string | null\n location: string\n participants: EditorParticipant[]\n resources: EditorResource[]\n preservedLinkedEntities: EditorLinkedEntity[]\n assigneeUserId: string | null\n assigneeName: string | null\n priority: EditorPriority\n description: string\n status: 'planned' | 'done' | 'canceled'\n}\n\nconst MO_FIRST_DAY_TOKENS = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU'] as const\n\nfunction padDatePart(value: number): string {\n return String(value).padStart(2, '0')\n}\n\nexport function formatLocalDateInput(date: Date): string {\n return `${date.getFullYear()}-${padDatePart(date.getMonth() + 1)}-${padDatePart(date.getDate())}`\n}\n\nexport function formatLocalTimeInput(date: Date): string {\n return `${padDatePart(date.getHours())}:${padDatePart(date.getMinutes())}`\n}\n\nfunction moFirstIndexOfDate(date: Date): number {\n return (date.getDay() + 6) % 7\n}\n\nexport function defaultRepeatDaysForDateInput(dateInput: string): boolean[] {\n const parsed = new Date(`${dateInput}T00:00:00`)\n return defaultRepeatDays(Number.isNaN(parsed.getTime()) ? new Date() : parsed)\n}\n\nfunction defaultRepeatDays(start: Date): boolean[] {\n const days = [false, false, false, false, false, false, false]\n days[moFirstIndexOfDate(start)] = true\n return days\n}\n\n/**\n * Resolves the `ownerUserId` an interaction will actually be SAVED with, so the\n * editor's conflict probe keys on the same owner the grid sees after save (see\n * buildInteractionPayload + the create/update command):\n * - Task (people === 'assignee'): owner = the assignee.\n * - Other kinds: the payload omits owner, so edit preserves the existing owner\n * and create is ownerless (null) \u2014 conflicts then come from participants only.\n */\nexport function resolveSavedOwnerUserId(\n config: EditorKindConfig,\n form: EditorFormState,\n isEdit: boolean,\n existingOwnerUserId: string | null,\n): string | null {\n if (config.people === 'assignee') return form.assigneeUserId ?? null\n return isEdit ? existingOwnerUserId : null\n}\n\nexport function createDefaultFormState(\n defaultDate?: Date | null,\n now: Date = new Date(),\n range?: { start: Date; end: Date } | null,\n): EditorFormState {\n let base: Date\n let end: Date\n if (range) {\n base = new Date(range.start)\n end = new Date(range.end)\n } else {\n base = defaultDate ? new Date(defaultDate) : new Date(now)\n base.setHours(now.getHours(), 0, 0, 0)\n base.setHours(base.getHours() + 1)\n end = new Date(base.getTime() + 90 * 60_000)\n }\n return {\n kind: 'meeting',\n title: '',\n relatedTo: null,\n dealId: null,\n dealLabel: null,\n allDay: false,\n date: formatLocalDateInput(base),\n startTime: formatLocalTimeInput(base),\n endDate: formatLocalDateInput(end),\n endTime: formatLocalTimeInput(end),\n repeatFreq: 'none',\n repeatDays: defaultRepeatDays(base),\n repeatEndType: 'never',\n repeatCount: 8,\n repeatUntilDate: '',\n category: null,\n location: '',\n participants: [],\n resources: [],\n preservedLinkedEntities: [],\n assigneeUserId: null,\n assigneeName: null,\n priority: 'medium',\n description: '',\n status: 'planned',\n }\n}\n\nexport function buildRecurrenceRule(state: EditorFormState): string | null {\n if (state.repeatFreq === 'none') return null\n let rule: string\n if (state.repeatFreq === 'daily') {\n rule = 'FREQ=DAILY'\n } else {\n const selected = MO_FIRST_DAY_TOKENS.filter((_, index) => state.repeatDays[index])\n const tokens = selected.length > 0\n ? selected\n : [MO_FIRST_DAY_TOKENS[moFirstIndexOfDate(new Date(`${state.date}T00:00:00`))]]\n rule = `FREQ=WEEKLY;BYDAY=${tokens.join(',')}`\n }\n if (state.repeatEndType === 'count') rule += `;COUNT=${state.repeatCount}`\n if (state.repeatEndType === 'date' && state.repeatUntilDate) {\n rule += `;UNTIL=${state.repeatUntilDate.replace(/-/g, '')}T235959Z`\n }\n return rule\n}\n\nexport function computeDurationMinutes(state: EditorFormState): number | null {\n const start = new Date(`${state.date}T${state.startTime}:00`)\n const end = new Date(`${state.endDate}T${state.endTime}:00`)\n if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) return null\n const minutes = Math.round((end.getTime() - start.getTime()) / 60_000)\n return minutes > 0 ? minutes : null\n}\n\nexport type BuildPayloadOptions = {\n mode: 'create' | 'edit'\n id?: string\n /**\n * When the resources module is loaded the payload owns `linkedEntities`\n * (preserved non-resource links + current resource assignments). When it is\n * not, the key is omitted entirely so edits never clobber links written by\n * other surfaces.\n */\n resourcesEnabled?: boolean\n /**\n * When the staff module is absent the Assignee field is hidden, so the\n * payload omits `ownerUserId` (edit keeps the stored owner untouched).\n */\n staffEnabled?: boolean\n}\n\nexport function buildInteractionPayload(state: EditorFormState, options: BuildPayloadOptions): Record<string, unknown> {\n const config = KIND_CONFIG[state.kind]\n const time = state.allDay && config.hasAllDay ? '00:00' : state.startTime\n const scheduledAt = new Date(`${state.date}T${time}:00`).toISOString()\n const recurrenceRule = config.hasRepeat ? buildRecurrenceRule(state) : null\n const payload: Record<string, unknown> = {\n ...(options.mode === 'edit' && options.id ? { id: options.id } : {}),\n entityId: state.relatedTo?.id ?? null,\n dealId: state.dealId ?? null,\n interactionType: state.category ?? state.kind,\n title: state.title.trim(),\n body: state.description.trim() || null,\n status: options.mode === 'create' ? 'planned' : state.status,\n date: state.date,\n time,\n scheduledAt,\n durationMinutes: config.hasEnd && !(state.allDay && config.hasAllDay) ? computeDurationMinutes(state) : null,\n allDay: config.hasAllDay ? state.allDay : null,\n location: config.location ? state.location.trim() || null : null,\n recurrenceRule,\n recurrenceEnd:\n recurrenceRule && state.repeatEndType === 'date' && state.repeatUntilDate\n ? new Date(state.repeatUntilDate).toISOString()\n : null,\n participants:\n config.people && config.people !== 'assignee' && state.participants.length > 0\n ? state.participants.map((participant) => ({\n userId: participant.userId,\n name: participant.name,\n email: participant.email,\n status: participant.isCustomer ? 'customer' : 'pending',\n }))\n : null,\n }\n if (config.people === 'assignee' && (options.staffEnabled ?? true)) {\n payload.ownerUserId = state.assigneeUserId ?? null\n }\n if (config.hasPriority) payload.priority = PRIORITY_NUMBER[state.priority]\n if (options.resourcesEnabled) {\n const linkedEntities: EditorLinkedEntity[] = [\n ...state.preservedLinkedEntities,\n ...state.resources.map((resource) => ({ id: resource.id, type: RESOURCE_LINK_TYPE, label: resource.label })),\n ]\n payload.linkedEntities = linkedEntities.length > 0 ? linkedEntities : null\n }\n return payload\n}\n\nfunction readUnknownString(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null\n}\n\nfunction readUnknownNumber(value: unknown): number | null {\n return typeof value === 'number' && Number.isFinite(value) ? value : null\n}\n\ntype ParsedRepeat = Pick<EditorFormState, 'repeatFreq' | 'repeatDays' | 'repeatEndType' | 'repeatCount' | 'repeatUntilDate'>\n\nfunction parseRepeatFromRule(rawRule: unknown, start: Date): ParsedRepeat {\n const fallback: ParsedRepeat = {\n repeatFreq: 'none',\n repeatDays: defaultRepeatDays(start),\n repeatEndType: 'never',\n repeatCount: 8,\n repeatUntilDate: '',\n }\n const ruleText = readUnknownString(rawRule)\n if (!ruleText) return fallback\n const parsed = parseRecurrenceRule(ruleText)\n if (!parsed) return fallback\n const repeatDays = defaultRepeatDays(start)\n if (parsed.byDay) {\n repeatDays.fill(false)\n for (const jsWeekday of parsed.byDay) repeatDays[(jsWeekday + 6) % 7] = true\n }\n let repeatEndType: EditorRepeatEndType = 'never'\n let repeatCount = 8\n let repeatUntilDate = ''\n if (parsed.count !== null) {\n repeatEndType = 'count'\n repeatCount = parsed.count\n } else if (parsed.until) {\n repeatEndType = 'date'\n repeatUntilDate = `${parsed.until.getUTCFullYear()}-${padDatePart(parsed.until.getUTCMonth() + 1)}-${padDatePart(parsed.until.getUTCDate())}`\n }\n return {\n repeatFreq: parsed.freq === 'DAILY' ? 'daily' : 'weekly',\n repeatDays,\n repeatEndType,\n repeatCount,\n repeatUntilDate,\n }\n}\n\nfunction parseParticipants(item: CalendarItem): EditorParticipant[] {\n const rawParticipants = Array.isArray(item.raw.participants) ? item.raw.participants : []\n const statusByUserId = new Map<string, string | null>()\n for (const raw of rawParticipants) {\n statusByUserId.set(raw.userId, readUnknownString((raw as Record<string, unknown>).status))\n }\n return item.participants.map((participant) => ({\n userId: participant.userId,\n name: participant.name ?? participant.email ?? participant.userId,\n email: participant.email,\n isCustomer: statusByUserId.get(participant.userId) === 'customer',\n }))\n}\n\nexport function parseItemToFormState(item: CalendarItem): EditorFormState {\n const kind = editorKindOfInteractionType(item.interactionType)\n const raw = item.raw as Record<string, unknown>\n const { resources, preservedLinkedEntities } = parseLinkedEntities(raw.linkedEntities)\n return {\n kind,\n title: item.title,\n relatedTo: item.entityId ? { id: item.entityId, kind: 'unknown', label: '' } : null,\n dealId: item.dealId,\n dealLabel: null,\n allDay: item.allDay,\n date: formatLocalDateInput(item.start),\n startTime: formatLocalTimeInput(item.start),\n endDate: formatLocalDateInput(item.end),\n endTime: formatLocalTimeInput(item.end),\n ...parseRepeatFromRule(item.raw.recurrenceRule, item.start),\n category: item.interactionType,\n location: item.location ?? '',\n participants: parseParticipants(item),\n resources,\n preservedLinkedEntities,\n assigneeUserId: item.ownerUserId,\n assigneeName: null,\n priority: priorityFromNumber(readUnknownNumber(raw.priority)),\n description: readUnknownString(raw.body) ?? '',\n status: item.status,\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -442,7 +442,7 @@ function EditDefinitionsPage({ params }) {
442
442
  setOrderDirty(true);
443
443
  },
444
444
  orderNotice: orderDirty ? { dirty: true, saving: orderSaving, message: "Reordered \u2014 will auto-save on blur" } : void 0,
445
- addButtonLabel: "Add Field",
445
+ addButtonLabel: t("entities.customFields.editor.addField", "Add Field"),
446
446
  translate: t,
447
447
  listRef,
448
448
  listProps: {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/entities/backend/entities/user/%5BentityId%5D/page.tsx"],
4
- "sourcesContent": ["\"use client\"\nimport React, { useEffect, useMemo, useState } from 'react'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { invalidateCustomFieldDefs } from '@open-mercato/ui/backend/utils/customFieldDefs'\nimport { upsertCustomEntitySchema, upsertCustomFieldDefSchema } from '@open-mercato/core/modules/entities/data/validators'\nimport { z } from 'zod'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport Link from 'next/link'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { loadGeneratedFieldRegistrations } from '@open-mercato/ui/backend/fields/registry'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { createCrudFormError, raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { FieldDefinitionsEditor, type FieldDefinition, type FieldDefinitionError } from '@open-mercato/ui/backend/custom-fields/FieldDefinitionsEditor'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { normalizeCustomFieldOptions } from '@open-mercato/shared/modules/entities/options'\nimport { TranslationManager } from '@open-mercato/core/modules/translations/components/TranslationManager'\n\ntype Def = FieldDefinition\nexport type EntitySource = 'code' | 'custom'\ntype EntitiesListResponse = { items?: Array<Record<string, unknown>> }\ntype FieldsetGroup = { code: string; title?: string; hint?: string }\ntype FieldsetDefinition = { code: string; label: string; icon?: string; description?: string; groups?: FieldsetGroup[] }\ntype DefinitionsManageResponse = { items?: any[]; deletedKeys?: string[]; fieldsets?: FieldsetDefinition[]; settings?: { singleFieldsetPerRecord?: boolean }; version?: string | null }\ntype DefinitionsBatchResponse = { ok?: boolean; version?: string | null }\n\nfunction readVersionToken(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null\n}\n\ntype DefErrors = FieldDefinitionError\n\n\nexport default function EditDefinitionsPage({ params }: { params?: { entityId?: string } }) {\n React.useEffect(() => { loadGeneratedFieldRegistrations().catch(() => {}) }, [])\n const router = useRouter()\n const searchParams = useSearchParams()\n const t = useT()\n const queryClient = useQueryClient()\n const entityId = useMemo(() => decodeURIComponent((params?.entityId as any) || ''), [params])\n const [label, setLabel] = useState('')\n const [entitySource, setEntitySource] = useState<EntitySource>('custom')\n const [entityFormLoading, setEntityFormLoading] = useState(true)\n const [entityInitial, setEntityInitial] = useState<{ label?: string; description?: string; labelField?: string; defaultEditor?: string; showInSidebar?: boolean; updatedAt?: string }>({})\n const [defs, setDefs] = useState<Def[]>([])\n const [defsVersion, setDefsVersion] = useState<string | null>(null)\n const [orderDirty, setOrderDirty] = useState(false)\n const [orderSaving, setOrderSaving] = useState(false)\n const listRef = React.useRef<HTMLDivElement | null>(null)\n const [loading, setLoading] = useState(true)\n const [saving, setSaving] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [deletedKeys, setDeletedKeys] = useState<string[]>([])\n const [defErrors, setDefErrors] = useState<Record<number, DefErrors>>({})\n const [fieldsets, setFieldsets] = useState<FieldsetDefinition[]>([])\n const [activeFieldset, setActiveFieldset] = useState<string | null>(null)\n const [singleFieldsetPerRecord, setSingleFieldsetPerRecord] = useState(true)\n const [translateDef, setTranslateDef] = useState<{ def: Def; entityId: string } | null>(null)\n\n const translateFields = React.useMemo(() => {\n if (!translateDef) return undefined\n const { def } = translateDef\n const fields: string[] = ['label', 'description']\n const options = normalizeCustomFieldOptions(def.configJson?.options)\n for (const opt of options) {\n if (opt.value) fields.push(`options.${opt.value}.label`)\n }\n return fields\n }, [translateDef])\n\n const translateBaseValues = React.useMemo(() => {\n if (!translateDef) return undefined\n const { def } = translateDef\n const base: Record<string, string> = {}\n if (typeof def.configJson?.label === 'string') base.label = def.configJson.label\n if (typeof def.configJson?.description === 'string') base.description = def.configJson.description\n const options = normalizeCustomFieldOptions(def.configJson?.options)\n for (const opt of options) {\n if (opt.value && opt.label) base[`options.${opt.value}.label`] = opt.label\n }\n return base\n }, [translateDef])\n\n const requestedFieldset = React.useMemo(() => {\n const raw = searchParams?.get('fieldset')\n return raw && raw.trim().length ? raw.trim() : null\n }, [searchParams])\n const embedFieldsetView = React.useMemo(() => searchParams?.get('view') === 'fieldset', [searchParams])\n const normalizeGroupPayload = React.useCallback((value: unknown) => {\n if (!value) return null\n if (typeof value === 'string') {\n const code = value.trim()\n return code ? { code } : null\n }\n if (typeof value !== 'object') return null\n const entry = value as Record<string, unknown>\n const code = typeof entry.code === 'string' ? entry.code.trim() : ''\n if (!code) return null\n const group: FieldsetGroup = { 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\n const buildFieldsetPayload = React.useCallback(() => {\n const groupMap = new Map<string, FieldsetGroup[]>()\n defs.forEach((definition) => {\n const code = typeof definition.configJson?.fieldset === 'string' ? definition.configJson.fieldset : null\n if (!code) return\n const normalized = normalizeGroupPayload(definition.configJson?.group)\n if (!normalized) return\n const list = groupMap.get(code) ?? []\n if (!list.some((entry) => entry.code === normalized.code)) {\n list.push(normalized)\n groupMap.set(code, list)\n }\n })\n return fieldsets.map((fs) => ({\n ...fs,\n groups: groupMap.get(fs.code) ?? [],\n }))\n }, [defs, fieldsets, normalizeGroupPayload])\n\n const validateDef = React.useCallback((d: Def): DefErrors => {\n const parsed = upsertCustomFieldDefSchema.safeParse({ entityId, key: d.key, kind: d.kind, configJson: d.configJson, isActive: d.isActive })\n if (parsed.success) return {}\n const errs: DefErrors = {}\n for (const issue of parsed.error.issues) {\n if ((issue.path || []).includes('key')) errs.key = issue.message\n if ((issue.path || []).includes('kind')) errs.kind = issue.message\n }\n return errs\n }, [entityId, requestedFieldset])\n\n const validateAndSetErrorAt = (index: number, d: Def) => {\n const errs = validateDef(d)\n setDefErrors((prev) => ({ ...prev, [index]: errs }))\n return !errs.key && !errs.kind\n }\n\n const validateAll = () => {\n const nextErrors: Record<number, DefErrors> = {}\n defs.forEach((d, i) => {\n nextErrors[i] = validateDef(d)\n })\n setDefErrors(nextErrors)\n return Object.values(nextErrors).every(e => !e.key && !e.kind)\n }\n\n useEffect(() => {\n let mounted = true\n async function load() {\n setLoading(true)\n try {\n const entJson = await readApiResultOrThrow<EntitiesListResponse>(\n '/api/entities/entities',\n undefined,\n { errorMessage: 'Failed to load entity metadata', fallback: { items: [] } },\n )\n const ent = (entJson.items || []).find((x: any) => x.entityId === entityId)\n if (mounted) {\n const record = ent as Record<string, unknown> | undefined\n const labelValue =\n typeof record?.label === 'string' && record.label.trim().length > 0 ? record.label : entityId\n const descriptionValue = typeof record?.description === 'string' ? record.description : ''\n const labelFieldValue =\n typeof record?.labelField === 'string' && record.labelField.length > 0 ? record.labelField : 'name'\n const defaultEditorValue =\n typeof record?.defaultEditor === 'string' ? record.defaultEditor : ''\n const showInSidebarValue = record?.showInSidebar === true\n const updatedAtValue =\n typeof record?.updatedAt === 'string' && record.updatedAt.length > 0 ? record.updatedAt : undefined\n setLabel(labelValue)\n if (record?.source === 'code' || record?.source === 'custom') setEntitySource(record.source)\n setEntityInitial({\n label: labelValue,\n description: descriptionValue,\n labelField: labelFieldValue,\n defaultEditor: defaultEditorValue,\n showInSidebar: showInSidebarValue,\n updatedAt: updatedAtValue,\n })\n setEntityFormLoading(false)\n }\n const json = await readApiResultOrThrow<DefinitionsManageResponse>(\n `/api/entities/definitions.manage?entityId=${encodeURIComponent(entityId)}`,\n undefined,\n { errorMessage: 'Failed to load entity definitions', fallback: { items: [], deletedKeys: [] } },\n )\n if (mounted) {\n const loaded: Def[] = (json.items || []).map((d: any) => ({ key: d.key, kind: d.kind, configJson: d.configJson || {}, isActive: d.isActive !== false }))\n loaded.sort(\n (a, b) => Number(a.configJson?.priority ?? 0) - Number(b.configJson?.priority ?? 0)\n )\n setDefs(loaded)\n setDefsVersion(readVersionToken(json.version))\n setDefErrors({})\n setDeletedKeys(Array.isArray(json.deletedKeys) ? json.deletedKeys : [])\n const loadedFieldsets = Array.isArray(json.fieldsets) ? json.fieldsets : []\n setFieldsets(loadedFieldsets)\n setActiveFieldset((prev) => {\n if (requestedFieldset && loadedFieldsets.some((fs) => fs.code === requestedFieldset)) {\n return requestedFieldset\n }\n if (prev && loadedFieldsets.some((fs) => fs.code === prev)) return prev\n return loadedFieldsets[0]?.code ?? null\n })\n setSingleFieldsetPerRecord(json.settings?.singleFieldsetPerRecord !== false)\n }\n } catch (e: any) {\n if (mounted) setError(e.message || 'Failed to load')\n } finally {\n if (mounted) setLoading(false)\n }\n }\n if (entityId) load()\n return () => { mounted = false }\n }, [entityId])\n\n function addField() {\n setDefs((arr) => [\n ...arr,\n {\n key: '',\n kind: 'text',\n configJson: activeFieldset ? { fieldset: activeFieldset } : {},\n isActive: true,\n },\n ])\n }\n\n async function restoreField(key: string) {\n try {\n const call = await apiCall('/api/entities/definitions.restore', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ entityId, key }),\n })\n if (!call.ok) {\n await raiseCrudError(call.response, 'Failed to restore field')\n }\n // Reload definitions & deleted keys\n const j2 = await readApiResultOrThrow<DefinitionsManageResponse>(\n `/api/entities/definitions.manage?entityId=${encodeURIComponent(entityId)}`,\n undefined,\n { errorMessage: 'Failed to reload field definitions', fallback: { items: [], deletedKeys: [] } },\n )\n const loaded: Def[] = (j2.items || []).map((d: any) => ({ key: d.key, kind: d.kind, configJson: d.configJson || {}, isActive: d.isActive !== false }))\n loaded.sort(\n (a, b) => Number(a.configJson?.priority ?? 0) - Number(b.configJson?.priority ?? 0)\n )\n setDefs(loaded)\n setDefsVersion(readVersionToken(j2.version))\n setDeletedKeys(Array.isArray(j2.deletedKeys) ? j2.deletedKeys : [])\n flash(`Restored ${key}`, 'success')\n await invalidateCustomFieldDefs(queryClient, entityId)\n } catch (e: any) {\n flash(e?.message || 'Failed to restore field', 'error')\n }\n }\n\n async function saveAll() {\n setSaving(true)\n setError(null)\n try {\n if (!validateAll()) {\n flash('Please fix validation errors in field definitions', 'error')\n throw new Error('Validation failed')\n }\n const payload = {\n entityId,\n definitions: defs.filter(d => !!d.key).map((d) => ({\n key: d.key,\n kind: d.kind,\n configJson: d.configJson,\n isActive: d.isActive !== false,\n })),\n }\n const call = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(defsVersion),\n () => apiCall<DefinitionsBatchResponse>('/api/entities/definitions.batch', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n }),\n )\n if (!call.ok) {\n if (surfaceRecordConflict({ status: call.status, body: call.result }, t)) return\n await raiseCrudError(call.response, 'Failed to save definitions')\n }\n setDefsVersion(readVersionToken(call.result?.version))\n await invalidateCustomFieldDefs(queryClient, entityId)\n router.push(`/backend/entities/user?flash=Definitions%20saved&type=success`)\n } catch (e: any) {\n setError(e.message || 'Failed to save')\n } finally {\n setSaving(false)\n }\n }\n\n async function removeField(idx: number) {\n const def = defs[idx]\n if (!def) return\n if (def.key) {\n try {\n const call = await apiCall<DefinitionsBatchResponse>('/api/entities/definitions', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ entityId, key: def.key }),\n })\n if (!call.ok) {\n await raiseCrudError(call.response, 'Failed to delete field')\n }\n // Keep the optimistic-lock token in sync after an out-of-band delete so a\n // later Save does not falsely 409 against our own change (issue #3152).\n setDefsVersion(readVersionToken(call.result?.version))\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed to delete field'\n flash(message, 'error')\n return\n }\n }\n setDefs((arr) => arr.filter((_, i) => i !== idx))\n setOrderDirty(true)\n if (def.key) {\n await invalidateCustomFieldDefs(queryClient, entityId)\n }\n }\n\n const handleFieldsetCodeChange = React.useCallback((previousCode: string, nextCode: string) => {\n if (!previousCode || !nextCode || previousCode === nextCode) return\n setDefs((arr) =>\n arr.map((entry) => {\n const current = typeof entry.configJson?.fieldset === 'string' ? entry.configJson.fieldset : undefined\n if (current !== previousCode) return entry\n const nextConfig = { ...(entry.configJson || {}) }\n nextConfig.fieldset = nextCode\n return { ...entry, configJson: nextConfig }\n })\n )\n setActiveFieldset((current) => (current === previousCode ? nextCode : current))\n }, [])\n\n const handleFieldsetRemoved = React.useCallback((code: string) => {\n if (!code) return\n setDefs((arr) =>\n arr.map((entry) => {\n const current = typeof entry.configJson?.fieldset === 'string' ? entry.configJson.fieldset : undefined\n if (current !== code) return entry\n const nextConfig = { ...(entry.configJson || {}) }\n delete nextConfig.fieldset\n delete nextConfig.group\n return { ...entry, configJson: nextConfig }\n })\n )\n }, [])\n\n async function saveOrderIfDirty() {\n if (!orderDirty) return\n setOrderSaving(true)\n try {\n // Do not save order when there are invalid keys/kinds\n if (!validateAll()) throw new Error('Validation failed')\n const payload = {\n entityId,\n definitions: defs.filter(d => !!d.key).map((d) => ({\n key: d.key,\n kind: d.kind,\n configJson: d.configJson,\n isActive: d.isActive !== false,\n })),\n }\n const call = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(defsVersion),\n () => apiCall<DefinitionsBatchResponse>('/api/entities/definitions.batch', {\n method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(payload),\n }),\n )\n if (!call.ok) {\n // A stale reorder auto-save (another tab changed the schema first) returns a\n // 409; surface the shared conflict bar and stop the retry loop instead of a\n // generic flash (issue #3152).\n if (surfaceRecordConflict({ status: call.status, body: call.result }, t)) {\n setOrderDirty(false)\n return\n }\n await raiseCrudError(call.response, 'Failed to save order')\n }\n setDefsVersion(readVersionToken(call.result?.version))\n setOrderDirty(false)\n flash('Order saved', 'success')\n await invalidateCustomFieldDefs(queryClient, entityId)\n } catch (e: any) {\n flash(e?.message || 'Failed to save order', 'error')\n } finally {\n setOrderSaving(false)\n }\n }\n\n // Unify loader via CrudForm isLoading; do not return early here\n\n // Schema for inline field-level validation in CrudForm\n const entityFormSchema = upsertCustomEntitySchema\n .pick({ label: true, description: true, defaultEditor: true as any })\n .extend({\n // Allow empty string in the UI select, treat as undefined later\n defaultEditor: z.union([z.enum(['markdown','simpleMarkdown','htmlRichText']).optional(), z.literal('')]).optional(),\n // Include showInSidebar so CrudForm doesn't strip it on submit\n showInSidebar: z.boolean().optional(),\n }) as z.ZodType<Record<string, unknown>>\n\n const metadataFieldsReadOnly = entitySource === 'code'\n const fields: CrudField[] = [\n { id: 'label', label: 'Label', type: 'text', required: true, readOnly: metadataFieldsReadOnly },\n { id: 'description', label: 'Description', type: 'textarea', readOnly: metadataFieldsReadOnly },\n {\n id: 'defaultEditor',\n label: 'Default Editor (multiline)',\n type: 'select',\n readOnly: metadataFieldsReadOnly,\n options: [\n { value: '', label: 'Default (Markdown)' },\n { value: 'markdown', label: 'Markdown (UIW)' },\n { value: 'simpleMarkdown', label: 'Simple Markdown' },\n { value: 'htmlRichText', label: 'HTML Rich Text' },\n ],\n } as any,\n ...(entitySource === 'custom' ? [{ id: 'showInSidebar', label: 'Show in sidebar', type: 'checkbox' }] : []),\n ]\n const renderFieldDefinitions = React.useCallback(() => (\n <FieldDefinitionsEditor\n definitions={defs}\n errors={defErrors}\n deletedKeys={deletedKeys}\n fieldsets={fieldsets}\n activeFieldset={activeFieldset}\n onActiveFieldsetChange={setActiveFieldset}\n onFieldsetsChange={(next) => {\n setFieldsets(next)\n if (!next.some((fs) => fs.code === activeFieldset)) {\n setActiveFieldset(next[0]?.code ?? null)\n }\n }}\n onFieldsetCodeChange={handleFieldsetCodeChange}\n onFieldsetRemoved={handleFieldsetRemoved}\n singleFieldsetPerRecord={singleFieldsetPerRecord}\n onSingleFieldsetPerRecordChange={setSingleFieldsetPerRecord}\n onAddField={addField}\n onRemoveField={(index) => { void removeField(index) }}\n onDefinitionChange={(index, nextDef) => {\n setDefs((arr) => arr.map((entry, idx) => (idx === index ? nextDef : entry)))\n validateAndSetErrorAt(index, nextDef)\n }}\n onTranslate={(def) => setTranslateDef({ def, entityId })}\n onRestoreField={(key) => { void restoreField(key) }}\n onReorder={(from, to) => {\n setDefs((arr) => {\n const next = [...arr]\n const [moved] = next.splice(from, 1)\n next.splice(to, 0, moved)\n return next\n })\n setOrderDirty(true)\n }}\n orderNotice={orderDirty ? { dirty: true, saving: orderSaving, message: 'Reordered \u2014 will auto-save on blur' } : undefined}\n addButtonLabel=\"Add Field\"\n translate={t}\n listRef={listRef}\n listProps={{\n tabIndex: -1,\n onBlur: (event) => {\n const current = listRef.current\n const next = event.relatedTarget as Node | null\n if (!current) return\n if (!next || !current.contains(next)) {\n void saveOrderIfDirty()\n }\n },\n }}\n />\n ),\n [defs, defErrors, deletedKeys, fieldsets, activeFieldset, singleFieldsetPerRecord, orderDirty, orderSaving, addField, removeField, restoreField, saveOrderIfDirty])\n\n const definitionsGroup: CrudFormGroup = { id: 'definitions', title: 'Field Definitions', column: 1, component: renderFieldDefinitions }\n\n const groups: CrudFormGroup[] = [\n { id: 'settings', title: 'Entity Settings', column: 1, fields: entitySource === 'custom' ? ['label','description','defaultEditor','showInSidebar'] : ['label','description','defaultEditor'] },\n definitionsGroup,\n ]\n\n const handleCrudFormSubmit = React.useCallback(async (vals: Record<string, unknown>) => {\n if (!entityId) {\n throw createCrudFormError('Invalid entity ID')\n }\n if (!validateAll()) {\n flash('Please fix validation errors in field definitions', 'error')\n throw createCrudFormError('Please fix validation errors in field definitions')\n }\n // Code-declared system entities are not registered as custom entities \u2014 their\n // metadata is owned by code and `POST /api/entities/entities` is fail-closed for\n // ORM-backed system ids (#3115). Only their field definitions are user-editable, so\n // skip the registration call and persist definitions below.\n if (shouldRegisterEntityMetadata(entitySource)) {\n const entityPayload = buildEntityMetadataPayload(entitySource, vals)\n if (!entityPayload) throw createCrudFormError('Validation failed')\n const callEntity = await apiCall('/api/entities/entities', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ entityId, ...entityPayload }),\n })\n if (!callEntity.ok) {\n await raiseCrudError(callEntity.response, 'Failed to save entity')\n }\n try { window.dispatchEvent(new Event('om:refresh-sidebar')) } catch {}\n }\n const defsPayload = buildDefinitionsBatchPayload({\n entityId,\n defs,\n fieldsets: buildFieldsetPayload(),\n singleFieldsetPerRecord,\n })\n // Send the aggregate schema version so a concurrent edit is rejected with a 409\n // instead of silently overwriting the other tab (issue #3152). CrudForm's submit\n // catch detects the optimistic-lock conflict and shows the shared conflict bar.\n const callDefs = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(defsVersion),\n () => apiCall<DefinitionsBatchResponse>('/api/entities/definitions.batch', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(defsPayload),\n }),\n )\n if (!callDefs.ok) {\n await raiseCrudError(callDefs.response, 'Failed to save definitions')\n }\n setDefsVersion(readVersionToken(callDefs.result?.version))\n try { window.dispatchEvent(new Event('om:refresh-sidebar')) } catch {}\n await invalidateCustomFieldDefs(queryClient, entityId)\n flash('Definitions saved', 'success')\n }, [buildFieldsetPayload, defs, defsVersion, entityId, entitySource, queryClient, singleFieldsetPerRecord, validateAll])\n\n if (!entityId) {\n return (\n <Page>\n <PageBody>\n <div className=\"p-6\">\n <Alert variant=\"destructive\">\n <AlertTitle>Invalid entity</AlertTitle>\n <AlertDescription>The requested entity ID is missing or invalid.</AlertDescription>\n </Alert>\n </div>\n </PageBody>\n </Page>\n )\n }\n\n if (embedFieldsetView) {\n return (\n <div className=\"p-4\">\n <CrudForm\n schema={entityFormSchema}\n title={`Edit fieldset: ${requestedFieldset ?? entityId}`}\n fields={[]}\n groups={[definitionsGroup]}\n initialValues={entityInitial as any}\n isLoading={entityFormLoading || loading}\n submitLabel=\"Save\"\n deleteVisible={false}\n backHref={undefined}\n cancelHref={undefined}\n embedded\n onSubmit={handleCrudFormSubmit}\n />\n </div>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <CrudForm\n schema={entityFormSchema}\n title={`Edit Entity: ${entityId}`}\n backHref={entitySource === 'code' ? \"/backend/entities/system\" : \"/backend/entities/user\"}\n fields={fields}\n groups={groups}\n initialValues={entityInitial as any}\n isLoading={entityFormLoading || loading}\n submitLabel=\"Save\"\n deleteVisible={entitySource === 'custom'}\n extraActions={entitySource === 'custom' ? (\n <Button variant=\"outline\" asChild>\n <Link href={`/backend/entities/user/${encodeURIComponent(entityId)}/records`}>\n Show Records\n </Link>\n </Button>\n ) : null}\n cancelHref={entitySource === 'code' ? \"/backend/entities/system\" : \"/backend/entities/user\"}\n successRedirect={entitySource === 'code' ? \"/backend/entities/system?flash=Definitions%20saved&type=success\" : \"/backend/entities/user?flash=Definitions%20saved&type=success\"}\n onSubmit={handleCrudFormSubmit}\n onDelete={entitySource === 'custom' ? async () => {\n const callDelete = await apiCall('/api/entities/entities', { method: 'DELETE', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ entityId }) })\n if (!callDelete.ok) {\n await raiseCrudError(callDelete.response, 'Failed to delete entity')\n }\n flash('Entity deleted', 'success')\n try { window.dispatchEvent(new Event('om:refresh-sidebar')) } catch {}\n } : undefined}\n />\n </PageBody>\n <Dialog open={!!translateDef} onOpenChange={(open) => { if (!open) setTranslateDef(null) }}>\n <DialogContent className=\"max-w-2xl max-h-[80vh] overflow-y-auto\">\n <DialogHeader>\n <DialogTitle>{t('translations.manager.translateField', 'Translate field: {{key}}', { key: translateDef?.def.key ?? '' })}</DialogTitle>\n </DialogHeader>\n {translateDef && (\n <TranslationManager\n mode=\"embedded\"\n compact\n entityType=\"entities:custom_field_def\"\n recordId={`${translateDef.entityId}:${translateDef.def.key}`}\n baseValues={translateBaseValues}\n translatableFields={translateFields}\n />\n )}\n </DialogContent>\n </Dialog>\n </Page>\n )\n}\n\nexport function shouldRegisterEntityMetadata(entitySource: EntitySource): boolean {\n return entitySource === 'custom'\n}\n\nexport function buildDefinitionsBatchPayload(options: {\n entityId: string\n defs: Array<Pick<Def, 'key' | 'kind' | 'configJson' | 'isActive'>>\n fieldsets: FieldsetDefinition[]\n singleFieldsetPerRecord: boolean\n}) {\n return {\n entityId: options.entityId,\n definitions: options.defs.filter((d) => !!d.key).map((d) => ({\n key: d.key,\n kind: d.kind,\n configJson: d.configJson,\n isActive: d.isActive !== false,\n })),\n fieldsets: options.fieldsets,\n singleFieldsetPerRecord: options.singleFieldsetPerRecord,\n }\n}\n\nexport function buildEntityMetadataPayload(\n entitySource: EntitySource,\n vals: Record<string, unknown>,\n): Record<string, unknown> | null {\n const partial = entitySource === 'custom'\n ? upsertCustomEntitySchema\n .pick({ label: true, description: true, labelField: true as any, defaultEditor: true as any })\n .extend({ showInSidebar: z.boolean().optional() }) as unknown as z.ZodTypeAny\n : upsertCustomEntitySchema\n .pick({ label: true, description: true, defaultEditor: true as any }) as unknown as z.ZodTypeAny\n const normalized = {\n ...vals,\n defaultEditor: typeof vals.defaultEditor === 'string' && vals.defaultEditor ? vals.defaultEditor : undefined,\n }\n const parsed = partial.safeParse(normalized)\n return parsed.success ? (parsed.data as Record<string, unknown>) : null\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\nimport React, { useEffect, useMemo, useState } from 'react'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { invalidateCustomFieldDefs } from '@open-mercato/ui/backend/utils/customFieldDefs'\nimport { upsertCustomEntitySchema, upsertCustomFieldDefSchema } from '@open-mercato/core/modules/entities/data/validators'\nimport { z } from 'zod'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport Link from 'next/link'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { loadGeneratedFieldRegistrations } from '@open-mercato/ui/backend/fields/registry'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { createCrudFormError, raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { FieldDefinitionsEditor, type FieldDefinition, type FieldDefinitionError } from '@open-mercato/ui/backend/custom-fields/FieldDefinitionsEditor'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { normalizeCustomFieldOptions } from '@open-mercato/shared/modules/entities/options'\nimport { TranslationManager } from '@open-mercato/core/modules/translations/components/TranslationManager'\n\ntype Def = FieldDefinition\nexport type EntitySource = 'code' | 'custom'\ntype EntitiesListResponse = { items?: Array<Record<string, unknown>> }\ntype FieldsetGroup = { code: string; title?: string; hint?: string }\ntype FieldsetDefinition = { code: string; label: string; icon?: string; description?: string; groups?: FieldsetGroup[] }\ntype DefinitionsManageResponse = { items?: any[]; deletedKeys?: string[]; fieldsets?: FieldsetDefinition[]; settings?: { singleFieldsetPerRecord?: boolean }; version?: string | null }\ntype DefinitionsBatchResponse = { ok?: boolean; version?: string | null }\n\nfunction readVersionToken(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null\n}\n\ntype DefErrors = FieldDefinitionError\n\n\nexport default function EditDefinitionsPage({ params }: { params?: { entityId?: string } }) {\n React.useEffect(() => { loadGeneratedFieldRegistrations().catch(() => {}) }, [])\n const router = useRouter()\n const searchParams = useSearchParams()\n const t = useT()\n const queryClient = useQueryClient()\n const entityId = useMemo(() => decodeURIComponent((params?.entityId as any) || ''), [params])\n const [label, setLabel] = useState('')\n const [entitySource, setEntitySource] = useState<EntitySource>('custom')\n const [entityFormLoading, setEntityFormLoading] = useState(true)\n const [entityInitial, setEntityInitial] = useState<{ label?: string; description?: string; labelField?: string; defaultEditor?: string; showInSidebar?: boolean; updatedAt?: string }>({})\n const [defs, setDefs] = useState<Def[]>([])\n const [defsVersion, setDefsVersion] = useState<string | null>(null)\n const [orderDirty, setOrderDirty] = useState(false)\n const [orderSaving, setOrderSaving] = useState(false)\n const listRef = React.useRef<HTMLDivElement | null>(null)\n const [loading, setLoading] = useState(true)\n const [saving, setSaving] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [deletedKeys, setDeletedKeys] = useState<string[]>([])\n const [defErrors, setDefErrors] = useState<Record<number, DefErrors>>({})\n const [fieldsets, setFieldsets] = useState<FieldsetDefinition[]>([])\n const [activeFieldset, setActiveFieldset] = useState<string | null>(null)\n const [singleFieldsetPerRecord, setSingleFieldsetPerRecord] = useState(true)\n const [translateDef, setTranslateDef] = useState<{ def: Def; entityId: string } | null>(null)\n\n const translateFields = React.useMemo(() => {\n if (!translateDef) return undefined\n const { def } = translateDef\n const fields: string[] = ['label', 'description']\n const options = normalizeCustomFieldOptions(def.configJson?.options)\n for (const opt of options) {\n if (opt.value) fields.push(`options.${opt.value}.label`)\n }\n return fields\n }, [translateDef])\n\n const translateBaseValues = React.useMemo(() => {\n if (!translateDef) return undefined\n const { def } = translateDef\n const base: Record<string, string> = {}\n if (typeof def.configJson?.label === 'string') base.label = def.configJson.label\n if (typeof def.configJson?.description === 'string') base.description = def.configJson.description\n const options = normalizeCustomFieldOptions(def.configJson?.options)\n for (const opt of options) {\n if (opt.value && opt.label) base[`options.${opt.value}.label`] = opt.label\n }\n return base\n }, [translateDef])\n\n const requestedFieldset = React.useMemo(() => {\n const raw = searchParams?.get('fieldset')\n return raw && raw.trim().length ? raw.trim() : null\n }, [searchParams])\n const embedFieldsetView = React.useMemo(() => searchParams?.get('view') === 'fieldset', [searchParams])\n const normalizeGroupPayload = React.useCallback((value: unknown) => {\n if (!value) return null\n if (typeof value === 'string') {\n const code = value.trim()\n return code ? { code } : null\n }\n if (typeof value !== 'object') return null\n const entry = value as Record<string, unknown>\n const code = typeof entry.code === 'string' ? entry.code.trim() : ''\n if (!code) return null\n const group: FieldsetGroup = { 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\n const buildFieldsetPayload = React.useCallback(() => {\n const groupMap = new Map<string, FieldsetGroup[]>()\n defs.forEach((definition) => {\n const code = typeof definition.configJson?.fieldset === 'string' ? definition.configJson.fieldset : null\n if (!code) return\n const normalized = normalizeGroupPayload(definition.configJson?.group)\n if (!normalized) return\n const list = groupMap.get(code) ?? []\n if (!list.some((entry) => entry.code === normalized.code)) {\n list.push(normalized)\n groupMap.set(code, list)\n }\n })\n return fieldsets.map((fs) => ({\n ...fs,\n groups: groupMap.get(fs.code) ?? [],\n }))\n }, [defs, fieldsets, normalizeGroupPayload])\n\n const validateDef = React.useCallback((d: Def): DefErrors => {\n const parsed = upsertCustomFieldDefSchema.safeParse({ entityId, key: d.key, kind: d.kind, configJson: d.configJson, isActive: d.isActive })\n if (parsed.success) return {}\n const errs: DefErrors = {}\n for (const issue of parsed.error.issues) {\n if ((issue.path || []).includes('key')) errs.key = issue.message\n if ((issue.path || []).includes('kind')) errs.kind = issue.message\n }\n return errs\n }, [entityId, requestedFieldset])\n\n const validateAndSetErrorAt = (index: number, d: Def) => {\n const errs = validateDef(d)\n setDefErrors((prev) => ({ ...prev, [index]: errs }))\n return !errs.key && !errs.kind\n }\n\n const validateAll = () => {\n const nextErrors: Record<number, DefErrors> = {}\n defs.forEach((d, i) => {\n nextErrors[i] = validateDef(d)\n })\n setDefErrors(nextErrors)\n return Object.values(nextErrors).every(e => !e.key && !e.kind)\n }\n\n useEffect(() => {\n let mounted = true\n async function load() {\n setLoading(true)\n try {\n const entJson = await readApiResultOrThrow<EntitiesListResponse>(\n '/api/entities/entities',\n undefined,\n { errorMessage: 'Failed to load entity metadata', fallback: { items: [] } },\n )\n const ent = (entJson.items || []).find((x: any) => x.entityId === entityId)\n if (mounted) {\n const record = ent as Record<string, unknown> | undefined\n const labelValue =\n typeof record?.label === 'string' && record.label.trim().length > 0 ? record.label : entityId\n const descriptionValue = typeof record?.description === 'string' ? record.description : ''\n const labelFieldValue =\n typeof record?.labelField === 'string' && record.labelField.length > 0 ? record.labelField : 'name'\n const defaultEditorValue =\n typeof record?.defaultEditor === 'string' ? record.defaultEditor : ''\n const showInSidebarValue = record?.showInSidebar === true\n const updatedAtValue =\n typeof record?.updatedAt === 'string' && record.updatedAt.length > 0 ? record.updatedAt : undefined\n setLabel(labelValue)\n if (record?.source === 'code' || record?.source === 'custom') setEntitySource(record.source)\n setEntityInitial({\n label: labelValue,\n description: descriptionValue,\n labelField: labelFieldValue,\n defaultEditor: defaultEditorValue,\n showInSidebar: showInSidebarValue,\n updatedAt: updatedAtValue,\n })\n setEntityFormLoading(false)\n }\n const json = await readApiResultOrThrow<DefinitionsManageResponse>(\n `/api/entities/definitions.manage?entityId=${encodeURIComponent(entityId)}`,\n undefined,\n { errorMessage: 'Failed to load entity definitions', fallback: { items: [], deletedKeys: [] } },\n )\n if (mounted) {\n const loaded: Def[] = (json.items || []).map((d: any) => ({ key: d.key, kind: d.kind, configJson: d.configJson || {}, isActive: d.isActive !== false }))\n loaded.sort(\n (a, b) => Number(a.configJson?.priority ?? 0) - Number(b.configJson?.priority ?? 0)\n )\n setDefs(loaded)\n setDefsVersion(readVersionToken(json.version))\n setDefErrors({})\n setDeletedKeys(Array.isArray(json.deletedKeys) ? json.deletedKeys : [])\n const loadedFieldsets = Array.isArray(json.fieldsets) ? json.fieldsets : []\n setFieldsets(loadedFieldsets)\n setActiveFieldset((prev) => {\n if (requestedFieldset && loadedFieldsets.some((fs) => fs.code === requestedFieldset)) {\n return requestedFieldset\n }\n if (prev && loadedFieldsets.some((fs) => fs.code === prev)) return prev\n return loadedFieldsets[0]?.code ?? null\n })\n setSingleFieldsetPerRecord(json.settings?.singleFieldsetPerRecord !== false)\n }\n } catch (e: any) {\n if (mounted) setError(e.message || 'Failed to load')\n } finally {\n if (mounted) setLoading(false)\n }\n }\n if (entityId) load()\n return () => { mounted = false }\n }, [entityId])\n\n function addField() {\n setDefs((arr) => [\n ...arr,\n {\n key: '',\n kind: 'text',\n configJson: activeFieldset ? { fieldset: activeFieldset } : {},\n isActive: true,\n },\n ])\n }\n\n async function restoreField(key: string) {\n try {\n const call = await apiCall('/api/entities/definitions.restore', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ entityId, key }),\n })\n if (!call.ok) {\n await raiseCrudError(call.response, 'Failed to restore field')\n }\n // Reload definitions & deleted keys\n const j2 = await readApiResultOrThrow<DefinitionsManageResponse>(\n `/api/entities/definitions.manage?entityId=${encodeURIComponent(entityId)}`,\n undefined,\n { errorMessage: 'Failed to reload field definitions', fallback: { items: [], deletedKeys: [] } },\n )\n const loaded: Def[] = (j2.items || []).map((d: any) => ({ key: d.key, kind: d.kind, configJson: d.configJson || {}, isActive: d.isActive !== false }))\n loaded.sort(\n (a, b) => Number(a.configJson?.priority ?? 0) - Number(b.configJson?.priority ?? 0)\n )\n setDefs(loaded)\n setDefsVersion(readVersionToken(j2.version))\n setDeletedKeys(Array.isArray(j2.deletedKeys) ? j2.deletedKeys : [])\n flash(`Restored ${key}`, 'success')\n await invalidateCustomFieldDefs(queryClient, entityId)\n } catch (e: any) {\n flash(e?.message || 'Failed to restore field', 'error')\n }\n }\n\n async function saveAll() {\n setSaving(true)\n setError(null)\n try {\n if (!validateAll()) {\n flash('Please fix validation errors in field definitions', 'error')\n throw new Error('Validation failed')\n }\n const payload = {\n entityId,\n definitions: defs.filter(d => !!d.key).map((d) => ({\n key: d.key,\n kind: d.kind,\n configJson: d.configJson,\n isActive: d.isActive !== false,\n })),\n }\n const call = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(defsVersion),\n () => apiCall<DefinitionsBatchResponse>('/api/entities/definitions.batch', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n }),\n )\n if (!call.ok) {\n if (surfaceRecordConflict({ status: call.status, body: call.result }, t)) return\n await raiseCrudError(call.response, 'Failed to save definitions')\n }\n setDefsVersion(readVersionToken(call.result?.version))\n await invalidateCustomFieldDefs(queryClient, entityId)\n router.push(`/backend/entities/user?flash=Definitions%20saved&type=success`)\n } catch (e: any) {\n setError(e.message || 'Failed to save')\n } finally {\n setSaving(false)\n }\n }\n\n async function removeField(idx: number) {\n const def = defs[idx]\n if (!def) return\n if (def.key) {\n try {\n const call = await apiCall<DefinitionsBatchResponse>('/api/entities/definitions', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ entityId, key: def.key }),\n })\n if (!call.ok) {\n await raiseCrudError(call.response, 'Failed to delete field')\n }\n // Keep the optimistic-lock token in sync after an out-of-band delete so a\n // later Save does not falsely 409 against our own change (issue #3152).\n setDefsVersion(readVersionToken(call.result?.version))\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed to delete field'\n flash(message, 'error')\n return\n }\n }\n setDefs((arr) => arr.filter((_, i) => i !== idx))\n setOrderDirty(true)\n if (def.key) {\n await invalidateCustomFieldDefs(queryClient, entityId)\n }\n }\n\n const handleFieldsetCodeChange = React.useCallback((previousCode: string, nextCode: string) => {\n if (!previousCode || !nextCode || previousCode === nextCode) return\n setDefs((arr) =>\n arr.map((entry) => {\n const current = typeof entry.configJson?.fieldset === 'string' ? entry.configJson.fieldset : undefined\n if (current !== previousCode) return entry\n const nextConfig = { ...(entry.configJson || {}) }\n nextConfig.fieldset = nextCode\n return { ...entry, configJson: nextConfig }\n })\n )\n setActiveFieldset((current) => (current === previousCode ? nextCode : current))\n }, [])\n\n const handleFieldsetRemoved = React.useCallback((code: string) => {\n if (!code) return\n setDefs((arr) =>\n arr.map((entry) => {\n const current = typeof entry.configJson?.fieldset === 'string' ? entry.configJson.fieldset : undefined\n if (current !== code) return entry\n const nextConfig = { ...(entry.configJson || {}) }\n delete nextConfig.fieldset\n delete nextConfig.group\n return { ...entry, configJson: nextConfig }\n })\n )\n }, [])\n\n async function saveOrderIfDirty() {\n if (!orderDirty) return\n setOrderSaving(true)\n try {\n // Do not save order when there are invalid keys/kinds\n if (!validateAll()) throw new Error('Validation failed')\n const payload = {\n entityId,\n definitions: defs.filter(d => !!d.key).map((d) => ({\n key: d.key,\n kind: d.kind,\n configJson: d.configJson,\n isActive: d.isActive !== false,\n })),\n }\n const call = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(defsVersion),\n () => apiCall<DefinitionsBatchResponse>('/api/entities/definitions.batch', {\n method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(payload),\n }),\n )\n if (!call.ok) {\n // A stale reorder auto-save (another tab changed the schema first) returns a\n // 409; surface the shared conflict bar and stop the retry loop instead of a\n // generic flash (issue #3152).\n if (surfaceRecordConflict({ status: call.status, body: call.result }, t)) {\n setOrderDirty(false)\n return\n }\n await raiseCrudError(call.response, 'Failed to save order')\n }\n setDefsVersion(readVersionToken(call.result?.version))\n setOrderDirty(false)\n flash('Order saved', 'success')\n await invalidateCustomFieldDefs(queryClient, entityId)\n } catch (e: any) {\n flash(e?.message || 'Failed to save order', 'error')\n } finally {\n setOrderSaving(false)\n }\n }\n\n // Unify loader via CrudForm isLoading; do not return early here\n\n // Schema for inline field-level validation in CrudForm\n const entityFormSchema = upsertCustomEntitySchema\n .pick({ label: true, description: true, defaultEditor: true as any })\n .extend({\n // Allow empty string in the UI select, treat as undefined later\n defaultEditor: z.union([z.enum(['markdown','simpleMarkdown','htmlRichText']).optional(), z.literal('')]).optional(),\n // Include showInSidebar so CrudForm doesn't strip it on submit\n showInSidebar: z.boolean().optional(),\n }) as z.ZodType<Record<string, unknown>>\n\n const metadataFieldsReadOnly = entitySource === 'code'\n const fields: CrudField[] = [\n { id: 'label', label: 'Label', type: 'text', required: true, readOnly: metadataFieldsReadOnly },\n { id: 'description', label: 'Description', type: 'textarea', readOnly: metadataFieldsReadOnly },\n {\n id: 'defaultEditor',\n label: 'Default Editor (multiline)',\n type: 'select',\n readOnly: metadataFieldsReadOnly,\n options: [\n { value: '', label: 'Default (Markdown)' },\n { value: 'markdown', label: 'Markdown (UIW)' },\n { value: 'simpleMarkdown', label: 'Simple Markdown' },\n { value: 'htmlRichText', label: 'HTML Rich Text' },\n ],\n } as any,\n ...(entitySource === 'custom' ? [{ id: 'showInSidebar', label: 'Show in sidebar', type: 'checkbox' }] : []),\n ]\n const renderFieldDefinitions = React.useCallback(() => (\n <FieldDefinitionsEditor\n definitions={defs}\n errors={defErrors}\n deletedKeys={deletedKeys}\n fieldsets={fieldsets}\n activeFieldset={activeFieldset}\n onActiveFieldsetChange={setActiveFieldset}\n onFieldsetsChange={(next) => {\n setFieldsets(next)\n if (!next.some((fs) => fs.code === activeFieldset)) {\n setActiveFieldset(next[0]?.code ?? null)\n }\n }}\n onFieldsetCodeChange={handleFieldsetCodeChange}\n onFieldsetRemoved={handleFieldsetRemoved}\n singleFieldsetPerRecord={singleFieldsetPerRecord}\n onSingleFieldsetPerRecordChange={setSingleFieldsetPerRecord}\n onAddField={addField}\n onRemoveField={(index) => { void removeField(index) }}\n onDefinitionChange={(index, nextDef) => {\n setDefs((arr) => arr.map((entry, idx) => (idx === index ? nextDef : entry)))\n validateAndSetErrorAt(index, nextDef)\n }}\n onTranslate={(def) => setTranslateDef({ def, entityId })}\n onRestoreField={(key) => { void restoreField(key) }}\n onReorder={(from, to) => {\n setDefs((arr) => {\n const next = [...arr]\n const [moved] = next.splice(from, 1)\n next.splice(to, 0, moved)\n return next\n })\n setOrderDirty(true)\n }}\n orderNotice={orderDirty ? { dirty: true, saving: orderSaving, message: 'Reordered \u2014 will auto-save on blur' } : undefined}\n addButtonLabel={t('entities.customFields.editor.addField', 'Add Field')}\n translate={t}\n listRef={listRef}\n listProps={{\n tabIndex: -1,\n onBlur: (event) => {\n const current = listRef.current\n const next = event.relatedTarget as Node | null\n if (!current) return\n if (!next || !current.contains(next)) {\n void saveOrderIfDirty()\n }\n },\n }}\n />\n ),\n [defs, defErrors, deletedKeys, fieldsets, activeFieldset, singleFieldsetPerRecord, orderDirty, orderSaving, addField, removeField, restoreField, saveOrderIfDirty])\n\n const definitionsGroup: CrudFormGroup = { id: 'definitions', title: 'Field Definitions', column: 1, component: renderFieldDefinitions }\n\n const groups: CrudFormGroup[] = [\n { id: 'settings', title: 'Entity Settings', column: 1, fields: entitySource === 'custom' ? ['label','description','defaultEditor','showInSidebar'] : ['label','description','defaultEditor'] },\n definitionsGroup,\n ]\n\n const handleCrudFormSubmit = React.useCallback(async (vals: Record<string, unknown>) => {\n if (!entityId) {\n throw createCrudFormError('Invalid entity ID')\n }\n if (!validateAll()) {\n flash('Please fix validation errors in field definitions', 'error')\n throw createCrudFormError('Please fix validation errors in field definitions')\n }\n // Code-declared system entities are not registered as custom entities \u2014 their\n // metadata is owned by code and `POST /api/entities/entities` is fail-closed for\n // ORM-backed system ids (#3115). Only their field definitions are user-editable, so\n // skip the registration call and persist definitions below.\n if (shouldRegisterEntityMetadata(entitySource)) {\n const entityPayload = buildEntityMetadataPayload(entitySource, vals)\n if (!entityPayload) throw createCrudFormError('Validation failed')\n const callEntity = await apiCall('/api/entities/entities', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ entityId, ...entityPayload }),\n })\n if (!callEntity.ok) {\n await raiseCrudError(callEntity.response, 'Failed to save entity')\n }\n try { window.dispatchEvent(new Event('om:refresh-sidebar')) } catch {}\n }\n const defsPayload = buildDefinitionsBatchPayload({\n entityId,\n defs,\n fieldsets: buildFieldsetPayload(),\n singleFieldsetPerRecord,\n })\n // Send the aggregate schema version so a concurrent edit is rejected with a 409\n // instead of silently overwriting the other tab (issue #3152). CrudForm's submit\n // catch detects the optimistic-lock conflict and shows the shared conflict bar.\n const callDefs = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(defsVersion),\n () => apiCall<DefinitionsBatchResponse>('/api/entities/definitions.batch', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(defsPayload),\n }),\n )\n if (!callDefs.ok) {\n await raiseCrudError(callDefs.response, 'Failed to save definitions')\n }\n setDefsVersion(readVersionToken(callDefs.result?.version))\n try { window.dispatchEvent(new Event('om:refresh-sidebar')) } catch {}\n await invalidateCustomFieldDefs(queryClient, entityId)\n flash('Definitions saved', 'success')\n }, [buildFieldsetPayload, defs, defsVersion, entityId, entitySource, queryClient, singleFieldsetPerRecord, validateAll])\n\n if (!entityId) {\n return (\n <Page>\n <PageBody>\n <div className=\"p-6\">\n <Alert variant=\"destructive\">\n <AlertTitle>Invalid entity</AlertTitle>\n <AlertDescription>The requested entity ID is missing or invalid.</AlertDescription>\n </Alert>\n </div>\n </PageBody>\n </Page>\n )\n }\n\n if (embedFieldsetView) {\n return (\n <div className=\"p-4\">\n <CrudForm\n schema={entityFormSchema}\n title={`Edit fieldset: ${requestedFieldset ?? entityId}`}\n fields={[]}\n groups={[definitionsGroup]}\n initialValues={entityInitial as any}\n isLoading={entityFormLoading || loading}\n submitLabel=\"Save\"\n deleteVisible={false}\n backHref={undefined}\n cancelHref={undefined}\n embedded\n onSubmit={handleCrudFormSubmit}\n />\n </div>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <CrudForm\n schema={entityFormSchema}\n title={`Edit Entity: ${entityId}`}\n backHref={entitySource === 'code' ? \"/backend/entities/system\" : \"/backend/entities/user\"}\n fields={fields}\n groups={groups}\n initialValues={entityInitial as any}\n isLoading={entityFormLoading || loading}\n submitLabel=\"Save\"\n deleteVisible={entitySource === 'custom'}\n extraActions={entitySource === 'custom' ? (\n <Button variant=\"outline\" asChild>\n <Link href={`/backend/entities/user/${encodeURIComponent(entityId)}/records`}>\n Show Records\n </Link>\n </Button>\n ) : null}\n cancelHref={entitySource === 'code' ? \"/backend/entities/system\" : \"/backend/entities/user\"}\n successRedirect={entitySource === 'code' ? \"/backend/entities/system?flash=Definitions%20saved&type=success\" : \"/backend/entities/user?flash=Definitions%20saved&type=success\"}\n onSubmit={handleCrudFormSubmit}\n onDelete={entitySource === 'custom' ? async () => {\n const callDelete = await apiCall('/api/entities/entities', { method: 'DELETE', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ entityId }) })\n if (!callDelete.ok) {\n await raiseCrudError(callDelete.response, 'Failed to delete entity')\n }\n flash('Entity deleted', 'success')\n try { window.dispatchEvent(new Event('om:refresh-sidebar')) } catch {}\n } : undefined}\n />\n </PageBody>\n <Dialog open={!!translateDef} onOpenChange={(open) => { if (!open) setTranslateDef(null) }}>\n <DialogContent className=\"max-w-2xl max-h-[80vh] overflow-y-auto\">\n <DialogHeader>\n <DialogTitle>{t('translations.manager.translateField', 'Translate field: {{key}}', { key: translateDef?.def.key ?? '' })}</DialogTitle>\n </DialogHeader>\n {translateDef && (\n <TranslationManager\n mode=\"embedded\"\n compact\n entityType=\"entities:custom_field_def\"\n recordId={`${translateDef.entityId}:${translateDef.def.key}`}\n baseValues={translateBaseValues}\n translatableFields={translateFields}\n />\n )}\n </DialogContent>\n </Dialog>\n </Page>\n )\n}\n\nexport function shouldRegisterEntityMetadata(entitySource: EntitySource): boolean {\n return entitySource === 'custom'\n}\n\nexport function buildDefinitionsBatchPayload(options: {\n entityId: string\n defs: Array<Pick<Def, 'key' | 'kind' | 'configJson' | 'isActive'>>\n fieldsets: FieldsetDefinition[]\n singleFieldsetPerRecord: boolean\n}) {\n return {\n entityId: options.entityId,\n definitions: options.defs.filter((d) => !!d.key).map((d) => ({\n key: d.key,\n kind: d.kind,\n configJson: d.configJson,\n isActive: d.isActive !== false,\n })),\n fieldsets: options.fieldsets,\n singleFieldsetPerRecord: options.singleFieldsetPerRecord,\n }\n}\n\nexport function buildEntityMetadataPayload(\n entitySource: EntitySource,\n vals: Record<string, unknown>,\n): Record<string, unknown> | null {\n const partial = entitySource === 'custom'\n ? upsertCustomEntitySchema\n .pick({ label: true, description: true, labelField: true as any, defaultEditor: true as any })\n .extend({ showInSidebar: z.boolean().optional() }) as unknown as z.ZodTypeAny\n : upsertCustomEntitySchema\n .pick({ label: true, description: true, defaultEditor: true as any }) as unknown as z.ZodTypeAny\n const normalized = {\n ...vals,\n defaultEditor: typeof vals.defaultEditor === 'string' && vals.defaultEditor ? vals.defaultEditor : undefined,\n }\n const parsed = partial.safeParse(normalized)\n return parsed.success ? (parsed.data as Record<string, unknown>) : null\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["fields", "code", "error"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.6.6-develop.6519.1.bdc91fb755",
3
+ "version": "0.6.6-develop.6520.1.55df6df792",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -253,16 +253,16 @@
253
253
  "zod": "^4.4.3"
254
254
  },
255
255
  "peerDependencies": {
256
- "@open-mercato/ai-assistant": "0.6.6-develop.6519.1.bdc91fb755",
257
- "@open-mercato/shared": "0.6.6-develop.6519.1.bdc91fb755",
258
- "@open-mercato/ui": "0.6.6-develop.6519.1.bdc91fb755",
256
+ "@open-mercato/ai-assistant": "0.6.6-develop.6520.1.55df6df792",
257
+ "@open-mercato/shared": "0.6.6-develop.6520.1.55df6df792",
258
+ "@open-mercato/ui": "0.6.6-develop.6520.1.55df6df792",
259
259
  "react": "^19.0.0",
260
260
  "react-dom": "^19.0.0"
261
261
  },
262
262
  "devDependencies": {
263
- "@open-mercato/ai-assistant": "0.6.6-develop.6519.1.bdc91fb755",
264
- "@open-mercato/shared": "0.6.6-develop.6519.1.bdc91fb755",
265
- "@open-mercato/ui": "0.6.6-develop.6519.1.bdc91fb755",
263
+ "@open-mercato/ai-assistant": "0.6.6-develop.6520.1.55df6df792",
264
+ "@open-mercato/shared": "0.6.6-develop.6520.1.55df6df792",
265
+ "@open-mercato/ui": "0.6.6-develop.6520.1.55df6df792",
266
266
  "@testing-library/dom": "^10.4.1",
267
267
  "@testing-library/jest-dom": "^6.9.1",
268
268
  "@testing-library/react": "^16.3.1",
@@ -1,11 +1,18 @@
1
1
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
2
+ import { getModules } from '@open-mercato/shared/lib/modules/registry'
2
3
  import { CalendarScreen } from '../../components/calendar/CalendarScreen'
3
4
 
4
5
  export default function CustomersCalendarPage() {
6
+ // Optional-module flags resolved server-side: the editor offers resource
7
+ // assignment / staff lookups only when those modules are loaded (#3552).
8
+ const moduleIds = new Set(getModules().map((module) => module.id))
5
9
  return (
6
10
  <Page>
7
11
  <PageBody>
8
- <CalendarScreen />
12
+ <CalendarScreen
13
+ resourcesEnabled={moduleIds.has('resources')}
14
+ staffEnabled={moduleIds.has('staff')}
15
+ />
9
16
  </PageBody>
10
17
  </Page>
11
18
  )
@@ -6,12 +6,10 @@ import { format } from 'date-fns/format'
6
6
  import { isToday } from 'date-fns/isToday'
7
7
  import { isTomorrow } from 'date-fns/isTomorrow'
8
8
  import { startOfDay } from 'date-fns/startOfDay'
9
- import { CalendarClock } from 'lucide-react'
10
9
  import { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'
11
10
  import { cn } from '@open-mercato/shared/lib/utils'
12
11
  import { Avatar } from '@open-mercato/ui/primitives/avatar'
13
12
  import { Button } from '@open-mercato/ui/primitives/button'
14
- import { EmptyState } from '@open-mercato/ui/primitives/empty-state'
15
13
  import { eventDisplayTitle, pluralCategory } from '../../lib/calendar/labels'
16
14
  import type { AgendaListProps, CalendarCategory, CalendarItem } from './types'
17
15
 
@@ -201,14 +199,6 @@ function AgendaRow({
201
199
  export function AgendaList({ anchor, horizonDays, items, typeLabels, onItemClick }: AgendaListProps) {
202
200
  const t = useT()
203
201
  const groups = React.useMemo(() => buildDayGroups(anchor, horizonDays, items), [anchor, horizonDays, items])
204
- if (groups.length === 0) {
205
- return (
206
- <EmptyState
207
- icon={<CalendarClock className="h-6 w-6" aria-hidden="true" />}
208
- title={t('customers.calendar.empty.agenda', 'Nothing scheduled in this period')}
209
- />
210
- )
211
- }
212
202
  return (
213
203
  <div className="flex w-full flex-col divide-y divide-border overflow-hidden border border-border bg-background">
214
204
  {groups.map((group) => (