@open-mercato/core 0.9.0 → 0.9.1-develop.7459.1.99e3accd30

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 (243) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/generated/entities/customer_dictionary_entry/index.js +2 -0
  3. package/dist/generated/entities/customer_dictionary_entry/index.js.map +2 -2
  4. package/dist/generated/entities/customer_interaction/index.js +2 -0
  5. package/dist/generated/entities/customer_interaction/index.js.map +2 -2
  6. package/dist/generated/entity-fields-registry.js +2 -0
  7. package/dist/generated/entity-fields-registry.js.map +2 -2
  8. package/dist/modules/catalog/commands/categories.js +1 -1
  9. package/dist/modules/catalog/commands/categories.js.map +2 -2
  10. package/dist/modules/catalog/commands/productDeleteChildren.js +389 -0
  11. package/dist/modules/catalog/commands/productDeleteChildren.js.map +7 -0
  12. package/dist/modules/catalog/commands/products.js +52 -17
  13. package/dist/modules/catalog/commands/products.js.map +2 -2
  14. package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js +4 -6
  15. package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js.map +2 -2
  16. package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js +4 -6
  17. package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js.map +2 -2
  18. package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js +47 -11
  19. package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js.map +3 -3
  20. package/dist/modules/communication_channels/commands/deliver-outbound-message.js +5 -1
  21. package/dist/modules/communication_channels/commands/deliver-outbound-message.js.map +2 -2
  22. package/dist/modules/communication_channels/lib/inbound-message-origin.js +1 -1
  23. package/dist/modules/communication_channels/lib/inbound-message-origin.js.map +2 -2
  24. package/dist/modules/communication_channels/lib/oauth-state.js +4 -1
  25. package/dist/modules/communication_channels/lib/oauth-state.js.map +2 -2
  26. package/dist/modules/communication_channels/lib/use-connect-channel.js +10 -0
  27. package/dist/modules/communication_channels/lib/use-connect-channel.js.map +2 -2
  28. package/dist/modules/customers/api/activity-types/route.js +151 -0
  29. package/dist/modules/customers/api/activity-types/route.js.map +7 -0
  30. package/dist/modules/customers/api/deals/[id]/people/route.js +58 -5
  31. package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
  32. package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js +13 -5
  33. package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js.map +2 -2
  34. package/dist/modules/customers/api/dictionaries/[kind]/route.js +20 -5
  35. package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
  36. package/dist/modules/customers/api/interactions/route.js +3 -0
  37. package/dist/modules/customers/api/interactions/route.js.map +2 -2
  38. package/dist/modules/customers/api/interactions/tasks/route.js +1 -0
  39. package/dist/modules/customers/api/interactions/tasks/route.js.map +2 -2
  40. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.meta.js +18 -0
  41. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.meta.js.map +2 -2
  42. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js +0 -23
  43. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js.map +2 -2
  44. package/dist/modules/customers/backend/customers/deals/[id]/page.js +15 -17
  45. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  46. package/dist/modules/customers/calendar-event-types.js +548 -0
  47. package/dist/modules/customers/calendar-event-types.js.map +7 -0
  48. package/dist/modules/customers/commands/dictionaries.js +70 -10
  49. package/dist/modules/customers/commands/dictionaries.js.map +2 -2
  50. package/dist/modules/customers/commands/interactions.js +187 -22
  51. package/dist/modules/customers/commands/interactions.js.map +3 -3
  52. package/dist/modules/customers/components/ActivityTypeEditor.js +288 -0
  53. package/dist/modules/customers/components/ActivityTypeEditor.js.map +7 -0
  54. package/dist/modules/customers/components/DictionarySettings.js +3 -1
  55. package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
  56. package/dist/modules/customers/components/calendar/CalendarEventEditor.js +391 -297
  57. package/dist/modules/customers/components/calendar/CalendarEventEditor.js.map +3 -3
  58. package/dist/modules/customers/components/calendar/CalendarSettingsModal.js +9 -74
  59. package/dist/modules/customers/components/calendar/CalendarSettingsModal.js.map +2 -2
  60. package/dist/modules/customers/components/calendar/TimeGrid.js +20 -16
  61. package/dist/modules/customers/components/calendar/TimeGrid.js.map +2 -2
  62. package/dist/modules/customers/components/calendar/editor/EventTypePanel.js +218 -0
  63. package/dist/modules/customers/components/calendar/editor/EventTypePanel.js.map +7 -0
  64. package/dist/modules/customers/components/calendar/editor/LocationField.js +7 -3
  65. package/dist/modules/customers/components/calendar/editor/LocationField.js.map +2 -2
  66. package/dist/modules/customers/components/calendar/editor/PeopleField.js +5 -2
  67. package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
  68. package/dist/modules/customers/components/calendar/editor/PriorityField.js +3 -1
  69. package/dist/modules/customers/components/calendar/editor/PriorityField.js.map +2 -2
  70. package/dist/modules/customers/components/calendar/editor/RepeatField.js +9 -1
  71. package/dist/modules/customers/components/calendar/editor/RepeatField.js.map +2 -2
  72. package/dist/modules/customers/components/calendar/editor/ResourcesField.js +4 -1
  73. package/dist/modules/customers/components/calendar/editor/ResourcesField.js.map +2 -2
  74. package/dist/modules/customers/components/calendar/editor/ScheduleSection.js +15 -7
  75. package/dist/modules/customers/components/calendar/editor/ScheduleSection.js.map +2 -2
  76. package/dist/modules/customers/components/calendar/editor/SegmentGroup.js +3 -1
  77. package/dist/modules/customers/components/calendar/editor/SegmentGroup.js.map +2 -2
  78. package/dist/modules/customers/components/calendar/editor/hooks.js +28 -13
  79. package/dist/modules/customers/components/calendar/editor/hooks.js.map +3 -3
  80. package/dist/modules/customers/components/calendar/editor/inputs.js +14 -6
  81. package/dist/modules/customers/components/calendar/editor/inputs.js.map +2 -2
  82. package/dist/modules/customers/components/calendar/editor/useCalendarCustomFields.js +54 -0
  83. package/dist/modules/customers/components/calendar/editor/useCalendarCustomFields.js.map +7 -0
  84. package/dist/modules/customers/components/calendar/editor/useEventTypeCatalog.js +82 -0
  85. package/dist/modules/customers/components/calendar/editor/useEventTypeCatalog.js.map +7 -0
  86. package/dist/modules/customers/components/calendar/types.js +1 -0
  87. package/dist/modules/customers/components/calendar/types.js.map +2 -2
  88. package/dist/modules/customers/components/detail/CompanyPeopleSection.js +78 -343
  89. package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
  90. package/dist/modules/customers/components/detail/CreatePersonDialog.js +12 -11
  91. package/dist/modules/customers/components/detail/CreatePersonDialog.js.map +2 -2
  92. package/dist/modules/customers/components/detail/DealForm.js +7 -3
  93. package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
  94. package/dist/modules/customers/components/detail/DealPeopleSection.js +206 -0
  95. package/dist/modules/customers/components/detail/DealPeopleSection.js.map +7 -0
  96. package/dist/modules/customers/components/detail/LinkedPeopleSection.js +342 -0
  97. package/dist/modules/customers/components/detail/LinkedPeopleSection.js.map +7 -0
  98. package/dist/modules/customers/components/detail/PersonCard.js +5 -2
  99. package/dist/modules/customers/components/detail/PersonCard.js.map +2 -2
  100. package/dist/modules/customers/components/linking/adapters/personAdapter.js +3 -0
  101. package/dist/modules/customers/components/linking/adapters/personAdapter.js.map +2 -2
  102. package/dist/modules/customers/data/entities.js +6 -0
  103. package/dist/modules/customers/data/entities.js.map +2 -2
  104. package/dist/modules/customers/data/validators.js +39 -10
  105. package/dist/modules/customers/data/validators.js.map +2 -2
  106. package/dist/modules/customers/di.js +20 -1
  107. package/dist/modules/customers/di.js.map +2 -2
  108. package/dist/modules/customers/extension-points.js +13 -0
  109. package/dist/modules/customers/extension-points.js.map +2 -2
  110. package/dist/modules/customers/lib/calendar/conflicts.js +59 -16
  111. package/dist/modules/customers/lib/calendar/conflicts.js.map +2 -2
  112. package/dist/modules/customers/lib/calendar/editorPayload.js +34 -16
  113. package/dist/modules/customers/lib/calendar/editorPayload.js.map +2 -2
  114. package/dist/modules/customers/lib/calendar/eventTypeResolver.js +197 -0
  115. package/dist/modules/customers/lib/calendar/eventTypeResolver.js.map +7 -0
  116. package/dist/modules/customers/lib/calendar/interactionApplicability.js +67 -0
  117. package/dist/modules/customers/lib/calendar/interactionApplicability.js.map +7 -0
  118. package/dist/modules/customers/lib/calendar/mapItem.js +5 -2
  119. package/dist/modules/customers/lib/calendar/mapItem.js.map +2 -2
  120. package/dist/modules/customers/lib/calendar/recurrence.js +62 -1
  121. package/dist/modules/customers/lib/calendar/recurrence.js.map +2 -2
  122. package/dist/modules/customers/lib/calendar/timezone.js +111 -0
  123. package/dist/modules/customers/lib/calendar/timezone.js.map +7 -0
  124. package/dist/modules/customers/lib/interactionReadModel.js +1 -0
  125. package/dist/modules/customers/lib/interactionReadModel.js.map +2 -2
  126. package/dist/modules/customers/lib/todoCompatibility.js +1 -0
  127. package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
  128. package/dist/modules/customers/migrations/Migration20260928175058_customers.js +17 -0
  129. package/dist/modules/customers/migrations/Migration20260928175058_customers.js.map +7 -0
  130. package/dist/modules/customers/migrations/Migration20260930110235_customers.js +17 -0
  131. package/dist/modules/customers/migrations/Migration20260930110235_customers.js.map +7 -0
  132. package/dist/modules/dictionaries/components/DictionaryTable.js +60 -51
  133. package/dist/modules/dictionaries/components/DictionaryTable.js.map +2 -2
  134. package/dist/modules/messages/subscribers/message-notification.js +13 -1
  135. package/dist/modules/messages/subscribers/message-notification.js.map +2 -2
  136. package/dist/modules/payment_gateways/api/webhook/[provider]/route.js +201 -53
  137. package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +2 -2
  138. package/dist/modules/payment_gateways/lib/gateway-service.js +37 -11
  139. package/dist/modules/payment_gateways/lib/gateway-service.js.map +2 -2
  140. package/dist/modules/planner/lib/availabilityMerge.js +101 -38
  141. package/dist/modules/planner/lib/availabilityMerge.js.map +3 -3
  142. package/dist/modules/planner/services/plannerAvailabilityService.js.map +2 -2
  143. package/dist/modules/sales/components/documents/LineItemDialog.js +159 -81
  144. package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
  145. package/dist/modules/sales/components/documents/lineItemUtils.js +7 -0
  146. package/dist/modules/sales/components/documents/lineItemUtils.js.map +2 -2
  147. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js +12 -7
  148. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js.map +2 -2
  149. package/dist/modules/sync_excel/lib/adapters/customers.js +28 -25
  150. package/dist/modules/sync_excel/lib/adapters/customers.js.map +2 -2
  151. package/generated/entities/customer_dictionary_entry/index.ts +1 -0
  152. package/generated/entities/customer_interaction/index.ts +1 -0
  153. package/generated/entity-fields-registry.ts +2 -0
  154. package/package.json +9 -8
  155. package/src/modules/auth/i18n/de.json +3 -0
  156. package/src/modules/auth/i18n/en.json +3 -0
  157. package/src/modules/auth/i18n/es.json +3 -0
  158. package/src/modules/auth/i18n/ko.json +3 -0
  159. package/src/modules/auth/i18n/pl.json +3 -0
  160. package/src/modules/catalog/commands/categories.ts +1 -1
  161. package/src/modules/catalog/commands/productDeleteChildren.ts +557 -0
  162. package/src/modules/catalog/commands/products.ts +58 -17
  163. package/src/modules/communication_channels/api/post/channels/connect/credentials/route.ts +4 -6
  164. package/src/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.ts +4 -6
  165. package/src/modules/communication_channels/api/post/oauth/[provider]/initiate/route.ts +46 -10
  166. package/src/modules/communication_channels/commands/deliver-outbound-message.ts +6 -1
  167. package/src/modules/communication_channels/i18n/de.json +4 -0
  168. package/src/modules/communication_channels/i18n/en.json +4 -0
  169. package/src/modules/communication_channels/i18n/es.json +4 -0
  170. package/src/modules/communication_channels/i18n/ko.json +4 -0
  171. package/src/modules/communication_channels/i18n/pl.json +4 -0
  172. package/src/modules/communication_channels/lib/inbound-message-origin.ts +2 -6
  173. package/src/modules/communication_channels/lib/oauth-state.ts +4 -1
  174. package/src/modules/communication_channels/lib/use-connect-channel.ts +10 -0
  175. package/src/modules/customers/api/activity-types/route.ts +154 -0
  176. package/src/modules/customers/api/deals/[id]/people/route.ts +82 -3
  177. package/src/modules/customers/api/dictionaries/[kind]/[id]/route.ts +9 -1
  178. package/src/modules/customers/api/dictionaries/[kind]/route.ts +18 -2
  179. package/src/modules/customers/api/interactions/route.ts +4 -0
  180. package/src/modules/customers/api/interactions/tasks/route.ts +1 -0
  181. package/src/modules/customers/backend/config/customers/pipeline-stages/page.meta.ts +20 -0
  182. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.ts +0 -29
  183. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +15 -17
  184. package/src/modules/customers/calendar-event-types.ts +674 -0
  185. package/src/modules/customers/commands/dictionaries.ts +84 -8
  186. package/src/modules/customers/commands/interactions.ts +244 -21
  187. package/src/modules/customers/components/ActivityTypeEditor.tsx +239 -0
  188. package/src/modules/customers/components/DictionarySettings.tsx +5 -3
  189. package/src/modules/customers/components/calendar/CalendarEventEditor.tsx +347 -218
  190. package/src/modules/customers/components/calendar/CalendarSettingsModal.tsx +18 -96
  191. package/src/modules/customers/components/calendar/TimeGrid.tsx +20 -18
  192. package/src/modules/customers/components/calendar/editor/EventTypePanel.tsx +230 -0
  193. package/src/modules/customers/components/calendar/editor/LocationField.tsx +7 -1
  194. package/src/modules/customers/components/calendar/editor/PeopleField.tsx +4 -0
  195. package/src/modules/customers/components/calendar/editor/PriorityField.tsx +3 -0
  196. package/src/modules/customers/components/calendar/editor/RepeatField.tsx +10 -0
  197. package/src/modules/customers/components/calendar/editor/ResourcesField.tsx +4 -0
  198. package/src/modules/customers/components/calendar/editor/ScheduleSection.tsx +16 -4
  199. package/src/modules/customers/components/calendar/editor/SegmentGroup.tsx +3 -0
  200. package/src/modules/customers/components/calendar/editor/hooks.ts +29 -13
  201. package/src/modules/customers/components/calendar/editor/inputs.tsx +17 -5
  202. package/src/modules/customers/components/calendar/editor/useCalendarCustomFields.ts +49 -0
  203. package/src/modules/customers/components/calendar/editor/useEventTypeCatalog.ts +100 -0
  204. package/src/modules/customers/components/calendar/types.ts +1 -0
  205. package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +94 -388
  206. package/src/modules/customers/components/detail/CreatePersonDialog.tsx +39 -19
  207. package/src/modules/customers/components/detail/DealForm.tsx +7 -3
  208. package/src/modules/customers/components/detail/DealPeopleSection.tsx +256 -0
  209. package/src/modules/customers/components/detail/LinkedPeopleSection.tsx +474 -0
  210. package/src/modules/customers/components/detail/PersonCard.tsx +12 -4
  211. package/src/modules/customers/components/detail/types.ts +1 -0
  212. package/src/modules/customers/components/linking/adapters/personAdapter.tsx +4 -0
  213. package/src/modules/customers/data/entities.ts +8 -1
  214. package/src/modules/customers/data/validators.ts +42 -7
  215. package/src/modules/customers/di.ts +21 -0
  216. package/src/modules/customers/extension-points.ts +13 -0
  217. package/src/modules/customers/i18n/de.json +104 -8
  218. package/src/modules/customers/i18n/en.json +104 -8
  219. package/src/modules/customers/i18n/es.json +104 -8
  220. package/src/modules/customers/i18n/ko.json +104 -8
  221. package/src/modules/customers/i18n/pl.json +104 -8
  222. package/src/modules/customers/lib/calendar/conflicts.ts +106 -27
  223. package/src/modules/customers/lib/calendar/editorPayload.ts +39 -19
  224. package/src/modules/customers/lib/calendar/eventTypeResolver.ts +258 -0
  225. package/src/modules/customers/lib/calendar/interactionApplicability.ts +99 -0
  226. package/src/modules/customers/lib/calendar/mapItem.ts +6 -2
  227. package/src/modules/customers/lib/calendar/recurrence.ts +65 -1
  228. package/src/modules/customers/lib/calendar/timezone.ts +117 -0
  229. package/src/modules/customers/lib/interactionReadModel.ts +1 -0
  230. package/src/modules/customers/lib/todoCompatibility.ts +2 -0
  231. package/src/modules/customers/migrations/.snapshot-open-mercato.json +34 -0
  232. package/src/modules/customers/migrations/Migration20260928175058_customers.ts +15 -0
  233. package/src/modules/customers/migrations/Migration20260930110235_customers.ts +15 -0
  234. package/src/modules/dictionaries/components/DictionaryTable.tsx +63 -56
  235. package/src/modules/messages/subscribers/message-notification.ts +13 -3
  236. package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +243 -62
  237. package/src/modules/payment_gateways/lib/gateway-service.ts +39 -12
  238. package/src/modules/planner/lib/availabilityMerge.ts +106 -41
  239. package/src/modules/planner/services/plannerAvailabilityService.ts +4 -0
  240. package/src/modules/sales/components/documents/LineItemDialog.tsx +194 -111
  241. package/src/modules/sales/components/documents/lineItemUtils.ts +18 -0
  242. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx +25 -13
  243. package/src/modules/sync_excel/lib/adapters/customers.ts +33 -27
@@ -2,6 +2,8 @@ import { z } from 'zod'
2
2
  import { isValidPhoneNumber } from '@open-mercato/shared/lib/phone'
3
3
  import { COORDINATE_RANGES } from '@open-mercato/shared/lib/location/coordinates'
4
4
  import { dictionaryEntrySortModeSchema } from '@open-mercato/core/modules/dictionaries/lib/entrySort'
5
+ import { calendarEventTypeBehaviorSchema } from '../calendar-event-types'
6
+ import { calendarDayStartInstant, calendarWallTimeToInstant, isCalendarTimezone } from '../lib/calendar/timezone'
5
7
 
6
8
  const uuid = () => z.string().uuid()
7
9
 
@@ -350,13 +352,27 @@ const dictionaryColorSchema = z
350
352
  )
351
353
  const dictionaryIconSchema = z.string().trim().max(48)
352
354
 
355
+ function validateDictionaryBehavior(
356
+ payload: { kind: string; behavior?: unknown },
357
+ ctx: z.RefinementCtx,
358
+ ): void {
359
+ if (payload.behavior !== undefined && payload.kind !== 'activity_type') {
360
+ ctx.addIssue({
361
+ code: z.ZodIssueCode.custom,
362
+ path: ['behavior'],
363
+ message: 'Behavior is only supported for activity types',
364
+ })
365
+ }
366
+ }
367
+
353
368
  export const customerDictionaryEntryCreateSchema = scopedSchema.extend({
354
369
  kind: dictionaryKindEnum,
355
370
  value: dictionaryValueSchema,
356
371
  label: dictionaryLabelSchema.optional(),
357
372
  color: dictionaryColorSchema.nullable().optional(),
358
373
  icon: dictionaryIconSchema.nullable().optional(),
359
- })
374
+ behavior: calendarEventTypeBehaviorSchema.nullable().optional(),
375
+ }).superRefine(validateDictionaryBehavior)
360
376
 
361
377
  export type CustomerDictionaryEntryCreateInput = z.infer<typeof customerDictionaryEntryCreateSchema>
362
378
 
@@ -368,18 +384,21 @@ export const customerDictionaryEntryUpdateSchema = scopedSchema
368
384
  label: dictionaryLabelSchema.optional(),
369
385
  color: dictionaryColorSchema.nullable().optional(),
370
386
  icon: dictionaryIconSchema.nullable().optional(),
387
+ behavior: calendarEventTypeBehaviorSchema.nullable().optional(),
371
388
  })
372
389
  .refine(
373
390
  (payload) =>
374
391
  payload.value !== undefined ||
375
392
  payload.label !== undefined ||
376
393
  payload.color !== undefined ||
377
- payload.icon !== undefined,
394
+ payload.icon !== undefined ||
395
+ payload.behavior !== undefined,
378
396
  {
379
397
  message: 'Provide at least one field to update.',
380
398
  path: ['value'],
381
399
  }
382
400
  )
401
+ .superRefine(validateDictionaryBehavior)
383
402
 
384
403
  export type CustomerDictionaryEntryUpdateInput = z.infer<typeof customerDictionaryEntryUpdateSchema>
385
404
 
@@ -477,7 +496,10 @@ const interactionGuestPermissionsSchema = z
477
496
  })
478
497
  .strict()
479
498
 
499
+ export const calendarTimezoneSchema = z.string().trim().min(1).max(120).refine(isCalendarTimezone, { message: 'customers.calendar.editor.validation.timezoneInvalid' })
500
+
480
501
  const interactionExtendedFields = {
502
+ timezone: calendarTimezoneSchema.optional().nullable(),
481
503
  durationMinutes: z.number().int().min(0).optional().nullable(),
482
504
  location: z.string().trim().max(500).optional().nullable(),
483
505
  allDay: z.boolean().optional().nullable(),
@@ -494,6 +516,7 @@ const interactionCreateBaseSchema = scopedSchema.extend({
494
516
  id: z.string().uuid().optional(),
495
517
  entityId: z.string().uuid(),
496
518
  interactionType: z.string().trim().min(1).max(100),
519
+ enforceSelectableType: z.boolean().optional(),
497
520
  title: z.string().trim().max(500).optional().nullable(),
498
521
  body: z.string().trim().max(10000).optional().nullable(),
499
522
  // Lenient like `deal_status` (status: z.string().max(50)). The `interaction-statuses`
@@ -518,11 +541,13 @@ const interactionCreateBaseSchema = scopedSchema.extend({
518
541
  ...interactionExtendedFields,
519
542
  })
520
543
 
521
- function deriveScheduledAtFromDateTime(date?: string | null, time?: string | null): Date | null {
544
+ function deriveScheduledAtFromDateTime(date?: string | null, time?: string | null, timezone?: string | null, allDay?: boolean | null): Date | null {
522
545
  if (!date || typeof date !== 'string') return null
523
546
  const trimmedDate = date.trim()
524
547
  if (!trimmedDate) return null
525
548
  const trimmedTime = typeof time === 'string' ? time.trim() : ''
549
+ if (timezone && allDay) return calendarDayStartInstant(trimmedDate, timezone)
550
+ if (timezone) return calendarWallTimeToInstant(trimmedDate, trimmedTime || '00:00', timezone)
526
551
  const iso = trimmedTime ? `${trimmedDate}T${trimmedTime}:00` : `${trimmedDate}T00:00:00`
527
552
  const parsed = new Date(iso)
528
553
  return Number.isNaN(parsed.getTime()) ? null : parsed
@@ -551,9 +576,13 @@ export const interactionCreateSchema = interactionCreateBaseSchema
551
576
  // external API consumers don't silently persist `scheduled_at: null` after
552
577
  // the validator already enforced non-empty date/time. The form already
553
578
  // computes `scheduledAt` itself, so this branch is a no-op for the form path.
554
- .transform((value) => {
579
+ .transform((value, ctx) => {
555
580
  if (value.scheduledAt) return value
556
- const derived = deriveScheduledAtFromDateTime(value.date, value.time)
581
+ const derived = deriveScheduledAtFromDateTime(value.date, value.time, value.timezone, value.allDay)
582
+ if (value.timezone && value.date && !derived) {
583
+ ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['time'], message: 'customers.calendar.editor.validation.timezoneGap' })
584
+ return z.NEVER
585
+ }
557
586
  return derived ? { ...value, scheduledAt: derived } : value
558
587
  })
559
588
 
@@ -572,6 +601,8 @@ const interactionUpdateBaseSchema = z
572
601
  // state to express — that depends on #5935 making the column nullable first.
573
602
  entityId: z.string().uuid().optional(),
574
603
  interactionType: z.string().trim().min(1).max(100).optional(),
604
+ enforceSelectableType: z.boolean().optional(),
605
+ confirmDiscardInapplicableValues: z.boolean().optional(),
575
606
  title: z.string().trim().max(500).optional().nullable(),
576
607
  body: z.string().trim().max(10000).optional().nullable(),
577
608
  status: z.string().max(50).optional(),
@@ -614,13 +645,17 @@ export const interactionUpdateSchema = interactionUpdateBaseSchema
614
645
  // Mirror the create-schema derivation for partial updates: when an external
615
646
  // caller supplies `date+time` without `scheduledAt`, derive the timestamp so
616
647
  // the update doesn't silently leave `scheduled_at` stale.
617
- .transform((value) => {
648
+ .transform((value, ctx) => {
618
649
  if (value.scheduledAt !== undefined) return value
619
650
  // An explicit `date: null` is how a caller drops the due date; mirror it
620
651
  // onto `scheduledAt` rather than leaving the old timestamp behind (#5941).
621
652
  if (value.date === null) return { ...value, scheduledAt: null }
622
653
  if (!value.date && !value.time) return value
623
- const derived = deriveScheduledAtFromDateTime(value.date, value.time)
654
+ const derived = deriveScheduledAtFromDateTime(value.date, value.time, value.timezone, value.allDay)
655
+ if (value.timezone && value.date && !derived) {
656
+ ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['time'], message: 'customers.calendar.editor.validation.timezoneGap' })
657
+ return z.NEVER
658
+ }
624
659
  return derived ? { ...value, scheduledAt: derived } : value
625
660
  })
626
661
 
@@ -3,6 +3,9 @@ import type { EntityManager } from '@mikro-orm/postgresql'
3
3
  import type { AppContainer } from '@open-mercato/shared/lib/di/container'
4
4
  import type { OptimisticLockCurrentReader } from '@open-mercato/shared/lib/crud/optimistic-lock'
5
5
  import { registerOptimisticLockReaders } from '@open-mercato/shared/lib/crud/optimistic-lock-store'
6
+ import { createCalendarEventTypeRegistry } from './calendar-event-types'
7
+ import { resolveCatalogEventType, resolveScopedCalendarEventTypes } from './lib/calendar/eventTypeResolver'
8
+ import type { OrganizationHierarchyService } from '@open-mercato/shared/lib/auth/principal-service'
6
9
  import { CustomerEntity, CustomerAddress, CustomerInteraction } from './data/entities'
7
10
 
8
11
  const RESOURCE_KIND_COMPANY = 'customers.company'
@@ -64,11 +67,29 @@ registerOptimisticLockReaders({
64
67
  [RESOURCE_KIND_PEOPLE]: readCustomerPersonUpdatedAt,
65
68
  })
66
69
 
70
+ const calendarEventTypeRegistry = createCalendarEventTypeRegistry()
71
+
67
72
  export function register(container: AppContainer) {
68
73
  container.register({
69
74
  CustomerEntity: asValue(CustomerEntity),
70
75
  CustomerAddress: asValue(CustomerAddress),
71
76
  CustomerInteraction: asValue(CustomerInteraction),
77
+ calendarEventTypeRegistry: asValue(calendarEventTypeRegistry),
78
+ calendarEventTypeCatalogService: asValue({
79
+ async resolveBehavior(input: { tenantId: string; organizationId: string; key: string }) {
80
+ const hierarchy = container.resolve('organizationHierarchyService') as OrganizationHierarchyService
81
+ const ancestors = await hierarchy.resolveAncestorIds({ tenantId: input.tenantId, organizationId: input.organizationId })
82
+ if (ancestors === null) throw new Error('[internal] Calendar organization not found')
83
+ const em = container.resolve('em') as EntityManager
84
+ const catalog = await resolveScopedCalendarEventTypes({
85
+ em,
86
+ tenantId: input.tenantId,
87
+ organizationId: input.organizationId,
88
+ readableOrganizationIds: [input.organizationId, ...ancestors.slice().reverse()],
89
+ })
90
+ return resolveCatalogEventType(catalog, input.key)?.behavior ?? null
91
+ },
92
+ }),
72
93
  })
73
94
  // `crudMutationGuardService` is registered platform-wide in the shared
74
95
  // DI bootstrap (`packages/shared/src/lib/di/container.ts`). It already
@@ -1,4 +1,5 @@
1
1
  import {
2
+ componentExtensionHost,
2
3
  crudFormExtensionHost,
3
4
  dataTableExtensionHost,
4
5
  defineModuleExtensionPoints,
@@ -23,6 +24,18 @@ export const extensionPoints = defineModuleExtensionPoints({
23
24
  companyForm: crudFormExtensionHost({ entityId: 'customers.company', spotId: 'crud-form:customers.company', source: 'backend/customers/companies-v2/[id]/page.tsx' }),
24
25
  dealForm: crudFormExtensionHost({ entityId: 'customers.deal', spotId: 'crud-form:customers.deal', source: 'backend/customers/deals/[id]/page.tsx' }),
25
26
  personForm: crudFormExtensionHost({ entityId: 'customers.person', spotId: 'crud-form:customers.person', source: 'backend/customers/people-v2/[id]/page.tsx' }),
27
+ calendarEventTypes: injectionExtensionHost({
28
+ family: 'generic',
29
+ spotId: 'calendar:customers.event-types',
30
+ supported: ['headless-widget', 'registry-contribution'],
31
+ dataContract: 'customers.calendar.event-types.v1',
32
+ source: 'lib/calendar/eventTypeResolver.ts',
33
+ }),
34
+ calendarEventTypePanel: componentExtensionHost({
35
+ componentId: 'section:customers.calendar-event-editor.type-panel',
36
+ propsContract: 'customers.calendar.event-type-panel.v1',
37
+ source: 'components/calendar/editor/EventTypePanel.tsx',
38
+ }),
26
39
  companyHeader: detailHost('detail:customers.company:header', 'backend/customers/companies-v2/[id]/page.tsx'),
27
40
  companyStatusBadges: detailHost('detail:customers.company:status-badges', 'backend/customers/companies-v2/[id]/page.tsx'),
28
41
  companyFooter: detailHost('detail:customers.company:footer', 'backend/customers/companies-v2/[id]/page.tsx'),
@@ -193,6 +193,11 @@
193
193
  "customers.audit.todos.link": "To-do verknüpfen",
194
194
  "customers.audit.todos.unlink": "To-do-Verknüpfung lösen",
195
195
  "customers.calendar.actions.newEvent": "Neuer Termin",
196
+ "customers.calendar.activityTypes.errors.changeConfirmationRequired": "Beim Ändern des Ereignistyps werden vorhandene Werte gelöscht.",
197
+ "customers.calendar.activityTypes.errors.fieldNotApplicable": "Dieses Feld gilt nicht für den ausgewählten Aktivitätstyp.",
198
+ "customers.calendar.activityTypes.errors.forbidden": "Sie haben keine Berechtigung, Aktivitätstypen anzuzeigen.",
199
+ "customers.calendar.activityTypes.errors.loadFailed": "Aktivitätstypen konnten nicht geladen werden.",
200
+ "customers.calendar.activityTypes.errors.unavailable": "Dieser Aktivitätstyp ist nicht verfügbar.",
196
201
  "customers.calendar.agenda.eventsCount.few": "{count} Termine",
197
202
  "customers.calendar.agenda.eventsCount.many": "{count} Termine",
198
203
  "customers.calendar.agenda.eventsCount.one": "{count} Termin",
@@ -232,6 +237,8 @@
232
237
  "customers.calendar.editor.assigneePlaceholder": "Einem Teammitglied zuweisen…",
233
238
  "customers.calendar.editor.attendees": "Teilnehmer",
234
239
  "customers.calendar.editor.cancel": "Abbrechen",
240
+ "customers.calendar.editor.catalogLoadFailed": "Aktivitätstypen konnten nicht geladen werden. Versuchen Sie es erneut, um dieses Ereignis zu speichern.",
241
+ "customers.calendar.editor.catalogLoading": "Aktivitätstypen werden geladen…",
235
242
  "customers.calendar.editor.category": "Kategorie",
236
243
  "customers.calendar.editor.close": "Schließen",
237
244
  "customers.calendar.editor.conflictTitle": "Kalenderkonflikt",
@@ -249,8 +256,14 @@
249
256
  "customers.calendar.editor.dealsSection": "Geschäfte",
250
257
  "customers.calendar.editor.description": "Beschreibung",
251
258
  "customers.calendar.editor.descriptionPlaceholder": "Details hinzufügen…",
259
+ "customers.calendar.editor.discardConfirm": "Typ ändern und Werte löschen",
260
+ "customers.calendar.editor.discardDescription": "Beim Ändern des Ereignistyps werden Werte gelöscht, die für den neuen Typ nicht gelten.",
261
+ "customers.calendar.editor.discardFields": "Zu löschende Werte:",
262
+ "customers.calendar.editor.discardTitle": "Ereignistyp ändern?",
263
+ "customers.calendar.editor.discardUnknownField": "Benutzerdefiniertes Feld",
252
264
  "customers.calendar.editor.error": "Termin konnte nicht gespeichert werden",
253
265
  "customers.calendar.editor.guestBadge": "Gast",
266
+ "customers.calendar.editor.historicalType": "Dieser Ereignistyp ist für neue Ereignisse nicht verfügbar. Sie können dieses Ereignis weiter bearbeiten oder einen anderen Typ wählen.",
254
267
  "customers.calendar.editor.location": "Ort",
255
268
  "customers.calendar.editor.locationPlaceholder": "Ort oder Link hinzufügen…",
256
269
  "customers.calendar.editor.multiDayHint": "Mehrtägiger Termin · {count} Tage",
@@ -295,6 +308,7 @@
295
308
  "customers.calendar.editor.saved": "Termin gespeichert",
296
309
  "customers.calendar.editor.saving": "Speichern…",
297
310
  "customers.calendar.editor.searching": "Suche…",
311
+ "customers.calendar.editor.timezone": "Zeitzone",
298
312
  "customers.calendar.editor.title.create": "Neuer Termin",
299
313
  "customers.calendar.editor.title.edit": "Termin bearbeiten",
300
314
  "customers.calendar.editor.titleLabel.email": "Betreff",
@@ -303,6 +317,7 @@
303
317
  "customers.calendar.editor.titlePlaceholder": "Titel hinzufügen…",
304
318
  "customers.calendar.editor.to": "An",
305
319
  "customers.calendar.editor.typeSwitcher": "Termintyp",
320
+ "customers.calendar.editor.typeUnavailable": "Wählen Sie vor dem Speichern einen verfügbaren Ereignistyp.",
306
321
  "customers.calendar.editor.types.call": "Anruf",
307
322
  "customers.calendar.editor.types.email": "E-Mail",
308
323
  "customers.calendar.editor.types.event": "Event",
@@ -312,6 +327,8 @@
312
327
  "customers.calendar.editor.validation.assigneeRequired": "Weise diese Aufgabe einem Teammitglied zu",
313
328
  "customers.calendar.editor.validation.endsBeforeStarts": "Das Ende muss nach dem Beginn liegen",
314
329
  "customers.calendar.editor.validation.relatedToRequired": "Wähle eine Person oder ein Unternehmen aus, um diesen Termin zu verknüpfen",
330
+ "customers.calendar.editor.validation.timezoneGap": "Diese Ortszeit existiert in der gewählten Zeitzone nicht",
331
+ "customers.calendar.editor.validation.timezoneInvalid": "Wählen Sie eine gültige Zeitzone",
315
332
  "customers.calendar.editor.validation.titleRequired": "Titel ist erforderlich",
316
333
  "customers.calendar.empty.action": "Neuer Termin",
317
334
  "customers.calendar.empty.agenda": "In diesem Zeitraum ist nichts geplant",
@@ -368,6 +385,7 @@
368
385
  "customers.calendar.settings.conflictWarnings": "Konfliktwarnungen",
369
386
  "customers.calendar.settings.eventCategories": "Ereigniskategorien",
370
387
  "customers.calendar.settings.eventCategoriesHint": "Eigene Gruppierungslabels (z. B. Teammeeting, Verkaufsanruf). Werden beim Erstellen eines Ereignisses angeboten.",
388
+ "customers.calendar.settings.manageActivityTypes": "Aktivitätstypen verwalten",
371
389
  "customers.calendar.settings.max": "(max. {count})",
372
390
  "customers.calendar.settings.removeTag": "{tag} entfernen",
373
391
  "customers.calendar.settings.save": "Änderungen speichern",
@@ -642,7 +660,6 @@
642
660
  "customers.companies.detail.notes.placeholder": "Schreibe eine Notiz zu diesem Unternehmen…",
643
661
  "customers.companies.detail.people.add": "Person hinzufügen",
644
662
  "customers.companies.detail.people.empty": "Noch keine Personen mit diesem Unternehmen verknüpft.",
645
- "customers.companies.detail.people.filter": "Filters",
646
663
  "customers.companies.detail.people.helper": "Mit diesem Unternehmen verknüpfte Personen erscheinen hier und können erstellt, verknüpft, geöffnet oder getrennt werden, ohne die Seite zu verlassen.",
647
664
  "customers.companies.detail.people.linkAction": "Vorhandene Person verknüpfen",
648
665
  "customers.companies.detail.people.linkCancel": "Abbrechen",
@@ -671,20 +688,13 @@
671
688
  "customers.companies.detail.people.linkedOn": "Verknüpft am {{date}}",
672
689
  "customers.companies.detail.people.loadError": "Failed to load people.",
673
690
  "customers.companies.detail.people.loading": "Personen werden geladen…",
674
- "customers.companies.detail.people.next": "Next",
675
- "customers.companies.detail.people.noSearchResults": "No people match your search.",
676
691
  "customers.companies.detail.people.open": "Person öffnen",
677
- "customers.companies.detail.people.pageSummary": "Page {{page}} of {{total}}",
678
- "customers.companies.detail.people.previous": "Previous",
679
692
  "customers.companies.detail.people.remove": "Trennen",
680
693
  "customers.companies.detail.people.removeError": "Person konnte nicht vom Unternehmen getrennt werden.",
681
694
  "customers.companies.detail.people.removeSuccess": "Person vom Unternehmen getrennt.",
682
695
  "customers.companies.detail.people.searchPlaceholder": "Search by name, role, email...",
683
696
  "customers.companies.detail.people.sectionSubtitle": "Employees and decision makers on the client side",
684
697
  "customers.companies.detail.people.sectionTitle": "People",
685
- "customers.companies.detail.people.sortNameAsc": "Sort: Name A-Z",
686
- "customers.companies.detail.people.sortNameDesc": "Sort: Name Z-A",
687
- "customers.companies.detail.people.sortRecent": "Sort: Recently linked",
688
698
  "customers.companies.detail.relativeTime.daysAgo": "vor {{days}} Tagen",
689
699
  "customers.companies.detail.relativeTime.monthsAgo": "vor {{months}} Mon.",
690
700
  "customers.companies.detail.relativeTime.oneDayAgo": "vor 1 Tag",
@@ -806,6 +816,71 @@
806
816
  "customers.companies.presets.inactive60": "Inaktiv > 60 Tage",
807
817
  "customers.companies.presets.myAccounts": "Meine Konten",
808
818
  "customers.companies.presets.recentlyCreated": "Kürzlich erstellt",
819
+ "customers.config.activityTypes.appearance": "Darstellung",
820
+ "customers.config.activityTypes.behavior": "Formularverhalten",
821
+ "customers.config.activityTypes.columns.active": "Auswählbar",
822
+ "customers.config.activityTypes.columns.behavior": "Verhalten",
823
+ "customers.config.activityTypes.columns.fields": "Felder",
824
+ "customers.config.activityTypes.columns.fieldsets": "Feldgruppen",
825
+ "customers.config.activityTypes.columns.key": "Stabiler Schlüssel",
826
+ "customers.config.activityTypes.columns.order": "Reihenfolge",
827
+ "customers.config.activityTypes.columns.source": "Quelle",
828
+ "customers.config.activityTypes.columns.type": "Aktivitätstyp",
829
+ "customers.config.activityTypes.configure": "Konfigurieren",
830
+ "customers.config.activityTypes.delete": "Löschen",
831
+ "customers.config.activityTypes.empty": "Keine Aktivitätstypen verfügbar.",
832
+ "customers.config.activityTypes.fields.allDay": "Ganztägig",
833
+ "customers.config.activityTypes.fields.baseKind": "Basisverhalten",
834
+ "customers.config.activityTypes.fields.color": "Farbe (#RRGGBB)",
835
+ "customers.config.activityTypes.fields.customFieldsetIds": "Feldgruppen benutzerdefinierter Felder",
836
+ "customers.config.activityTypes.fields.endTime": "Endzeit",
837
+ "customers.config.activityTypes.fields.icon": "Symbol",
838
+ "customers.config.activityTypes.fields.label": "Bezeichnung",
839
+ "customers.config.activityTypes.fields.location": "Ortsfeld",
840
+ "customers.config.activityTypes.fields.order": "Anzeigereihenfolge",
841
+ "customers.config.activityTypes.fields.people": "Personenfeld",
842
+ "customers.config.activityTypes.fields.priority": "Priorität",
843
+ "customers.config.activityTypes.fields.recurrence": "Wiederholung",
844
+ "customers.config.activityTypes.fields.resources": "Ressourcen",
845
+ "customers.config.activityTypes.fields.selectable": "Für neue Ereignisse verfügbar",
846
+ "customers.config.activityTypes.fields.value": "Stabiler Schlüssel",
847
+ "customers.config.activityTypes.fieldsetsUnavailable": "Feldgruppen sind nicht verfügbar. Laden Sie sie erneut, bevor Sie speichern.",
848
+ "customers.config.activityTypes.inactiveHint": "Ein Modul verhindert derzeit, dass diese Überschreibung wirksam wird.",
849
+ "customers.config.activityTypes.inherited": "Geerbt",
850
+ "customers.config.activityTypes.inheritedHint": "Beim Speichern wird eine lokale Überschreibung für diese Organisation erstellt.",
851
+ "customers.config.activityTypes.invalidBehavior": "Prüfen Sie die Werte für das Formularverhalten.",
852
+ "customers.config.activityTypes.invalidKey": "Verwenden Sie einen kleingeschriebenen Schlüssel mit Buchstaben, Zahlen, Bindestrichen oder Unterstrichen.",
853
+ "customers.config.activityTypes.loadFailed": "Aktivitätstypen konnten nicht geladen werden.",
854
+ "customers.config.activityTypes.loadingFieldsets": "Feldgruppen werden geladen…",
855
+ "customers.config.activityTypes.local": "Lokale Überschreibung",
856
+ "customers.config.activityTypes.missing": "fehlt",
857
+ "customers.config.activityTypes.missingHint": "Einige konfigurierte Feldgruppen existieren nicht mehr.",
858
+ "customers.config.activityTypes.new": "Neuer Aktivitätstyp",
859
+ "customers.config.activityTypes.no": "Nein",
860
+ "customers.config.activityTypes.noFieldsets": "Es sind noch keine Feldgruppen für benutzerdefinierte Felder definiert.",
861
+ "customers.config.activityTypes.options.assignee": "Zuständige Person",
862
+ "customers.config.activityTypes.options.attendees": "Teilnehmende",
863
+ "customers.config.activityTypes.options.call": "Anruf",
864
+ "customers.config.activityTypes.options.email": "E-Mail",
865
+ "customers.config.activityTypes.options.event": "Ereignis",
866
+ "customers.config.activityTypes.options.location": "Ort",
867
+ "customers.config.activityTypes.options.meeting": "Besprechung",
868
+ "customers.config.activityTypes.options.none": "Keine",
869
+ "customers.config.activityTypes.options.note": "Notiz",
870
+ "customers.config.activityTypes.options.participants": "Beteiligte",
871
+ "customers.config.activityTypes.options.phoneLink": "Telefonlink",
872
+ "customers.config.activityTypes.options.recipients": "Empfänger",
873
+ "customers.config.activityTypes.options.task": "Aufgabe",
874
+ "customers.config.activityTypes.refresh": "Aktualisieren",
875
+ "customers.config.activityTypes.removed": "Konfiguration des Aktivitätstyps entfernt.",
876
+ "customers.config.activityTypes.reset": "Überschreibung zurücksetzen",
877
+ "customers.config.activityTypes.resetConfirm": "Lokale Konfiguration des Aktivitätstyps entfernen?",
878
+ "customers.config.activityTypes.retry": "Erneut versuchen",
879
+ "customers.config.activityTypes.save": "Speichern",
880
+ "customers.config.activityTypes.saveFailed": "Aktivitätstyp konnte nicht gespeichert werden.",
881
+ "customers.config.activityTypes.saved": "Aktivitätstyp gespeichert.",
882
+ "customers.config.activityTypes.view": "Anzeigen",
883
+ "customers.config.activityTypes.yes": "Ja",
809
884
  "customers.config.addressFormat.description": "Legen Sie fest, wie Formulare und Ansichten im Kundenmodul strukturiert werden.",
810
885
  "customers.config.addressFormat.error": "Adresseinstellungen konnten nicht geladen werden",
811
886
  "customers.config.addressFormat.errorSave": "Adresseinstellungen konnten nicht aktualisiert werden",
@@ -1073,7 +1148,18 @@
1073
1148
  "customers.deals.detail.notesEmptyAction": "Notiz hinzufügen",
1074
1149
  "customers.deals.detail.notesEmptyTitle": "Teile die wichtigsten Informationen",
1075
1150
  "customers.deals.detail.notesLoading": "Notizen werden geladen…",
1151
+ "customers.deals.detail.people.addNewSubtitle": "The new contact is linked to this deal automatically",
1152
+ "customers.deals.detail.people.dialogSubtitle": "Link an existing contact to this deal",
1153
+ "customers.deals.detail.people.linkAction": "Link existing person",
1154
+ "customers.deals.detail.people.loadError": "Failed to load people.",
1155
+ "customers.deals.detail.people.searchPlaceholder": "Search by name, role, email...",
1156
+ "customers.deals.detail.people.sectionSubtitle": "Contacts involved in this deal",
1157
+ "customers.deals.detail.people.sectionTitle": "People",
1158
+ "customers.deals.detail.peopleAdd": "Add person",
1076
1159
  "customers.deals.detail.peopleEditorTitle": "Manage linked people",
1160
+ "customers.deals.detail.peopleEmpty": "No people linked to this deal yet.",
1161
+ "customers.deals.detail.peopleEmptyAction": "Add person",
1162
+ "customers.deals.detail.peopleEmptyTitle": "Link the people involved",
1077
1163
  "customers.deals.detail.peopleNoDetails": "Keine weiteren Details",
1078
1164
  "customers.deals.detail.peopleSearch": "Search linked people…",
1079
1165
  "customers.deals.detail.peopleSection": "Personen",
@@ -1702,6 +1788,15 @@
1702
1788
  "customers.interactions.status.waiting": "Waiting / blocked",
1703
1789
  "customers.interactions.update.error": "Failed to update interaction.",
1704
1790
  "customers.interactions.update.success": "Interaction updated.",
1791
+ "customers.linkedPeople.filter": "Filters",
1792
+ "customers.linkedPeople.loading": "Personen werden geladen…",
1793
+ "customers.linkedPeople.next": "Next",
1794
+ "customers.linkedPeople.noSearchResults": "No people match your search.",
1795
+ "customers.linkedPeople.pageSummary": "Page {{page}} of {{total}}",
1796
+ "customers.linkedPeople.previous": "Previous",
1797
+ "customers.linkedPeople.sortNameAsc": "Sort: Name A-Z",
1798
+ "customers.linkedPeople.sortNameDesc": "Sort: Name Z-A",
1799
+ "customers.linkedPeople.sortRecent": "Sort: Recently linked",
1705
1800
  "customers.linking.actions.cancel": "Abbrechen",
1706
1801
  "customers.linking.actions.save": "Verknüpfungen speichern",
1707
1802
  "customers.linking.actions.saving": "Speichern…",
@@ -1812,6 +1907,7 @@
1812
1907
  "customers.people.createDialog.required": "Fields marked * are required",
1813
1908
  "customers.people.createDialog.submit": "Create person",
1814
1909
  "customers.people.createDialog.success": "Person created and linked to company",
1910
+ "customers.people.createDialog.successPlain": "Person created",
1815
1911
  "customers.people.createDialog.title": "Add new person",
1816
1912
  "customers.people.decisionMakers.count": "{{count}} key people",
1817
1913
  "customers.people.decisionMakers.label": "Decision Makers",
@@ -193,6 +193,11 @@
193
193
  "customers.audit.todos.link": "Link todo",
194
194
  "customers.audit.todos.unlink": "Unlink todo",
195
195
  "customers.calendar.actions.newEvent": "New event",
196
+ "customers.calendar.activityTypes.errors.changeConfirmationRequired": "Changing the event type will clear existing values.",
197
+ "customers.calendar.activityTypes.errors.fieldNotApplicable": "This field is not applicable to the selected activity type.",
198
+ "customers.calendar.activityTypes.errors.forbidden": "You do not have permission to view activity types.",
199
+ "customers.calendar.activityTypes.errors.loadFailed": "Failed to load activity types.",
200
+ "customers.calendar.activityTypes.errors.unavailable": "This activity type is unavailable.",
196
201
  "customers.calendar.agenda.eventsCount.few": "{count} events",
197
202
  "customers.calendar.agenda.eventsCount.many": "{count} events",
198
203
  "customers.calendar.agenda.eventsCount.one": "{count} event",
@@ -232,6 +237,8 @@
232
237
  "customers.calendar.editor.assigneePlaceholder": "Assign to a team member…",
233
238
  "customers.calendar.editor.attendees": "Attendees",
234
239
  "customers.calendar.editor.cancel": "Cancel",
240
+ "customers.calendar.editor.catalogLoadFailed": "Activity types could not be loaded. Retry to save this event.",
241
+ "customers.calendar.editor.catalogLoading": "Loading activity types…",
235
242
  "customers.calendar.editor.category": "Category",
236
243
  "customers.calendar.editor.close": "Close",
237
244
  "customers.calendar.editor.conflictTitle": "Calendar conflict",
@@ -249,8 +256,14 @@
249
256
  "customers.calendar.editor.dealsSection": "Deals",
250
257
  "customers.calendar.editor.description": "Description",
251
258
  "customers.calendar.editor.descriptionPlaceholder": "Add details…",
259
+ "customers.calendar.editor.discardConfirm": "Change type and clear values",
260
+ "customers.calendar.editor.discardDescription": "Changing the event type will clear values that do not apply to the new type.",
261
+ "customers.calendar.editor.discardFields": "Values to clear:",
262
+ "customers.calendar.editor.discardTitle": "Change event type?",
263
+ "customers.calendar.editor.discardUnknownField": "Custom field",
252
264
  "customers.calendar.editor.error": "Failed to save event",
253
265
  "customers.calendar.editor.guestBadge": "Guest",
266
+ "customers.calendar.editor.historicalType": "This event type is unavailable for new events. You can keep editing this event or choose another type.",
254
267
  "customers.calendar.editor.location": "Location",
255
268
  "customers.calendar.editor.locationPlaceholder": "Add a location or link…",
256
269
  "customers.calendar.editor.multiDayHint": "Multi-day event · {count} days",
@@ -295,6 +308,7 @@
295
308
  "customers.calendar.editor.saved": "Event saved",
296
309
  "customers.calendar.editor.saving": "Saving…",
297
310
  "customers.calendar.editor.searching": "Searching…",
311
+ "customers.calendar.editor.timezone": "Time zone",
298
312
  "customers.calendar.editor.title.create": "New event",
299
313
  "customers.calendar.editor.title.edit": "Edit event",
300
314
  "customers.calendar.editor.titleLabel.email": "Subject",
@@ -303,6 +317,7 @@
303
317
  "customers.calendar.editor.titlePlaceholder": "Add a title…",
304
318
  "customers.calendar.editor.to": "To",
305
319
  "customers.calendar.editor.typeSwitcher": "Event type",
320
+ "customers.calendar.editor.typeUnavailable": "Choose an available event type before saving.",
306
321
  "customers.calendar.editor.types.call": "Call",
307
322
  "customers.calendar.editor.types.email": "Email",
308
323
  "customers.calendar.editor.types.event": "Event",
@@ -312,6 +327,8 @@
312
327
  "customers.calendar.editor.validation.assigneeRequired": "Assign this task to a team member",
313
328
  "customers.calendar.editor.validation.endsBeforeStarts": "End must be after start",
314
329
  "customers.calendar.editor.validation.relatedToRequired": "Select a person or company to link this event",
330
+ "customers.calendar.editor.validation.timezoneGap": "This local time does not exist in the selected time zone",
331
+ "customers.calendar.editor.validation.timezoneInvalid": "Choose a valid time zone",
315
332
  "customers.calendar.editor.validation.titleRequired": "Title is required",
316
333
  "customers.calendar.empty.action": "New event",
317
334
  "customers.calendar.empty.agenda": "Nothing scheduled in this period",
@@ -368,6 +385,7 @@
368
385
  "customers.calendar.settings.conflictWarnings": "Conflict warnings",
369
386
  "customers.calendar.settings.eventCategories": "Event Categories",
370
387
  "customers.calendar.settings.eventCategoriesHint": "Your own grouping labels (e.g. Team Meeting, Sales Call). Offered when creating an event.",
388
+ "customers.calendar.settings.manageActivityTypes": "Manage activity types",
371
389
  "customers.calendar.settings.max": "(max. {count})",
372
390
  "customers.calendar.settings.removeTag": "Remove {tag}",
373
391
  "customers.calendar.settings.save": "Save Changes",
@@ -642,7 +660,6 @@
642
660
  "customers.companies.detail.notes.placeholder": "Write a note about this company…",
643
661
  "customers.companies.detail.people.add": "Add person",
644
662
  "customers.companies.detail.people.empty": "No people linked to this company yet.",
645
- "customers.companies.detail.people.filter": "Filters",
646
663
  "customers.companies.detail.people.helper": "People linked to this company appear here and can be created, linked, opened, or unlinked without leaving the page.",
647
664
  "customers.companies.detail.people.linkAction": "Link existing person",
648
665
  "customers.companies.detail.people.linkCancel": "Cancel",
@@ -671,20 +688,13 @@
671
688
  "customers.companies.detail.people.linkedOn": "Linked on {{date}}",
672
689
  "customers.companies.detail.people.loadError": "Failed to load people.",
673
690
  "customers.companies.detail.people.loading": "Loading people…",
674
- "customers.companies.detail.people.next": "Next",
675
- "customers.companies.detail.people.noSearchResults": "No people match your search.",
676
691
  "customers.companies.detail.people.open": "Open person",
677
- "customers.companies.detail.people.pageSummary": "Page {{page}} of {{total}}",
678
- "customers.companies.detail.people.previous": "Previous",
679
692
  "customers.companies.detail.people.remove": "Unlink",
680
693
  "customers.companies.detail.people.removeError": "Failed to unlink person from company.",
681
694
  "customers.companies.detail.people.removeSuccess": "Person unlinked from company.",
682
695
  "customers.companies.detail.people.searchPlaceholder": "Search by name, role, email...",
683
696
  "customers.companies.detail.people.sectionSubtitle": "Employees and decision makers on the client side",
684
697
  "customers.companies.detail.people.sectionTitle": "People",
685
- "customers.companies.detail.people.sortNameAsc": "Sort: Name A-Z",
686
- "customers.companies.detail.people.sortNameDesc": "Sort: Name Z-A",
687
- "customers.companies.detail.people.sortRecent": "Sort: Recently linked",
688
698
  "customers.companies.detail.relativeTime.daysAgo": "{{days}} days ago",
689
699
  "customers.companies.detail.relativeTime.monthsAgo": "{{months}} mo. ago",
690
700
  "customers.companies.detail.relativeTime.oneDayAgo": "1 day ago",
@@ -806,6 +816,71 @@
806
816
  "customers.companies.presets.inactive60": "Inactive > 60 days",
807
817
  "customers.companies.presets.myAccounts": "My accounts",
808
818
  "customers.companies.presets.recentlyCreated": "Recently created",
819
+ "customers.config.activityTypes.appearance": "Appearance",
820
+ "customers.config.activityTypes.behavior": "Form behavior",
821
+ "customers.config.activityTypes.columns.active": "Selectable",
822
+ "customers.config.activityTypes.columns.behavior": "Behavior",
823
+ "customers.config.activityTypes.columns.fields": "Fields",
824
+ "customers.config.activityTypes.columns.fieldsets": "Fieldsets",
825
+ "customers.config.activityTypes.columns.key": "Stable key",
826
+ "customers.config.activityTypes.columns.order": "Order",
827
+ "customers.config.activityTypes.columns.source": "Source",
828
+ "customers.config.activityTypes.columns.type": "Activity type",
829
+ "customers.config.activityTypes.configure": "Configure",
830
+ "customers.config.activityTypes.delete": "Delete",
831
+ "customers.config.activityTypes.empty": "No activity types available.",
832
+ "customers.config.activityTypes.fields.allDay": "All day",
833
+ "customers.config.activityTypes.fields.baseKind": "Base behavior",
834
+ "customers.config.activityTypes.fields.color": "Color (#RRGGBB)",
835
+ "customers.config.activityTypes.fields.customFieldsetIds": "Custom-field fieldsets",
836
+ "customers.config.activityTypes.fields.endTime": "End time",
837
+ "customers.config.activityTypes.fields.icon": "Icon",
838
+ "customers.config.activityTypes.fields.label": "Label",
839
+ "customers.config.activityTypes.fields.location": "Location field",
840
+ "customers.config.activityTypes.fields.order": "Display order",
841
+ "customers.config.activityTypes.fields.people": "People field",
842
+ "customers.config.activityTypes.fields.priority": "Priority",
843
+ "customers.config.activityTypes.fields.recurrence": "Recurrence",
844
+ "customers.config.activityTypes.fields.resources": "Resources",
845
+ "customers.config.activityTypes.fields.selectable": "Available for new events",
846
+ "customers.config.activityTypes.fields.value": "Stable key",
847
+ "customers.config.activityTypes.fieldsetsUnavailable": "Fieldsets are unavailable. Retry loading before saving.",
848
+ "customers.config.activityTypes.inactiveHint": "A module currently prevents this override from taking effect.",
849
+ "customers.config.activityTypes.inherited": "Inherited",
850
+ "customers.config.activityTypes.inheritedHint": "Saving creates a local override for this organization.",
851
+ "customers.config.activityTypes.invalidBehavior": "Check the form behavior values.",
852
+ "customers.config.activityTypes.invalidKey": "Use a lowercase key with letters, numbers, hyphens, or underscores.",
853
+ "customers.config.activityTypes.loadFailed": "Failed to load activity types.",
854
+ "customers.config.activityTypes.loadingFieldsets": "Loading fieldsets…",
855
+ "customers.config.activityTypes.local": "Local override",
856
+ "customers.config.activityTypes.missing": "missing",
857
+ "customers.config.activityTypes.missingHint": "Some configured fieldsets no longer exist.",
858
+ "customers.config.activityTypes.new": "New activity type",
859
+ "customers.config.activityTypes.no": "No",
860
+ "customers.config.activityTypes.noFieldsets": "No custom-field fieldsets are defined yet.",
861
+ "customers.config.activityTypes.options.assignee": "Assignee",
862
+ "customers.config.activityTypes.options.attendees": "Attendees",
863
+ "customers.config.activityTypes.options.call": "Call",
864
+ "customers.config.activityTypes.options.email": "Email",
865
+ "customers.config.activityTypes.options.event": "Event",
866
+ "customers.config.activityTypes.options.location": "Location",
867
+ "customers.config.activityTypes.options.meeting": "Meeting",
868
+ "customers.config.activityTypes.options.none": "None",
869
+ "customers.config.activityTypes.options.note": "Note",
870
+ "customers.config.activityTypes.options.participants": "Participants",
871
+ "customers.config.activityTypes.options.phoneLink": "Phone link",
872
+ "customers.config.activityTypes.options.recipients": "Recipients",
873
+ "customers.config.activityTypes.options.task": "Task",
874
+ "customers.config.activityTypes.refresh": "Refresh",
875
+ "customers.config.activityTypes.removed": "Activity type configuration removed.",
876
+ "customers.config.activityTypes.reset": "Reset override",
877
+ "customers.config.activityTypes.resetConfirm": "Remove the local activity type configuration?",
878
+ "customers.config.activityTypes.retry": "Retry",
879
+ "customers.config.activityTypes.save": "Save",
880
+ "customers.config.activityTypes.saveFailed": "Failed to save activity type.",
881
+ "customers.config.activityTypes.saved": "Activity type saved.",
882
+ "customers.config.activityTypes.view": "View",
883
+ "customers.config.activityTypes.yes": "Yes",
809
884
  "customers.config.addressFormat.description": "Choose how address forms and displays should be structured across the customer module.",
810
885
  "customers.config.addressFormat.error": "Failed to load address settings",
811
886
  "customers.config.addressFormat.errorSave": "Failed to update address settings",
@@ -1073,7 +1148,18 @@
1073
1148
  "customers.deals.detail.notesEmptyAction": "Add a note",
1074
1149
  "customers.deals.detail.notesEmptyTitle": "Keep everyone in the loop",
1075
1150
  "customers.deals.detail.notesLoading": "Loading notes…",
1151
+ "customers.deals.detail.people.addNewSubtitle": "The new contact is linked to this deal automatically",
1152
+ "customers.deals.detail.people.dialogSubtitle": "Link an existing contact to this deal",
1153
+ "customers.deals.detail.people.linkAction": "Link existing person",
1154
+ "customers.deals.detail.people.loadError": "Failed to load people.",
1155
+ "customers.deals.detail.people.searchPlaceholder": "Search by name, role, email...",
1156
+ "customers.deals.detail.people.sectionSubtitle": "Contacts involved in this deal",
1157
+ "customers.deals.detail.people.sectionTitle": "People",
1158
+ "customers.deals.detail.peopleAdd": "Add person",
1076
1159
  "customers.deals.detail.peopleEditorTitle": "Manage linked people",
1160
+ "customers.deals.detail.peopleEmpty": "No people linked to this deal yet.",
1161
+ "customers.deals.detail.peopleEmptyAction": "Add person",
1162
+ "customers.deals.detail.peopleEmptyTitle": "Link the people involved",
1077
1163
  "customers.deals.detail.peopleNoDetails": "No additional details",
1078
1164
  "customers.deals.detail.peopleSearch": "Search linked people…",
1079
1165
  "customers.deals.detail.peopleSection": "People",
@@ -1702,6 +1788,15 @@
1702
1788
  "customers.interactions.status.waiting": "Waiting / blocked",
1703
1789
  "customers.interactions.update.error": "Failed to update interaction.",
1704
1790
  "customers.interactions.update.success": "Interaction updated.",
1791
+ "customers.linkedPeople.filter": "Filters",
1792
+ "customers.linkedPeople.loading": "Loading people…",
1793
+ "customers.linkedPeople.next": "Next",
1794
+ "customers.linkedPeople.noSearchResults": "No people match your search.",
1795
+ "customers.linkedPeople.pageSummary": "Page {{page}} of {{total}}",
1796
+ "customers.linkedPeople.previous": "Previous",
1797
+ "customers.linkedPeople.sortNameAsc": "Sort: Name A-Z",
1798
+ "customers.linkedPeople.sortNameDesc": "Sort: Name Z-A",
1799
+ "customers.linkedPeople.sortRecent": "Sort: Recently linked",
1705
1800
  "customers.linking.actions.cancel": "Cancel",
1706
1801
  "customers.linking.actions.save": "Save links",
1707
1802
  "customers.linking.actions.saving": "Saving…",
@@ -1812,6 +1907,7 @@
1812
1907
  "customers.people.createDialog.required": "Fields marked * are required",
1813
1908
  "customers.people.createDialog.submit": "Create person",
1814
1909
  "customers.people.createDialog.success": "Person created and linked to company",
1910
+ "customers.people.createDialog.successPlain": "Person created",
1815
1911
  "customers.people.createDialog.title": "Add new person",
1816
1912
  "customers.people.decisionMakers.count": "{{count}} key people",
1817
1913
  "customers.people.decisionMakers.label": "Decision Makers",