@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
@@ -57,6 +57,7 @@ type ActivityTypeDictionaryEntry = {
57
57
  value?: unknown
58
58
  label?: unknown
59
59
  color?: unknown
60
+ icon?: unknown
60
61
  }
61
62
 
62
63
  export type UseCalendarItemsResult = {
@@ -66,6 +67,7 @@ export type UseCalendarItemsResult = {
66
67
  truncated: boolean
67
68
  typeLabels: Record<string, string>
68
69
  typeColors: Record<string, string | null>
70
+ typeIcons: Record<string, string | null>
69
71
  refetch: () => void
70
72
  }
71
73
 
@@ -76,6 +78,7 @@ export function useCalendarItems(range: CalendarRange): UseCalendarItemsResult {
76
78
  const [truncated, setTruncated] = React.useState(false)
77
79
  const [typeLabels, setTypeLabels] = React.useState<Record<string, string>>({})
78
80
  const [typeColors, setTypeColors] = React.useState<Record<string, string | null>>({})
81
+ const [typeIcons, setTypeIcons] = React.useState<Record<string, string | null>>({})
79
82
  const [reloadToken, setReloadToken] = React.useState(0)
80
83
 
81
84
  const fromTime = range.from.getTime()
@@ -93,18 +96,22 @@ export function useCalendarItems(range: CalendarRange): UseCalendarItemsResult {
93
96
  const entries = Array.isArray(call.result?.items) ? call.result.items : []
94
97
  const labels: Record<string, string> = {}
95
98
  const colors: Record<string, string | null> = {}
99
+ const icons: Record<string, string | null> = {}
96
100
  for (const entry of entries) {
97
101
  if (typeof entry?.value !== 'string' || entry.value.length === 0) continue
98
102
  labels[entry.value] = typeof entry.label === 'string' && entry.label.length > 0 ? entry.label : entry.value
99
103
  colors[entry.value] = typeof entry.color === 'string' && entry.color.length > 0 ? entry.color : null
104
+ icons[entry.value] = typeof entry.icon === 'string' && entry.icon.length > 0 ? entry.icon : null
100
105
  }
101
106
  setTypeLabels(labels)
102
107
  setTypeColors(colors)
108
+ setTypeIcons(icons)
103
109
  }
104
110
  loadActivityTypes().catch(() => {
105
111
  if (cancelled || controller.signal.aborted) return
106
112
  setTypeLabels({})
107
113
  setTypeColors({})
114
+ setTypeIcons({})
108
115
  })
109
116
  return () => {
110
117
  cancelled = true
@@ -158,5 +165,5 @@ export function useCalendarItems(range: CalendarRange): UseCalendarItemsResult {
158
165
  setReloadToken((token) => token + 1)
159
166
  }, [])
160
167
 
161
- return { items, isLoading, error, truncated, typeLabels, typeColors, refetch }
168
+ return { items, isLoading, error, truncated, typeLabels, typeColors, typeIcons, refetch }
162
169
  }
@@ -428,7 +428,9 @@ const interactionParticipantSchema = z.object({
428
428
 
429
429
  const interactionLinkedEntitySchema = z.object({
430
430
  id: z.string().uuid(),
431
- type: z.enum(['company', 'deal', 'offer']),
431
+ // 'resource' links calendar events to bookable resources (rooms, cars,
432
+ // equipment) from the optional resources module (#3552).
433
+ type: z.enum(['company', 'deal', 'offer', 'resource']),
432
434
  label: z.string().trim().max(500),
433
435
  })
434
436
 
@@ -237,6 +237,8 @@
237
237
  "customers.calendar.editor.participants": "Teilnehmer",
238
238
  "customers.calendar.editor.phoneLink": "Telefon / Link",
239
239
  "customers.calendar.editor.phoneLinkPlaceholder": "Telefonnummer oder Link hinzufügen…",
240
+ "customers.calendar.editor.phoneSuggestion.add": "Hinzufügen",
241
+ "customers.calendar.editor.phoneSuggestion.use": "Nummer von {name} verwenden",
240
242
  "customers.calendar.editor.priority.high": "Hoch",
241
243
  "customers.calendar.editor.priority.label": "Priorität",
242
244
  "customers.calendar.editor.priority.low": "Niedrig",
@@ -245,6 +247,7 @@
245
247
  "customers.calendar.editor.relatedToPlaceholder": "Personen oder Unternehmen suchen…",
246
248
  "customers.calendar.editor.removePerson": "{name} entfernen",
247
249
  "customers.calendar.editor.removeRelated": "Verknüpfung entfernen",
250
+ "customers.calendar.editor.removeResource": "{name} entfernen",
248
251
  "customers.calendar.editor.repeat.after": "Nach",
249
252
  "customers.calendar.editor.repeat.days.fri": "F",
250
253
  "customers.calendar.editor.repeat.days.mon": "M",
@@ -261,6 +264,12 @@
261
264
  "customers.calendar.editor.repeat.never": "Nie",
262
265
  "customers.calendar.editor.repeat.onDate": "Am Datum",
263
266
  "customers.calendar.editor.repeat.times": "Mal",
267
+ "customers.calendar.editor.resourceTypes.all": "Alle Typen",
268
+ "customers.calendar.editor.resourceTypes.filterLabel": "Ressourcentyp",
269
+ "customers.calendar.editor.resourceTypes.moreHint": "{shown} von {total} angezeigt — tippen zum Eingrenzen",
270
+ "customers.calendar.editor.resourceTypes.searchPlaceholder": "Typen suchen…",
271
+ "customers.calendar.editor.resources": "Ressourcen",
272
+ "customers.calendar.editor.resourcesPlaceholder": "Ressource hinzufügen…",
264
273
  "customers.calendar.editor.save": "Termin speichern",
265
274
  "customers.calendar.editor.saved": "Termin gespeichert",
266
275
  "customers.calendar.editor.saving": "Speichern…",
@@ -279,6 +288,7 @@
279
288
  "customers.calendar.editor.types.meeting": "Meeting",
280
289
  "customers.calendar.editor.types.note": "Notiz",
281
290
  "customers.calendar.editor.types.task": "Aufgabe",
291
+ "customers.calendar.editor.validation.assigneeRequired": "Weise diese Aufgabe einem Teammitglied zu",
282
292
  "customers.calendar.editor.validation.endsBeforeStarts": "Das Ende muss nach dem Beginn liegen",
283
293
  "customers.calendar.editor.validation.relatedToRequired": "Wähle eine Person oder ein Unternehmen aus, um diesen Termin zu verknüpfen",
284
294
  "customers.calendar.editor.validation.titleRequired": "Titel ist erforderlich",
@@ -331,9 +341,9 @@
331
341
  "customers.calendar.settings.cancel": "Abbrechen",
332
342
  "customers.calendar.settings.close": "Schließen",
333
343
  "customers.calendar.settings.conflictScope": "Konfliktbereich",
334
- "customers.calendar.settings.conflictScopeAll": "Alle Termine der Organisation",
344
+ "customers.calendar.settings.conflictScopeAll": "Alle Termine",
335
345
  "customers.calendar.settings.conflictScopeHint": "Wählen Sie, wessen Überschneidungen der Kalender als Konflikte kennzeichnet.",
336
- "customers.calendar.settings.conflictScopeMine": "Nur meine Termine",
346
+ "customers.calendar.settings.conflictScopeMine": "Meine Termine",
337
347
  "customers.calendar.settings.conflictWarnings": "Konfliktwarnungen",
338
348
  "customers.calendar.settings.eventCategories": "Ereigniskategorien",
339
349
  "customers.calendar.settings.eventCategoriesHint": "Eigene Gruppierungslabels (z. B. Teammeeting, Verkaufsanruf). Werden beim Erstellen eines Ereignisses angeboten.",
@@ -237,6 +237,8 @@
237
237
  "customers.calendar.editor.participants": "Participants",
238
238
  "customers.calendar.editor.phoneLink": "Phone / link",
239
239
  "customers.calendar.editor.phoneLinkPlaceholder": "Add a phone number or link…",
240
+ "customers.calendar.editor.phoneSuggestion.add": "Add",
241
+ "customers.calendar.editor.phoneSuggestion.use": "Use {name}’s number",
240
242
  "customers.calendar.editor.priority.high": "High",
241
243
  "customers.calendar.editor.priority.label": "Priority",
242
244
  "customers.calendar.editor.priority.low": "Low",
@@ -245,6 +247,7 @@
245
247
  "customers.calendar.editor.relatedToPlaceholder": "Search people or companies…",
246
248
  "customers.calendar.editor.removePerson": "Remove {name}",
247
249
  "customers.calendar.editor.removeRelated": "Clear related record",
250
+ "customers.calendar.editor.removeResource": "Remove {name}",
248
251
  "customers.calendar.editor.repeat.after": "After",
249
252
  "customers.calendar.editor.repeat.days.fri": "F",
250
253
  "customers.calendar.editor.repeat.days.mon": "M",
@@ -261,6 +264,12 @@
261
264
  "customers.calendar.editor.repeat.never": "Never",
262
265
  "customers.calendar.editor.repeat.onDate": "On date",
263
266
  "customers.calendar.editor.repeat.times": "times",
267
+ "customers.calendar.editor.resourceTypes.all": "All types",
268
+ "customers.calendar.editor.resourceTypes.filterLabel": "Resource type",
269
+ "customers.calendar.editor.resourceTypes.moreHint": "Showing {shown} of {total} — keep typing to narrow",
270
+ "customers.calendar.editor.resourceTypes.searchPlaceholder": "Search types…",
271
+ "customers.calendar.editor.resources": "Resources",
272
+ "customers.calendar.editor.resourcesPlaceholder": "Add a resource…",
264
273
  "customers.calendar.editor.save": "Save event",
265
274
  "customers.calendar.editor.saved": "Event saved",
266
275
  "customers.calendar.editor.saving": "Saving…",
@@ -279,6 +288,7 @@
279
288
  "customers.calendar.editor.types.meeting": "Meeting",
280
289
  "customers.calendar.editor.types.note": "Note",
281
290
  "customers.calendar.editor.types.task": "Task",
291
+ "customers.calendar.editor.validation.assigneeRequired": "Assign this task to a team member",
282
292
  "customers.calendar.editor.validation.endsBeforeStarts": "End must be after start",
283
293
  "customers.calendar.editor.validation.relatedToRequired": "Select a person or company to link this event",
284
294
  "customers.calendar.editor.validation.titleRequired": "Title is required",
@@ -331,9 +341,9 @@
331
341
  "customers.calendar.settings.cancel": "Cancel",
332
342
  "customers.calendar.settings.close": "Close",
333
343
  "customers.calendar.settings.conflictScope": "Conflict scope",
334
- "customers.calendar.settings.conflictScopeAll": "All org meetings",
344
+ "customers.calendar.settings.conflictScopeAll": "All meetings",
335
345
  "customers.calendar.settings.conflictScopeHint": "Choose whose overlaps the calendar flags as conflicts.",
336
- "customers.calendar.settings.conflictScopeMine": "My meetings only",
346
+ "customers.calendar.settings.conflictScopeMine": "My meetings",
337
347
  "customers.calendar.settings.conflictWarnings": "Conflict warnings",
338
348
  "customers.calendar.settings.eventCategories": "Event Categories",
339
349
  "customers.calendar.settings.eventCategoriesHint": "Your own grouping labels (e.g. Team Meeting, Sales Call). Offered when creating an event.",
@@ -237,6 +237,8 @@
237
237
  "customers.calendar.editor.participants": "Participantes",
238
238
  "customers.calendar.editor.phoneLink": "Teléfono / enlace",
239
239
  "customers.calendar.editor.phoneLinkPlaceholder": "Añadir un teléfono o enlace…",
240
+ "customers.calendar.editor.phoneSuggestion.add": "Añadir",
241
+ "customers.calendar.editor.phoneSuggestion.use": "Usar el número de {name}",
240
242
  "customers.calendar.editor.priority.high": "Alta",
241
243
  "customers.calendar.editor.priority.label": "Prioridad",
242
244
  "customers.calendar.editor.priority.low": "Baja",
@@ -245,6 +247,7 @@
245
247
  "customers.calendar.editor.relatedToPlaceholder": "Buscar personas o empresas…",
246
248
  "customers.calendar.editor.removePerson": "Quitar a {name}",
247
249
  "customers.calendar.editor.removeRelated": "Quitar el registro vinculado",
250
+ "customers.calendar.editor.removeResource": "Quitar {name}",
248
251
  "customers.calendar.editor.repeat.after": "Después de",
249
252
  "customers.calendar.editor.repeat.days.fri": "V",
250
253
  "customers.calendar.editor.repeat.days.mon": "L",
@@ -261,6 +264,12 @@
261
264
  "customers.calendar.editor.repeat.never": "Nunca",
262
265
  "customers.calendar.editor.repeat.onDate": "En fecha",
263
266
  "customers.calendar.editor.repeat.times": "veces",
267
+ "customers.calendar.editor.resourceTypes.all": "Todos los tipos",
268
+ "customers.calendar.editor.resourceTypes.filterLabel": "Tipo de recurso",
269
+ "customers.calendar.editor.resourceTypes.moreHint": "Mostrando {shown} de {total} — escribe para acotar",
270
+ "customers.calendar.editor.resourceTypes.searchPlaceholder": "Buscar tipos…",
271
+ "customers.calendar.editor.resources": "Recursos",
272
+ "customers.calendar.editor.resourcesPlaceholder": "Añadir un recurso…",
264
273
  "customers.calendar.editor.save": "Guardar evento",
265
274
  "customers.calendar.editor.saved": "Evento guardado",
266
275
  "customers.calendar.editor.saving": "Guardando…",
@@ -279,6 +288,7 @@
279
288
  "customers.calendar.editor.types.meeting": "Reunión",
280
289
  "customers.calendar.editor.types.note": "Nota",
281
290
  "customers.calendar.editor.types.task": "Tarea",
291
+ "customers.calendar.editor.validation.assigneeRequired": "Asigna esta tarea a un miembro del equipo",
282
292
  "customers.calendar.editor.validation.endsBeforeStarts": "El fin debe ser posterior al inicio",
283
293
  "customers.calendar.editor.validation.relatedToRequired": "Selecciona una persona o empresa para vincular este evento",
284
294
  "customers.calendar.editor.validation.titleRequired": "El título es obligatorio",
@@ -331,9 +341,9 @@
331
341
  "customers.calendar.settings.cancel": "Cancelar",
332
342
  "customers.calendar.settings.close": "Cerrar",
333
343
  "customers.calendar.settings.conflictScope": "Ámbito de conflictos",
334
- "customers.calendar.settings.conflictScopeAll": "Todas las reuniones de la organización",
344
+ "customers.calendar.settings.conflictScopeAll": "Todas las reuniones",
335
345
  "customers.calendar.settings.conflictScopeHint": "Elige qué solapamientos marca el calendario como conflictos.",
336
- "customers.calendar.settings.conflictScopeMine": "Solo mis reuniones",
346
+ "customers.calendar.settings.conflictScopeMine": "Mis reuniones",
337
347
  "customers.calendar.settings.conflictWarnings": "Avisos de conflicto",
338
348
  "customers.calendar.settings.eventCategories": "Categorías de eventos",
339
349
  "customers.calendar.settings.eventCategoriesHint": "Tus propias etiquetas de agrupación (p. ej., Reunión de equipo, Llamada de ventas). Se ofrecen al crear un evento.",
@@ -237,6 +237,8 @@
237
237
  "customers.calendar.editor.participants": "Uczestnicy",
238
238
  "customers.calendar.editor.phoneLink": "Telefon / link",
239
239
  "customers.calendar.editor.phoneLinkPlaceholder": "Dodaj numer telefonu lub link…",
240
+ "customers.calendar.editor.phoneSuggestion.add": "Dodaj",
241
+ "customers.calendar.editor.phoneSuggestion.use": "Użyj numeru: {name}",
240
242
  "customers.calendar.editor.priority.high": "Wysoki",
241
243
  "customers.calendar.editor.priority.label": "Priorytet",
242
244
  "customers.calendar.editor.priority.low": "Niski",
@@ -245,6 +247,7 @@
245
247
  "customers.calendar.editor.relatedToPlaceholder": "Szukaj osób lub firm…",
246
248
  "customers.calendar.editor.removePerson": "Usuń {name}",
247
249
  "customers.calendar.editor.removeRelated": "Usuń powiązanie",
250
+ "customers.calendar.editor.removeResource": "Usuń {name}",
248
251
  "customers.calendar.editor.repeat.after": "Po",
249
252
  "customers.calendar.editor.repeat.days.fri": "P",
250
253
  "customers.calendar.editor.repeat.days.mon": "P",
@@ -261,6 +264,12 @@
261
264
  "customers.calendar.editor.repeat.never": "Nigdy",
262
265
  "customers.calendar.editor.repeat.onDate": "W dniu",
263
266
  "customers.calendar.editor.repeat.times": "razy",
267
+ "customers.calendar.editor.resourceTypes.all": "Wszystkie typy",
268
+ "customers.calendar.editor.resourceTypes.filterLabel": "Typ zasobu",
269
+ "customers.calendar.editor.resourceTypes.moreHint": "Pokazano {shown} z {total} — wpisz, aby zawęzić",
270
+ "customers.calendar.editor.resourceTypes.searchPlaceholder": "Szukaj typów…",
271
+ "customers.calendar.editor.resources": "Zasoby",
272
+ "customers.calendar.editor.resourcesPlaceholder": "Dodaj zasób…",
264
273
  "customers.calendar.editor.save": "Zapisz wydarzenie",
265
274
  "customers.calendar.editor.saved": "Wydarzenie zapisane",
266
275
  "customers.calendar.editor.saving": "Zapisywanie…",
@@ -279,6 +288,7 @@
279
288
  "customers.calendar.editor.types.meeting": "Spotkanie",
280
289
  "customers.calendar.editor.types.note": "Notatka",
281
290
  "customers.calendar.editor.types.task": "Zadanie",
291
+ "customers.calendar.editor.validation.assigneeRequired": "Przypisz to zadanie do członka zespołu",
282
292
  "customers.calendar.editor.validation.endsBeforeStarts": "Zakończenie musi być po rozpoczęciu",
283
293
  "customers.calendar.editor.validation.relatedToRequired": "Wybierz osobę lub firmę, aby powiązać to wydarzenie",
284
294
  "customers.calendar.editor.validation.titleRequired": "Tytuł jest wymagany",
@@ -331,9 +341,9 @@
331
341
  "customers.calendar.settings.cancel": "Anuluj",
332
342
  "customers.calendar.settings.close": "Zamknij",
333
343
  "customers.calendar.settings.conflictScope": "Zakres konfliktów",
334
- "customers.calendar.settings.conflictScopeAll": "Wszystkie spotkania organizacji",
344
+ "customers.calendar.settings.conflictScopeAll": "Wszystkie spotkania",
335
345
  "customers.calendar.settings.conflictScopeHint": "Wybierz, które nakładające się terminy kalendarz oznacza jako konflikty.",
336
- "customers.calendar.settings.conflictScopeMine": "Tylko moje spotkania",
346
+ "customers.calendar.settings.conflictScopeMine": "Moje spotkania",
337
347
  "customers.calendar.settings.conflictWarnings": "Ostrzeżenia o konfliktach",
338
348
  "customers.calendar.settings.eventCategories": "Kategorie wydarzeń",
339
349
  "customers.calendar.settings.eventCategoriesHint": "Twoje własne etykiety grupujące (np. Spotkanie zespołu, Telefon sprzedażowy). Pojawiają się podczas tworzenia wydarzenia.",
@@ -45,46 +45,47 @@ export function editorKindOfInteractionType(interactionType: string): EditorKind
45
45
  return KIND_BY_INTERACTION_TYPE[interactionType] ?? 'meeting'
46
46
  }
47
47
 
48
- export type EditorCategoryOption = { value: string; label: string }
48
+ export type EditorTypeOption = { value: string; label: string; icon: string | null }
49
+
50
+ /** Fallback switcher icons matching the seeded dictionary appearance icons. */
51
+ export const EDITOR_KIND_ICONS: Record<EditorKind, string> = {
52
+ meeting: 'lucide:users',
53
+ call: 'lucide:phone-call',
54
+ email: 'lucide:mail',
55
+ note: 'lucide:notebook',
56
+ event: 'lucide:calendar',
57
+ task: 'lucide:check-square',
58
+ }
49
59
 
50
60
  /**
51
- * Builds the editor's Category quick-pick options from the tenant dictionary and
52
- * the user's calendar preferences:
53
- * - `surfacedTypes` (settings "Activity Types") filters which dictionary types are
54
- * offered a non-empty list keeps only matching labels; empty means "show all".
55
- * - `eventCategories` (settings "Event Categories") add custom quick-pick labels.
56
- * Dictionary entries keep their canonical key as the value (so category→tab/tint
57
- * mapping is preserved); custom labels use the label as the value.
61
+ * Builds the editor's type-switcher options from the tenant `activity-types`
62
+ * dictionary (#3552) so tenant-added types are first-class calendar event types,
63
+ * not just Category values. Field morphology per option resolves through
64
+ * `editorKindOfInteractionType` (custom types get the meeting-shaped default).
65
+ * Falls back to the built-in kinds when the dictionary is empty/unavailable,
66
+ * and always includes the currently selected value (e.g. a since-deleted type
67
+ * on an existing event). Icons come from the dictionary entry appearance with
68
+ * the seeded kind icons as fallback.
58
69
  */
59
- export function buildEditorCategoryOptions(params: {
70
+ export function buildEditorTypeOptions(params: {
60
71
  typeLabels: Record<string, string>
61
- surfacedTypes: string[]
62
- eventCategories: string[]
72
+ typeIcons?: Record<string, string | null>
63
73
  selectedValue: string
64
- selectedFallbackLabel: string
65
- }): EditorCategoryOption[] {
66
- const { typeLabels, surfacedTypes, eventCategories, selectedValue, selectedFallbackLabel } = params
67
- const dictionaryOptions: EditorCategoryOption[] = Object.entries(typeLabels).map(([value, label]) => ({ value, label }))
68
- const surfacedSet = new Set(surfacedTypes)
69
- const filtered =
70
- surfacedTypes.length > 0
71
- ? dictionaryOptions.filter((option) => surfacedSet.has(option.label))
72
- : dictionaryOptions
73
- const options = [...filtered]
74
- const knownLabels = new Set(options.map((option) => option.label))
75
- const knownValues = new Set(options.map((option) => option.value))
76
- const customLabels: string[] = []
77
- for (const label of [...eventCategories, ...surfacedTypes]) {
78
- const trimmed = label.trim()
79
- if (!trimmed || knownLabels.has(trimmed) || customLabels.includes(trimmed)) continue
80
- customLabels.push(trimmed)
81
- }
82
- for (const label of customLabels) {
83
- options.push({ value: label, label })
84
- knownValues.add(label)
85
- }
86
- if (!knownValues.has(selectedValue)) {
87
- options.unshift({ value: selectedValue, label: typeLabels[selectedValue] ?? selectedFallbackLabel })
74
+ kindLabels: Record<EditorKind, string>
75
+ }): EditorTypeOption[] {
76
+ const { typeLabels, typeIcons, selectedValue, kindLabels } = params
77
+ const iconOf = (value: string): string | null =>
78
+ typeIcons?.[value] ?? EDITOR_KIND_ICONS[editorKindOfInteractionType(value)] ?? null
79
+ const dictionaryEntries = Object.entries(typeLabels)
80
+ const options: EditorTypeOption[] = dictionaryEntries.length
81
+ ? dictionaryEntries.map(([value, label]) => ({ value, label, icon: iconOf(value) }))
82
+ : EDITOR_KINDS.map((kind) => ({ value: kind, label: kindLabels[kind], icon: EDITOR_KIND_ICONS[kind] }))
83
+ if (!options.some((option) => option.value === selectedValue)) {
84
+ options.unshift({
85
+ value: selectedValue,
86
+ label: typeLabels[selectedValue] ?? kindLabels[editorKindOfInteractionType(selectedValue)] ?? selectedValue,
87
+ icon: iconOf(selectedValue),
88
+ })
88
89
  }
89
90
  return options
90
91
  }
@@ -117,6 +118,31 @@ export type EditorParticipant = {
117
118
  isCustomer: boolean
118
119
  }
119
120
 
121
+ /** linkedEntities `type` marker for resource assignments (FK-id + label snapshot). */
122
+ export const RESOURCE_LINK_TYPE = 'resource'
123
+
124
+ export type EditorLinkedEntity = { id: string; type: string; label: string }
125
+
126
+ export type EditorResource = { id: string; label: string }
127
+
128
+ export function parseLinkedEntities(raw: unknown): {
129
+ resources: EditorResource[]
130
+ preservedLinkedEntities: EditorLinkedEntity[]
131
+ } {
132
+ const resources: EditorResource[] = []
133
+ const preservedLinkedEntities: EditorLinkedEntity[] = []
134
+ if (!Array.isArray(raw)) return { resources, preservedLinkedEntities }
135
+ for (const entry of raw) {
136
+ if (!entry || typeof entry !== 'object') continue
137
+ const { id, type, label } = entry as Record<string, unknown>
138
+ if (typeof id !== 'string' || !id.length || typeof type !== 'string' || !type.length) continue
139
+ const safeLabel = typeof label === 'string' ? label : ''
140
+ if (type === RESOURCE_LINK_TYPE) resources.push({ id, label: safeLabel || id })
141
+ else preservedLinkedEntities.push({ id, type, label: safeLabel })
142
+ }
143
+ return { resources, preservedLinkedEntities }
144
+ }
145
+
120
146
  export type EditorFormState = {
121
147
  kind: EditorKind
122
148
  title: string
@@ -136,6 +162,8 @@ export type EditorFormState = {
136
162
  category: string | null
137
163
  location: string
138
164
  participants: EditorParticipant[]
165
+ resources: EditorResource[]
166
+ preservedLinkedEntities: EditorLinkedEntity[]
139
167
  assigneeUserId: string | null
140
168
  assigneeName: string | null
141
169
  priority: EditorPriority
@@ -225,6 +253,8 @@ export function createDefaultFormState(
225
253
  category: null,
226
254
  location: '',
227
255
  participants: [],
256
+ resources: [],
257
+ preservedLinkedEntities: [],
228
258
  assigneeUserId: null,
229
259
  assigneeName: null,
230
260
  priority: 'medium',
@@ -260,7 +290,22 @@ export function computeDurationMinutes(state: EditorFormState): number | null {
260
290
  return minutes > 0 ? minutes : null
261
291
  }
262
292
 
263
- export type BuildPayloadOptions = { mode: 'create' | 'edit'; id?: string }
293
+ export type BuildPayloadOptions = {
294
+ mode: 'create' | 'edit'
295
+ id?: string
296
+ /**
297
+ * When the resources module is loaded the payload owns `linkedEntities`
298
+ * (preserved non-resource links + current resource assignments). When it is
299
+ * not, the key is omitted entirely so edits never clobber links written by
300
+ * other surfaces.
301
+ */
302
+ resourcesEnabled?: boolean
303
+ /**
304
+ * When the staff module is absent the Assignee field is hidden, so the
305
+ * payload omits `ownerUserId` (edit keeps the stored owner untouched).
306
+ */
307
+ staffEnabled?: boolean
308
+ }
264
309
 
265
310
  export function buildInteractionPayload(state: EditorFormState, options: BuildPayloadOptions): Record<string, unknown> {
266
311
  const config = KIND_CONFIG[state.kind]
@@ -296,8 +341,17 @@ export function buildInteractionPayload(state: EditorFormState, options: BuildPa
296
341
  }))
297
342
  : null,
298
343
  }
299
- if (config.people === 'assignee') payload.ownerUserId = state.assigneeUserId ?? null
344
+ if (config.people === 'assignee' && (options.staffEnabled ?? true)) {
345
+ payload.ownerUserId = state.assigneeUserId ?? null
346
+ }
300
347
  if (config.hasPriority) payload.priority = PRIORITY_NUMBER[state.priority]
348
+ if (options.resourcesEnabled) {
349
+ const linkedEntities: EditorLinkedEntity[] = [
350
+ ...state.preservedLinkedEntities,
351
+ ...state.resources.map((resource) => ({ id: resource.id, type: RESOURCE_LINK_TYPE, label: resource.label })),
352
+ ]
353
+ payload.linkedEntities = linkedEntities.length > 0 ? linkedEntities : null
354
+ }
301
355
  return payload
302
356
  }
303
357
 
@@ -364,6 +418,7 @@ function parseParticipants(item: CalendarItem): EditorParticipant[] {
364
418
  export function parseItemToFormState(item: CalendarItem): EditorFormState {
365
419
  const kind = editorKindOfInteractionType(item.interactionType)
366
420
  const raw = item.raw as Record<string, unknown>
421
+ const { resources, preservedLinkedEntities } = parseLinkedEntities(raw.linkedEntities)
367
422
  return {
368
423
  kind,
369
424
  title: item.title,
@@ -379,6 +434,8 @@ export function parseItemToFormState(item: CalendarItem): EditorFormState {
379
434
  category: item.interactionType,
380
435
  location: item.location ?? '',
381
436
  participants: parseParticipants(item),
437
+ resources,
438
+ preservedLinkedEntities,
382
439
  assigneeUserId: item.ownerUserId,
383
440
  assigneeName: null,
384
441
  priority: priorityFromNumber(readUnknownNumber(raw.priority)),
@@ -475,7 +475,7 @@ export default function EditDefinitionsPage({ params }: { params?: { entityId?:
475
475
  setOrderDirty(true)
476
476
  }}
477
477
  orderNotice={orderDirty ? { dirty: true, saving: orderSaving, message: 'Reordered — will auto-save on blur' } : undefined}
478
- addButtonLabel="Add Field"
478
+ addButtonLabel={t('entities.customFields.editor.addField', 'Add Field')}
479
479
  translate={t}
480
480
  listRef={listRef}
481
481
  listProps={{
@@ -1,6 +1,126 @@
1
1
  {
2
2
  "entities.customFields.addFirst": "Erstes hinzufügen",
3
3
  "entities.customFields.defaultFieldset": "Standard",
4
+ "entities.customFields.editor.active": "Aktiv",
5
+ "entities.customFields.editor.add": "Hinzufügen",
6
+ "entities.customFields.editor.addField": "Feld hinzufügen",
7
+ "entities.customFields.editor.addFirstFieldset": "Erste Feldgruppe hinzufügen",
8
+ "entities.customFields.editor.addOption": "Option hinzufügen",
9
+ "entities.customFields.editor.addOptionDescription": "Geben Sie den gespeicherten Wert und optional eine für Benutzer sichtbare Beschriftung an.",
10
+ "entities.customFields.editor.addRule": "Regel hinzufügen",
11
+ "entities.customFields.editor.assignToFieldset": "Feldgruppe zuweisen",
12
+ "entities.customFields.editor.cancel": "Abbrechen",
13
+ "entities.customFields.editor.code": "Code",
14
+ "entities.customFields.editor.createGroup": "Gruppe erstellen",
15
+ "entities.customFields.editor.defaultOption": "Standard",
16
+ "entities.customFields.editor.delete": "Löschen",
17
+ "entities.customFields.editor.deleteFieldsetText": "Die zugehörigen Felder werden zu „Nicht zugewiesen“ verschoben.",
18
+ "entities.customFields.editor.deleteFieldsetTitle": "Feldgruppe „{code}“ löschen?",
19
+ "entities.customFields.editor.deleteGroupAria": "{code} löschen",
20
+ "entities.customFields.editor.description": "Beschreibung",
21
+ "entities.customFields.editor.dragToReorder": "Zum Neuanordnen ziehen",
22
+ "entities.customFields.editor.editGroup": "Gruppe bearbeiten",
23
+ "entities.customFields.editor.editGroupAria": "{code} bearbeiten",
24
+ "entities.customFields.editor.editGroupDescription": "Aktualisieren Sie die ausgewählte Gruppe dieser Feldgruppe.",
25
+ "entities.customFields.editor.editGroups": "Gruppen bearbeiten",
26
+ "entities.customFields.editor.editor": "Editor",
27
+ "entities.customFields.editor.editorHtmlRichText": "HTML-Rich-Text",
28
+ "entities.customFields.editor.editorMarkdown": "Markdown (UIW)",
29
+ "entities.customFields.editor.editorSimpleMarkdown": "Einfaches Markdown",
30
+ "entities.customFields.editor.existingGroups": "Vorhandene Gruppen",
31
+ "entities.customFields.editor.group": "Gruppe",
32
+ "entities.customFields.editor.groupCode": "Gruppencode",
33
+ "entities.customFields.editor.groupCodePlaceholder": "z. B. einkaufsgremium",
34
+ "entities.customFields.editor.groupCodeRequired": "Der Gruppencode ist erforderlich.",
35
+ "entities.customFields.editor.groupHintPlaceholder": "Sichtbar für Merchandiser",
36
+ "entities.customFields.editor.groupLabelPlaceholder": "Einkaufsgremium",
37
+ "entities.customFields.editor.hint": "Hinweis",
38
+ "entities.customFields.editor.icon": "Symbol",
39
+ "entities.customFields.editor.iconOption.bookOpen": "Aufgeschlagenes Buch",
40
+ "entities.customFields.editor.iconOption.bookmark": "Lesezeichen",
41
+ "entities.customFields.editor.iconOption.briefcase": "Aktentasche",
42
+ "entities.customFields.editor.iconOption.building": "Gebäude",
43
+ "entities.customFields.editor.iconOption.camera": "Kamera",
44
+ "entities.customFields.editor.iconOption.car": "Auto",
45
+ "entities.customFields.editor.iconOption.clock": "Uhr",
46
+ "entities.customFields.editor.iconOption.cloud": "Wolke",
47
+ "entities.customFields.editor.iconOption.coins": "Münzen",
48
+ "entities.customFields.editor.iconOption.compass": "Kompass",
49
+ "entities.customFields.editor.iconOption.creditCard": "Kreditkarte",
50
+ "entities.customFields.editor.iconOption.database": "Datenbank",
51
+ "entities.customFields.editor.iconOption.flame": "Flamme",
52
+ "entities.customFields.editor.iconOption.gift": "Geschenk",
53
+ "entities.customFields.editor.iconOption.globe": "Globus",
54
+ "entities.customFields.editor.iconOption.grid": "Raster",
55
+ "entities.customFields.editor.iconOption.heart": "Herz",
56
+ "entities.customFields.editor.iconOption.key": "Schlüssel",
57
+ "entities.customFields.editor.iconOption.layers": "Ebenen",
58
+ "entities.customFields.editor.iconOption.map": "Karte",
59
+ "entities.customFields.editor.iconOption.package": "Paket",
60
+ "entities.customFields.editor.iconOption.palette": "Palette",
61
+ "entities.customFields.editor.iconOption.shield": "Schild",
62
+ "entities.customFields.editor.iconOption.shirt": "Hemd",
63
+ "entities.customFields.editor.iconOption.shoppingBag": "Einkaufstasche",
64
+ "entities.customFields.editor.iconOption.shoppingCart": "Einkaufswagen",
65
+ "entities.customFields.editor.iconOption.sparkles": "Funkeln",
66
+ "entities.customFields.editor.iconOption.star": "Stern",
67
+ "entities.customFields.editor.iconOption.store": "Geschäft",
68
+ "entities.customFields.editor.iconOption.tag": "Etikett",
69
+ "entities.customFields.editor.iconOption.truck": "Lkw",
70
+ "entities.customFields.editor.iconOption.users": "Benutzer",
71
+ "entities.customFields.editor.iconOption.zap": "Blitz",
72
+ "entities.customFields.editor.key": "Schlüssel",
73
+ "entities.customFields.editor.kind": "Typ",
74
+ "entities.customFields.editor.kindOption.attachment": "Anhang",
75
+ "entities.customFields.editor.kindOption.boolean": "Boolescher Wert",
76
+ "entities.customFields.editor.kindOption.currency": "Währung",
77
+ "entities.customFields.editor.kindOption.date": "Datum",
78
+ "entities.customFields.editor.kindOption.datetime": "Datum und Uhrzeit",
79
+ "entities.customFields.editor.kindOption.dictionary": "Wörterbuch",
80
+ "entities.customFields.editor.kindOption.float": "Dezimalzahl",
81
+ "entities.customFields.editor.kindOption.integer": "Ganzzahl",
82
+ "entities.customFields.editor.kindOption.multiline": "Mehrzeiliger Text",
83
+ "entities.customFields.editor.kindOption.relation": "Relation",
84
+ "entities.customFields.editor.kindOption.select": "Auswahlliste",
85
+ "entities.customFields.editor.kindOption.text": "Text",
86
+ "entities.customFields.editor.label": "Beschriftung",
87
+ "entities.customFields.editor.listboxSearchable": "Liste (durchsuchbar)",
88
+ "entities.customFields.editor.multiSelectInputStyle": "Stil des Mehrfachauswahlfelds",
89
+ "entities.customFields.editor.multiple": "Mehrere Werte",
90
+ "entities.customFields.editor.newFieldsetLabel": "Neue Feldgruppe",
91
+ "entities.customFields.editor.newGroup": "Neue Gruppe",
92
+ "entities.customFields.editor.newGroupDescription": "Fügen Sie eine wiederverwendbare Gruppe für diese Feldgruppe hinzu.",
93
+ "entities.customFields.editor.noFieldsets": "Noch keine Feldgruppen definiert. Mit Feldgruppen lassen sich benutzerdefinierte Felder für verschiedene Varianten derselben Entität gruppieren (z. B. Mode- vs. Sportprodukte).",
94
+ "entities.customFields.editor.noGroup": "Keine Gruppe",
95
+ "entities.customFields.editor.noOptionsDefined": "Keine Optionen definiert.",
96
+ "entities.customFields.editor.noValidationRules": "Keine Validierungsregeln definiert.",
97
+ "entities.customFields.editor.optionLabelPlaceholder": "Für Benutzer sichtbare Beschriftung (optional)",
98
+ "entities.customFields.editor.options": "Optionen",
99
+ "entities.customFields.editor.optionsSource": "Optionsquelle",
100
+ "entities.customFields.editor.optionsUrl": "Options-URL",
101
+ "entities.customFields.editor.relatedEntityId": "ID der verknüpften Entität",
102
+ "entities.customFields.editor.removeField": "Feld entfernen",
103
+ "entities.customFields.editor.removeOption": "Option entfernen",
104
+ "entities.customFields.editor.removeRule": "Regel entfernen",
105
+ "entities.customFields.editor.reorderedSavingSoon": "Neu angeordnet — wird gleich gespeichert",
106
+ "entities.customFields.editor.restoreDeletedFields": "Gelöschte Felder wiederherstellen:",
107
+ "entities.customFields.editor.ruleErrorMessagePlaceholder": "Fehlermeldung",
108
+ "entities.customFields.editor.ruleNumberPlaceholder": "Zahl",
109
+ "entities.customFields.editor.rulePatternPlaceholder": "Muster (z. B. ^[a-z]+$)",
110
+ "entities.customFields.editor.ruleRequiredMessage": "Dieses Feld ist erforderlich",
111
+ "entities.customFields.editor.saveGroup": "Gruppe speichern",
112
+ "entities.customFields.editor.savingOrder": "Reihenfolge wird gespeichert…",
113
+ "entities.customFields.editor.singleFieldsetPerEntity": "Eine Feldgruppe pro Entität",
114
+ "entities.customFields.editor.singleFieldsetToggleHint": "(fügen Sie mindestens zwei Feldgruppen hinzu, um umzuschalten)",
115
+ "entities.customFields.editor.supportedKindsNote": "Unterstützte Typen: Text, mehrzeiliger Text, Ganzzahl, Dezimalzahl, boolescher Wert, Auswahlliste (mit Optionen/optionsUrl), Währung (feste Währungsliste), Relation (mit verknüpfter Entität und Options-URL).",
116
+ "entities.customFields.editor.translateField": "Feld übersetzen",
117
+ "entities.customFields.editor.unassigned": "Nicht zugewiesen",
118
+ "entities.customFields.editor.unassignedFields": "Nicht zugewiesene Felder",
119
+ "entities.customFields.editor.unitsOptional": "Einheiten (optional)",
120
+ "entities.customFields.editor.unitsPlaceholder": "kg, cm usw.",
121
+ "entities.customFields.editor.validationRules": "Validierungsregeln",
122
+ "entities.customFields.editor.value": "Wert",
123
+ "entities.customFields.editor.valueRequired": "Der Wert ist erforderlich",
4
124
  "entities.customFields.empty": "Noch keine benutzerdefinierten Attribute definiert.",
5
125
  "entities.customFields.emptyFieldset": "Für diese Feldgruppe sind keine Felder definiert.",
6
126
  "entities.customFields.errors.deleteFailed": "Feld konnte nicht gelöscht werden.",