@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.
- package/.turbo/turbo-build.log +1 -1
- package/dist/generated/entities/customer_dictionary_entry/index.js +2 -0
- package/dist/generated/entities/customer_dictionary_entry/index.js.map +2 -2
- package/dist/generated/entities/customer_interaction/index.js +2 -0
- package/dist/generated/entities/customer_interaction/index.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +2 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/catalog/commands/categories.js +1 -1
- package/dist/modules/catalog/commands/categories.js.map +2 -2
- package/dist/modules/catalog/commands/productDeleteChildren.js +389 -0
- package/dist/modules/catalog/commands/productDeleteChildren.js.map +7 -0
- package/dist/modules/catalog/commands/products.js +52 -17
- package/dist/modules/catalog/commands/products.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js +4 -6
- package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js +4 -6
- package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js +47 -11
- package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js.map +3 -3
- package/dist/modules/communication_channels/commands/deliver-outbound-message.js +5 -1
- package/dist/modules/communication_channels/commands/deliver-outbound-message.js.map +2 -2
- package/dist/modules/communication_channels/lib/inbound-message-origin.js +1 -1
- package/dist/modules/communication_channels/lib/inbound-message-origin.js.map +2 -2
- package/dist/modules/communication_channels/lib/oauth-state.js +4 -1
- package/dist/modules/communication_channels/lib/oauth-state.js.map +2 -2
- package/dist/modules/communication_channels/lib/use-connect-channel.js +10 -0
- package/dist/modules/communication_channels/lib/use-connect-channel.js.map +2 -2
- package/dist/modules/customers/api/activity-types/route.js +151 -0
- package/dist/modules/customers/api/activity-types/route.js.map +7 -0
- package/dist/modules/customers/api/deals/[id]/people/route.js +58 -5
- package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js +13 -5
- package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/route.js +20 -5
- package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/route.js +3 -0
- package/dist/modules/customers/api/interactions/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/tasks/route.js +1 -0
- package/dist/modules/customers/api/interactions/tasks/route.js.map +2 -2
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.meta.js +18 -0
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.meta.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js +0 -23
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +15 -17
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/calendar-event-types.js +548 -0
- package/dist/modules/customers/calendar-event-types.js.map +7 -0
- package/dist/modules/customers/commands/dictionaries.js +70 -10
- package/dist/modules/customers/commands/dictionaries.js.map +2 -2
- package/dist/modules/customers/commands/interactions.js +187 -22
- package/dist/modules/customers/commands/interactions.js.map +3 -3
- package/dist/modules/customers/components/ActivityTypeEditor.js +288 -0
- package/dist/modules/customers/components/ActivityTypeEditor.js.map +7 -0
- package/dist/modules/customers/components/DictionarySettings.js +3 -1
- package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js +391 -297
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js.map +3 -3
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js +9 -74
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js.map +2 -2
- package/dist/modules/customers/components/calendar/TimeGrid.js +20 -16
- package/dist/modules/customers/components/calendar/TimeGrid.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/EventTypePanel.js +218 -0
- package/dist/modules/customers/components/calendar/editor/EventTypePanel.js.map +7 -0
- package/dist/modules/customers/components/calendar/editor/LocationField.js +7 -3
- package/dist/modules/customers/components/calendar/editor/LocationField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/PeopleField.js +5 -2
- package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/PriorityField.js +3 -1
- package/dist/modules/customers/components/calendar/editor/PriorityField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/RepeatField.js +9 -1
- package/dist/modules/customers/components/calendar/editor/RepeatField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/ResourcesField.js +4 -1
- package/dist/modules/customers/components/calendar/editor/ResourcesField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/ScheduleSection.js +15 -7
- package/dist/modules/customers/components/calendar/editor/ScheduleSection.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/SegmentGroup.js +3 -1
- package/dist/modules/customers/components/calendar/editor/SegmentGroup.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/hooks.js +28 -13
- package/dist/modules/customers/components/calendar/editor/hooks.js.map +3 -3
- package/dist/modules/customers/components/calendar/editor/inputs.js +14 -6
- package/dist/modules/customers/components/calendar/editor/inputs.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/useCalendarCustomFields.js +54 -0
- package/dist/modules/customers/components/calendar/editor/useCalendarCustomFields.js.map +7 -0
- package/dist/modules/customers/components/calendar/editor/useEventTypeCatalog.js +82 -0
- package/dist/modules/customers/components/calendar/editor/useEventTypeCatalog.js.map +7 -0
- package/dist/modules/customers/components/calendar/types.js +1 -0
- package/dist/modules/customers/components/calendar/types.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js +78 -343
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
- package/dist/modules/customers/components/detail/CreatePersonDialog.js +12 -11
- package/dist/modules/customers/components/detail/CreatePersonDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/DealForm.js +7 -3
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/components/detail/DealPeopleSection.js +206 -0
- package/dist/modules/customers/components/detail/DealPeopleSection.js.map +7 -0
- package/dist/modules/customers/components/detail/LinkedPeopleSection.js +342 -0
- package/dist/modules/customers/components/detail/LinkedPeopleSection.js.map +7 -0
- package/dist/modules/customers/components/detail/PersonCard.js +5 -2
- package/dist/modules/customers/components/detail/PersonCard.js.map +2 -2
- package/dist/modules/customers/components/linking/adapters/personAdapter.js +3 -0
- package/dist/modules/customers/components/linking/adapters/personAdapter.js.map +2 -2
- package/dist/modules/customers/data/entities.js +6 -0
- package/dist/modules/customers/data/entities.js.map +2 -2
- package/dist/modules/customers/data/validators.js +39 -10
- package/dist/modules/customers/data/validators.js.map +2 -2
- package/dist/modules/customers/di.js +20 -1
- package/dist/modules/customers/di.js.map +2 -2
- package/dist/modules/customers/extension-points.js +13 -0
- package/dist/modules/customers/extension-points.js.map +2 -2
- package/dist/modules/customers/lib/calendar/conflicts.js +59 -16
- package/dist/modules/customers/lib/calendar/conflicts.js.map +2 -2
- package/dist/modules/customers/lib/calendar/editorPayload.js +34 -16
- package/dist/modules/customers/lib/calendar/editorPayload.js.map +2 -2
- package/dist/modules/customers/lib/calendar/eventTypeResolver.js +197 -0
- package/dist/modules/customers/lib/calendar/eventTypeResolver.js.map +7 -0
- package/dist/modules/customers/lib/calendar/interactionApplicability.js +67 -0
- package/dist/modules/customers/lib/calendar/interactionApplicability.js.map +7 -0
- package/dist/modules/customers/lib/calendar/mapItem.js +5 -2
- package/dist/modules/customers/lib/calendar/mapItem.js.map +2 -2
- package/dist/modules/customers/lib/calendar/recurrence.js +62 -1
- package/dist/modules/customers/lib/calendar/recurrence.js.map +2 -2
- package/dist/modules/customers/lib/calendar/timezone.js +111 -0
- package/dist/modules/customers/lib/calendar/timezone.js.map +7 -0
- package/dist/modules/customers/lib/interactionReadModel.js +1 -0
- package/dist/modules/customers/lib/interactionReadModel.js.map +2 -2
- package/dist/modules/customers/lib/todoCompatibility.js +1 -0
- package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
- package/dist/modules/customers/migrations/Migration20260928175058_customers.js +17 -0
- package/dist/modules/customers/migrations/Migration20260928175058_customers.js.map +7 -0
- package/dist/modules/customers/migrations/Migration20260930110235_customers.js +17 -0
- package/dist/modules/customers/migrations/Migration20260930110235_customers.js.map +7 -0
- package/dist/modules/dictionaries/components/DictionaryTable.js +60 -51
- package/dist/modules/dictionaries/components/DictionaryTable.js.map +2 -2
- package/dist/modules/messages/subscribers/message-notification.js +13 -1
- package/dist/modules/messages/subscribers/message-notification.js.map +2 -2
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js +201 -53
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +2 -2
- package/dist/modules/payment_gateways/lib/gateway-service.js +37 -11
- package/dist/modules/payment_gateways/lib/gateway-service.js.map +2 -2
- package/dist/modules/planner/lib/availabilityMerge.js +101 -38
- package/dist/modules/planner/lib/availabilityMerge.js.map +3 -3
- package/dist/modules/planner/services/plannerAvailabilityService.js.map +2 -2
- package/dist/modules/sales/components/documents/LineItemDialog.js +159 -81
- package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/lineItemUtils.js +7 -0
- package/dist/modules/sales/components/documents/lineItemUtils.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js +12 -7
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js.map +2 -2
- package/dist/modules/sync_excel/lib/adapters/customers.js +28 -25
- package/dist/modules/sync_excel/lib/adapters/customers.js.map +2 -2
- package/generated/entities/customer_dictionary_entry/index.ts +1 -0
- package/generated/entities/customer_interaction/index.ts +1 -0
- package/generated/entity-fields-registry.ts +2 -0
- package/package.json +9 -8
- package/src/modules/auth/i18n/de.json +3 -0
- package/src/modules/auth/i18n/en.json +3 -0
- package/src/modules/auth/i18n/es.json +3 -0
- package/src/modules/auth/i18n/ko.json +3 -0
- package/src/modules/auth/i18n/pl.json +3 -0
- package/src/modules/catalog/commands/categories.ts +1 -1
- package/src/modules/catalog/commands/productDeleteChildren.ts +557 -0
- package/src/modules/catalog/commands/products.ts +58 -17
- package/src/modules/communication_channels/api/post/channels/connect/credentials/route.ts +4 -6
- package/src/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.ts +4 -6
- package/src/modules/communication_channels/api/post/oauth/[provider]/initiate/route.ts +46 -10
- package/src/modules/communication_channels/commands/deliver-outbound-message.ts +6 -1
- package/src/modules/communication_channels/i18n/de.json +4 -0
- package/src/modules/communication_channels/i18n/en.json +4 -0
- package/src/modules/communication_channels/i18n/es.json +4 -0
- package/src/modules/communication_channels/i18n/ko.json +4 -0
- package/src/modules/communication_channels/i18n/pl.json +4 -0
- package/src/modules/communication_channels/lib/inbound-message-origin.ts +2 -6
- package/src/modules/communication_channels/lib/oauth-state.ts +4 -1
- package/src/modules/communication_channels/lib/use-connect-channel.ts +10 -0
- package/src/modules/customers/api/activity-types/route.ts +154 -0
- package/src/modules/customers/api/deals/[id]/people/route.ts +82 -3
- package/src/modules/customers/api/dictionaries/[kind]/[id]/route.ts +9 -1
- package/src/modules/customers/api/dictionaries/[kind]/route.ts +18 -2
- package/src/modules/customers/api/interactions/route.ts +4 -0
- package/src/modules/customers/api/interactions/tasks/route.ts +1 -0
- package/src/modules/customers/backend/config/customers/pipeline-stages/page.meta.ts +20 -0
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.ts +0 -29
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +15 -17
- package/src/modules/customers/calendar-event-types.ts +674 -0
- package/src/modules/customers/commands/dictionaries.ts +84 -8
- package/src/modules/customers/commands/interactions.ts +244 -21
- package/src/modules/customers/components/ActivityTypeEditor.tsx +239 -0
- package/src/modules/customers/components/DictionarySettings.tsx +5 -3
- package/src/modules/customers/components/calendar/CalendarEventEditor.tsx +347 -218
- package/src/modules/customers/components/calendar/CalendarSettingsModal.tsx +18 -96
- package/src/modules/customers/components/calendar/TimeGrid.tsx +20 -18
- package/src/modules/customers/components/calendar/editor/EventTypePanel.tsx +230 -0
- package/src/modules/customers/components/calendar/editor/LocationField.tsx +7 -1
- package/src/modules/customers/components/calendar/editor/PeopleField.tsx +4 -0
- package/src/modules/customers/components/calendar/editor/PriorityField.tsx +3 -0
- package/src/modules/customers/components/calendar/editor/RepeatField.tsx +10 -0
- package/src/modules/customers/components/calendar/editor/ResourcesField.tsx +4 -0
- package/src/modules/customers/components/calendar/editor/ScheduleSection.tsx +16 -4
- package/src/modules/customers/components/calendar/editor/SegmentGroup.tsx +3 -0
- package/src/modules/customers/components/calendar/editor/hooks.ts +29 -13
- package/src/modules/customers/components/calendar/editor/inputs.tsx +17 -5
- package/src/modules/customers/components/calendar/editor/useCalendarCustomFields.ts +49 -0
- package/src/modules/customers/components/calendar/editor/useEventTypeCatalog.ts +100 -0
- package/src/modules/customers/components/calendar/types.ts +1 -0
- package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +94 -388
- package/src/modules/customers/components/detail/CreatePersonDialog.tsx +39 -19
- package/src/modules/customers/components/detail/DealForm.tsx +7 -3
- package/src/modules/customers/components/detail/DealPeopleSection.tsx +256 -0
- package/src/modules/customers/components/detail/LinkedPeopleSection.tsx +474 -0
- package/src/modules/customers/components/detail/PersonCard.tsx +12 -4
- package/src/modules/customers/components/detail/types.ts +1 -0
- package/src/modules/customers/components/linking/adapters/personAdapter.tsx +4 -0
- package/src/modules/customers/data/entities.ts +8 -1
- package/src/modules/customers/data/validators.ts +42 -7
- package/src/modules/customers/di.ts +21 -0
- package/src/modules/customers/extension-points.ts +13 -0
- package/src/modules/customers/i18n/de.json +104 -8
- package/src/modules/customers/i18n/en.json +104 -8
- package/src/modules/customers/i18n/es.json +104 -8
- package/src/modules/customers/i18n/ko.json +104 -8
- package/src/modules/customers/i18n/pl.json +104 -8
- package/src/modules/customers/lib/calendar/conflicts.ts +106 -27
- package/src/modules/customers/lib/calendar/editorPayload.ts +39 -19
- package/src/modules/customers/lib/calendar/eventTypeResolver.ts +258 -0
- package/src/modules/customers/lib/calendar/interactionApplicability.ts +99 -0
- package/src/modules/customers/lib/calendar/mapItem.ts +6 -2
- package/src/modules/customers/lib/calendar/recurrence.ts +65 -1
- package/src/modules/customers/lib/calendar/timezone.ts +117 -0
- package/src/modules/customers/lib/interactionReadModel.ts +1 -0
- package/src/modules/customers/lib/todoCompatibility.ts +2 -0
- package/src/modules/customers/migrations/.snapshot-open-mercato.json +34 -0
- package/src/modules/customers/migrations/Migration20260928175058_customers.ts +15 -0
- package/src/modules/customers/migrations/Migration20260930110235_customers.ts +15 -0
- package/src/modules/dictionaries/components/DictionaryTable.tsx +63 -56
- package/src/modules/messages/subscribers/message-notification.ts +13 -3
- package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +243 -62
- package/src/modules/payment_gateways/lib/gateway-service.ts +39 -12
- package/src/modules/planner/lib/availabilityMerge.ts +106 -41
- package/src/modules/planner/services/plannerAvailabilityService.ts +4 -0
- package/src/modules/sales/components/documents/LineItemDialog.tsx +194 -111
- package/src/modules/sales/components/documents/lineItemUtils.ts +18 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx +25 -13
- package/src/modules/sync_excel/lib/adapters/customers.ts +33 -27
|
@@ -193,6 +193,11 @@
|
|
|
193
193
|
"customers.audit.todos.link": "Powiąż zadanie",
|
|
194
194
|
"customers.audit.todos.unlink": "Odepnij zadanie",
|
|
195
195
|
"customers.calendar.actions.newEvent": "Nowe wydarzenie",
|
|
196
|
+
"customers.calendar.activityTypes.errors.changeConfirmationRequired": "Zmiana typu wydarzenia usunie istniejące wartości.",
|
|
197
|
+
"customers.calendar.activityTypes.errors.fieldNotApplicable": "To pole nie dotyczy wybranego typu aktywności.",
|
|
198
|
+
"customers.calendar.activityTypes.errors.forbidden": "Nie masz uprawnień do wyświetlania typów aktywności.",
|
|
199
|
+
"customers.calendar.activityTypes.errors.loadFailed": "Nie udało się wczytać typów aktywności.",
|
|
200
|
+
"customers.calendar.activityTypes.errors.unavailable": "Ten typ aktywności jest niedostępny.",
|
|
196
201
|
"customers.calendar.agenda.eventsCount.few": "{count} wydarzenia",
|
|
197
202
|
"customers.calendar.agenda.eventsCount.many": "{count} wydarzeń",
|
|
198
203
|
"customers.calendar.agenda.eventsCount.one": "{count} wydarzenie",
|
|
@@ -232,6 +237,8 @@
|
|
|
232
237
|
"customers.calendar.editor.assigneePlaceholder": "Przypisz do członka zespołu…",
|
|
233
238
|
"customers.calendar.editor.attendees": "Uczestnicy",
|
|
234
239
|
"customers.calendar.editor.cancel": "Anuluj",
|
|
240
|
+
"customers.calendar.editor.catalogLoadFailed": "Nie udało się wczytać typów aktywności. Spróbuj ponownie, aby zapisać wydarzenie.",
|
|
241
|
+
"customers.calendar.editor.catalogLoading": "Wczytywanie typów aktywności…",
|
|
235
242
|
"customers.calendar.editor.category": "Kategoria",
|
|
236
243
|
"customers.calendar.editor.close": "Zamknij",
|
|
237
244
|
"customers.calendar.editor.conflictTitle": "Konflikt w kalendarzu",
|
|
@@ -249,8 +256,14 @@
|
|
|
249
256
|
"customers.calendar.editor.dealsSection": "Szanse",
|
|
250
257
|
"customers.calendar.editor.description": "Opis",
|
|
251
258
|
"customers.calendar.editor.descriptionPlaceholder": "Dodaj szczegóły…",
|
|
259
|
+
"customers.calendar.editor.discardConfirm": "Zmień typ i usuń wartości",
|
|
260
|
+
"customers.calendar.editor.discardDescription": "Zmiana typu wydarzenia usunie wartości, które nie dotyczą nowego typu.",
|
|
261
|
+
"customers.calendar.editor.discardFields": "Wartości do usunięcia:",
|
|
262
|
+
"customers.calendar.editor.discardTitle": "Zmienić typ wydarzenia?",
|
|
263
|
+
"customers.calendar.editor.discardUnknownField": "Pole niestandardowe",
|
|
252
264
|
"customers.calendar.editor.error": "Nie udało się zapisać wydarzenia",
|
|
253
265
|
"customers.calendar.editor.guestBadge": "Gość",
|
|
266
|
+
"customers.calendar.editor.historicalType": "Ten typ wydarzenia nie jest dostępny dla nowych wydarzeń. Możesz dalej edytować to wydarzenie albo wybrać inny typ.",
|
|
254
267
|
"customers.calendar.editor.location": "Lokalizacja",
|
|
255
268
|
"customers.calendar.editor.locationPlaceholder": "Dodaj lokalizację lub link…",
|
|
256
269
|
"customers.calendar.editor.multiDayHint": "Wydarzenie wielodniowe · {count} dni",
|
|
@@ -295,6 +308,7 @@
|
|
|
295
308
|
"customers.calendar.editor.saved": "Wydarzenie zapisane",
|
|
296
309
|
"customers.calendar.editor.saving": "Zapisywanie…",
|
|
297
310
|
"customers.calendar.editor.searching": "Wyszukiwanie…",
|
|
311
|
+
"customers.calendar.editor.timezone": "Strefa czasowa",
|
|
298
312
|
"customers.calendar.editor.title.create": "Nowe wydarzenie",
|
|
299
313
|
"customers.calendar.editor.title.edit": "Edytuj wydarzenie",
|
|
300
314
|
"customers.calendar.editor.titleLabel.email": "Temat",
|
|
@@ -303,6 +317,7 @@
|
|
|
303
317
|
"customers.calendar.editor.titlePlaceholder": "Dodaj tytuł…",
|
|
304
318
|
"customers.calendar.editor.to": "Do",
|
|
305
319
|
"customers.calendar.editor.typeSwitcher": "Typ wydarzenia",
|
|
320
|
+
"customers.calendar.editor.typeUnavailable": "Przed zapisaniem wybierz dostępny typ wydarzenia.",
|
|
306
321
|
"customers.calendar.editor.types.call": "Telefon",
|
|
307
322
|
"customers.calendar.editor.types.email": "E-mail",
|
|
308
323
|
"customers.calendar.editor.types.event": "Wydarzenie",
|
|
@@ -312,6 +327,8 @@
|
|
|
312
327
|
"customers.calendar.editor.validation.assigneeRequired": "Przypisz to zadanie do członka zespołu",
|
|
313
328
|
"customers.calendar.editor.validation.endsBeforeStarts": "Zakończenie musi być po rozpoczęciu",
|
|
314
329
|
"customers.calendar.editor.validation.relatedToRequired": "Wybierz osobę lub firmę, aby powiązać to wydarzenie",
|
|
330
|
+
"customers.calendar.editor.validation.timezoneGap": "Ta godzina nie istnieje w wybranej strefie czasowej",
|
|
331
|
+
"customers.calendar.editor.validation.timezoneInvalid": "Wybierz poprawną strefę czasową",
|
|
315
332
|
"customers.calendar.editor.validation.titleRequired": "Tytuł jest wymagany",
|
|
316
333
|
"customers.calendar.empty.action": "Nowe wydarzenie",
|
|
317
334
|
"customers.calendar.empty.agenda": "Brak zaplanowanych wydarzeń w tym okresie",
|
|
@@ -368,6 +385,7 @@
|
|
|
368
385
|
"customers.calendar.settings.conflictWarnings": "Ostrzeżenia o konfliktach",
|
|
369
386
|
"customers.calendar.settings.eventCategories": "Kategorie wydarzeń",
|
|
370
387
|
"customers.calendar.settings.eventCategoriesHint": "Twoje własne etykiety grupujące (np. Spotkanie zespołu, Telefon sprzedażowy). Pojawiają się podczas tworzenia wydarzenia.",
|
|
388
|
+
"customers.calendar.settings.manageActivityTypes": "Zarządzaj typami aktywności",
|
|
371
389
|
"customers.calendar.settings.max": "(maks. {count})",
|
|
372
390
|
"customers.calendar.settings.removeTag": "Usuń {tag}",
|
|
373
391
|
"customers.calendar.settings.save": "Zapisz zmiany",
|
|
@@ -642,7 +660,6 @@
|
|
|
642
660
|
"customers.companies.detail.notes.placeholder": "Napisz notatkę o tej firmie…",
|
|
643
661
|
"customers.companies.detail.people.add": "Dodaj osobę",
|
|
644
662
|
"customers.companies.detail.people.empty": "Brak powiązanych osób z tą firmą.",
|
|
645
|
-
"customers.companies.detail.people.filter": "Filtry",
|
|
646
663
|
"customers.companies.detail.people.helper": "Osoby powiązane z tą firmą pojawiają się tutaj i mogą być tworzone, przypinane, otwierane lub odpinane bez opuszczania strony.",
|
|
647
664
|
"customers.companies.detail.people.linkAction": "Podepnij istniejącą osobę",
|
|
648
665
|
"customers.companies.detail.people.linkCancel": "Anuluj",
|
|
@@ -671,20 +688,13 @@
|
|
|
671
688
|
"customers.companies.detail.people.linkedOn": "Powiązano {{date}}",
|
|
672
689
|
"customers.companies.detail.people.loadError": "Nie udało się załadować osób.",
|
|
673
690
|
"customers.companies.detail.people.loading": "Ładowanie osób…",
|
|
674
|
-
"customers.companies.detail.people.next": "Następna",
|
|
675
|
-
"customers.companies.detail.people.noSearchResults": "Żadna osoba nie pasuje do wyszukiwania.",
|
|
676
691
|
"customers.companies.detail.people.open": "Otwórz osobę",
|
|
677
|
-
"customers.companies.detail.people.pageSummary": "Strona {{page}} z {{total}}",
|
|
678
|
-
"customers.companies.detail.people.previous": "Poprzednia",
|
|
679
692
|
"customers.companies.detail.people.remove": "Odepnij",
|
|
680
693
|
"customers.companies.detail.people.removeError": "Nie udało się odpiąć osoby od firmy.",
|
|
681
694
|
"customers.companies.detail.people.removeSuccess": "Osoba odpięta od firmy.",
|
|
682
695
|
"customers.companies.detail.people.searchPlaceholder": "Szukaj po nazwisku, roli, e-mailu...",
|
|
683
696
|
"customers.companies.detail.people.sectionSubtitle": "Pracownicy i decydenci po stronie klienta",
|
|
684
697
|
"customers.companies.detail.people.sectionTitle": "Osoby",
|
|
685
|
-
"customers.companies.detail.people.sortNameAsc": "Sortuj: Nazwa A-Z",
|
|
686
|
-
"customers.companies.detail.people.sortNameDesc": "Sortuj: Nazwa Z-A",
|
|
687
|
-
"customers.companies.detail.people.sortRecent": "Sortuj: Ostatnio powiązane",
|
|
688
698
|
"customers.companies.detail.relativeTime.daysAgo": "{{days}} dni temu",
|
|
689
699
|
"customers.companies.detail.relativeTime.monthsAgo": "{{months}} mies. temu",
|
|
690
700
|
"customers.companies.detail.relativeTime.oneDayAgo": "1 dzień temu",
|
|
@@ -806,6 +816,71 @@
|
|
|
806
816
|
"customers.companies.presets.inactive60": "Nieaktywne > 60 dni",
|
|
807
817
|
"customers.companies.presets.myAccounts": "Moje konta",
|
|
808
818
|
"customers.companies.presets.recentlyCreated": "Ostatnio utworzone",
|
|
819
|
+
"customers.config.activityTypes.appearance": "Wygląd",
|
|
820
|
+
"customers.config.activityTypes.behavior": "Zachowanie formularza",
|
|
821
|
+
"customers.config.activityTypes.columns.active": "Możliwość wyboru",
|
|
822
|
+
"customers.config.activityTypes.columns.behavior": "Zachowanie",
|
|
823
|
+
"customers.config.activityTypes.columns.fields": "Pola",
|
|
824
|
+
"customers.config.activityTypes.columns.fieldsets": "Zestawy pól",
|
|
825
|
+
"customers.config.activityTypes.columns.key": "Stały klucz",
|
|
826
|
+
"customers.config.activityTypes.columns.order": "Kolejność",
|
|
827
|
+
"customers.config.activityTypes.columns.source": "Źródło",
|
|
828
|
+
"customers.config.activityTypes.columns.type": "Typ aktywności",
|
|
829
|
+
"customers.config.activityTypes.configure": "Konfiguruj",
|
|
830
|
+
"customers.config.activityTypes.delete": "Usuń",
|
|
831
|
+
"customers.config.activityTypes.empty": "Brak dostępnych typów aktywności.",
|
|
832
|
+
"customers.config.activityTypes.fields.allDay": "Cały dzień",
|
|
833
|
+
"customers.config.activityTypes.fields.baseKind": "Podstawowe zachowanie",
|
|
834
|
+
"customers.config.activityTypes.fields.color": "Kolor (#RRGGBB)",
|
|
835
|
+
"customers.config.activityTypes.fields.customFieldsetIds": "Zestawy pól niestandardowych",
|
|
836
|
+
"customers.config.activityTypes.fields.endTime": "Czas zakończenia",
|
|
837
|
+
"customers.config.activityTypes.fields.icon": "Ikona",
|
|
838
|
+
"customers.config.activityTypes.fields.label": "Etykieta",
|
|
839
|
+
"customers.config.activityTypes.fields.location": "Pole lokalizacji",
|
|
840
|
+
"customers.config.activityTypes.fields.order": "Kolejność wyświetlania",
|
|
841
|
+
"customers.config.activityTypes.fields.people": "Pole osób",
|
|
842
|
+
"customers.config.activityTypes.fields.priority": "Priorytet",
|
|
843
|
+
"customers.config.activityTypes.fields.recurrence": "Powtarzanie",
|
|
844
|
+
"customers.config.activityTypes.fields.resources": "Zasoby",
|
|
845
|
+
"customers.config.activityTypes.fields.selectable": "Dostępny dla nowych wydarzeń",
|
|
846
|
+
"customers.config.activityTypes.fields.value": "Stały klucz",
|
|
847
|
+
"customers.config.activityTypes.fieldsetsUnavailable": "Zestawy pól są niedostępne. Wczytaj je ponownie przed zapisem.",
|
|
848
|
+
"customers.config.activityTypes.inactiveHint": "Moduł obecnie uniemożliwia zastosowanie tego nadpisania.",
|
|
849
|
+
"customers.config.activityTypes.inherited": "Dziedziczony",
|
|
850
|
+
"customers.config.activityTypes.inheritedHint": "Zapis utworzy lokalne nadpisanie dla tej organizacji.",
|
|
851
|
+
"customers.config.activityTypes.invalidBehavior": "Sprawdź wartości zachowania formularza.",
|
|
852
|
+
"customers.config.activityTypes.invalidKey": "Użyj klucza z małych liter, cyfr, łączników lub podkreśleń.",
|
|
853
|
+
"customers.config.activityTypes.loadFailed": "Nie udało się wczytać typów aktywności.",
|
|
854
|
+
"customers.config.activityTypes.loadingFieldsets": "Wczytywanie zestawów pól…",
|
|
855
|
+
"customers.config.activityTypes.local": "Lokalne nadpisanie",
|
|
856
|
+
"customers.config.activityTypes.missing": "brak",
|
|
857
|
+
"customers.config.activityTypes.missingHint": "Niektóre skonfigurowane zestawy pól już nie istnieją.",
|
|
858
|
+
"customers.config.activityTypes.new": "Nowy typ aktywności",
|
|
859
|
+
"customers.config.activityTypes.no": "Nie",
|
|
860
|
+
"customers.config.activityTypes.noFieldsets": "Nie zdefiniowano jeszcze zestawów pól niestandardowych.",
|
|
861
|
+
"customers.config.activityTypes.options.assignee": "Osoba przypisana",
|
|
862
|
+
"customers.config.activityTypes.options.attendees": "Uczestnicy",
|
|
863
|
+
"customers.config.activityTypes.options.call": "Połączenie",
|
|
864
|
+
"customers.config.activityTypes.options.email": "E-mail",
|
|
865
|
+
"customers.config.activityTypes.options.event": "Wydarzenie",
|
|
866
|
+
"customers.config.activityTypes.options.location": "Lokalizacja",
|
|
867
|
+
"customers.config.activityTypes.options.meeting": "Spotkanie",
|
|
868
|
+
"customers.config.activityTypes.options.none": "Brak",
|
|
869
|
+
"customers.config.activityTypes.options.note": "Notatka",
|
|
870
|
+
"customers.config.activityTypes.options.participants": "Osoby uczestniczące",
|
|
871
|
+
"customers.config.activityTypes.options.phoneLink": "Łącze telefoniczne",
|
|
872
|
+
"customers.config.activityTypes.options.recipients": "Odbiorcy",
|
|
873
|
+
"customers.config.activityTypes.options.task": "Zadanie",
|
|
874
|
+
"customers.config.activityTypes.refresh": "Odśwież",
|
|
875
|
+
"customers.config.activityTypes.removed": "Usunięto konfigurację typu aktywności.",
|
|
876
|
+
"customers.config.activityTypes.reset": "Zresetuj nadpisanie",
|
|
877
|
+
"customers.config.activityTypes.resetConfirm": "Usunąć lokalną konfigurację typu aktywności?",
|
|
878
|
+
"customers.config.activityTypes.retry": "Ponów",
|
|
879
|
+
"customers.config.activityTypes.save": "Zapisz",
|
|
880
|
+
"customers.config.activityTypes.saveFailed": "Nie udało się zapisać typu aktywności.",
|
|
881
|
+
"customers.config.activityTypes.saved": "Zapisano typ aktywności.",
|
|
882
|
+
"customers.config.activityTypes.view": "Wyświetl",
|
|
883
|
+
"customers.config.activityTypes.yes": "Tak",
|
|
809
884
|
"customers.config.addressFormat.description": "Wybierz, jak powinny być zbudowane formularze i widoki w module klientów.",
|
|
810
885
|
"customers.config.addressFormat.error": "Nie udało się wczytać ustawień adresu",
|
|
811
886
|
"customers.config.addressFormat.errorSave": "Nie udało się zaktualizować ustawień adresu",
|
|
@@ -1073,7 +1148,18 @@
|
|
|
1073
1148
|
"customers.deals.detail.notesEmptyAction": "Dodaj notatkę",
|
|
1074
1149
|
"customers.deals.detail.notesEmptyTitle": "Dziel się najważniejszymi informacjami",
|
|
1075
1150
|
"customers.deals.detail.notesLoading": "Ładowanie notatek…",
|
|
1151
|
+
"customers.deals.detail.people.addNewSubtitle": "Nowy kontakt zostanie automatycznie podpięty do tej szansy sprzedaży",
|
|
1152
|
+
"customers.deals.detail.people.dialogSubtitle": "Podepnij istniejący kontakt do tej szansy sprzedaży",
|
|
1153
|
+
"customers.deals.detail.people.linkAction": "Podepnij istniejącą osobę",
|
|
1154
|
+
"customers.deals.detail.people.loadError": "Nie udało się załadować osób.",
|
|
1155
|
+
"customers.deals.detail.people.searchPlaceholder": "Szukaj po nazwisku, roli, e-mailu...",
|
|
1156
|
+
"customers.deals.detail.people.sectionSubtitle": "Kontakty zaangażowane w tę szansę sprzedaży",
|
|
1157
|
+
"customers.deals.detail.people.sectionTitle": "Osoby",
|
|
1158
|
+
"customers.deals.detail.peopleAdd": "Dodaj osobę",
|
|
1076
1159
|
"customers.deals.detail.peopleEditorTitle": "Zarządzaj powiązanymi osobami",
|
|
1160
|
+
"customers.deals.detail.peopleEmpty": "Brak osób powiązanych z tą szansą sprzedaży.",
|
|
1161
|
+
"customers.deals.detail.peopleEmptyAction": "Dodaj osobę",
|
|
1162
|
+
"customers.deals.detail.peopleEmptyTitle": "Podepnij zaangażowane osoby",
|
|
1077
1163
|
"customers.deals.detail.peopleNoDetails": "Brak dodatkowych informacji",
|
|
1078
1164
|
"customers.deals.detail.peopleSearch": "Szukaj powiązanych osób…",
|
|
1079
1165
|
"customers.deals.detail.peopleSection": "Osoby",
|
|
@@ -1702,6 +1788,15 @@
|
|
|
1702
1788
|
"customers.interactions.status.waiting": "Oczekuje / zablokowana",
|
|
1703
1789
|
"customers.interactions.update.error": "Nie udało się zaktualizować interakcji.",
|
|
1704
1790
|
"customers.interactions.update.success": "Zaktualizowano interakcję.",
|
|
1791
|
+
"customers.linkedPeople.filter": "Filtry",
|
|
1792
|
+
"customers.linkedPeople.loading": "Ładowanie osób…",
|
|
1793
|
+
"customers.linkedPeople.next": "Następna",
|
|
1794
|
+
"customers.linkedPeople.noSearchResults": "Żadna osoba nie pasuje do wyszukiwania.",
|
|
1795
|
+
"customers.linkedPeople.pageSummary": "Strona {{page}} z {{total}}",
|
|
1796
|
+
"customers.linkedPeople.previous": "Poprzednia",
|
|
1797
|
+
"customers.linkedPeople.sortNameAsc": "Sortuj: Nazwa A-Z",
|
|
1798
|
+
"customers.linkedPeople.sortNameDesc": "Sortuj: Nazwa Z-A",
|
|
1799
|
+
"customers.linkedPeople.sortRecent": "Sortuj: Ostatnio powiązane",
|
|
1705
1800
|
"customers.linking.actions.cancel": "Anuluj",
|
|
1706
1801
|
"customers.linking.actions.save": "Zapisz powiązania",
|
|
1707
1802
|
"customers.linking.actions.saving": "Zapisywanie…",
|
|
@@ -1812,6 +1907,7 @@
|
|
|
1812
1907
|
"customers.people.createDialog.required": "Pola oznaczone * są wymagane",
|
|
1813
1908
|
"customers.people.createDialog.submit": "Utwórz osobę",
|
|
1814
1909
|
"customers.people.createDialog.success": "Utworzono osobę i powiązano z firmą",
|
|
1910
|
+
"customers.people.createDialog.successPlain": "Osoba została utworzona",
|
|
1815
1911
|
"customers.people.createDialog.title": "Dodaj nową osobę",
|
|
1816
1912
|
"customers.people.decisionMakers.count": "{{count}} kluczowych osób",
|
|
1817
1913
|
"customers.people.decisionMakers.label": "Decydenci",
|
|
@@ -1,29 +1,70 @@
|
|
|
1
1
|
import type { CalendarItem, CalendarItemStatus, CalendarParticipant } from '../../components/calendar/types'
|
|
2
|
+
import { parseLinkedEntities } from './editorPayload'
|
|
2
3
|
import { participantActorKey } from './participantIdentity'
|
|
3
4
|
import type { ConflictScope } from './preferences'
|
|
4
5
|
|
|
5
6
|
export const EDITOR_DRAFT_CONFLICT_ID = '__draft__'
|
|
6
7
|
|
|
7
8
|
export type FindConflictsOptions = {
|
|
8
|
-
// 'all' (default) flags any
|
|
9
|
-
// where `currentUserId` is an actor of BOTH
|
|
10
|
-
//
|
|
9
|
+
// 'all' (default) flags any overlap that shares a person or a resource. 'mine'
|
|
10
|
+
// narrows the PEOPLE overlaps to those where `currentUserId` is an actor of BOTH
|
|
11
|
+
// events (the user is double-booked); a shared resource is flagged in both
|
|
12
|
+
// scopes. 'mine' with no `currentUserId` degrades to 'all' — it cannot resolve
|
|
13
|
+
// "mine".
|
|
11
14
|
scope?: ConflictScope
|
|
12
15
|
currentUserId?: string | null
|
|
13
16
|
}
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
export type ConflictSubject = { kind: 'person' | 'resource'; key: string; label: string }
|
|
19
|
+
|
|
20
|
+
function participantLabel(participant: CalendarParticipant): string {
|
|
21
|
+
return participant.name?.trim() || participant.email?.trim() || ''
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function bookedResources(item: CalendarItem): Array<{ id: string; label: string }> {
|
|
25
|
+
return parseLinkedEntities((item.raw as { linkedEntities?: unknown }).linkedEntities).resources
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The people and resources two events have in common — what makes an overlap a
|
|
30
|
+
* double-booking. A person is shared through the owner or a participant; a
|
|
31
|
+
* resource through a `resource` link. Labels are empty when the event carries no
|
|
32
|
+
* name for the subject.
|
|
33
|
+
*/
|
|
34
|
+
export function sharedConflictSubjects(first: CalendarItem, second: CalendarItem): ConflictSubject[] {
|
|
35
|
+
const shared = new Map<string, ConflictSubject>()
|
|
36
|
+
const labelOf = (item: CalendarItem, actorKey: string): string => {
|
|
37
|
+
const match = item.participants.find((participant) => participantActorKey(participant) === actorKey)
|
|
38
|
+
return match ? participantLabel(match) : ''
|
|
39
|
+
}
|
|
40
|
+
if (first.ownerUserId && second.ownerUserId && first.ownerUserId === second.ownerUserId) {
|
|
41
|
+
const key = `user:${first.ownerUserId}`
|
|
42
|
+
shared.set(key, { kind: 'person', key, label: labelOf(first, key) || labelOf(second, key) })
|
|
43
|
+
}
|
|
44
|
+
if (first.participants.length > 0 && second.participants.length > 0) {
|
|
45
|
+
const secondActorKeys = new Set(
|
|
46
|
+
second.participants
|
|
47
|
+
.map(participantActorKey)
|
|
48
|
+
.filter((actorKey): actorKey is string => actorKey !== null),
|
|
49
|
+
)
|
|
50
|
+
for (const participant of first.participants) {
|
|
51
|
+
const key = participantActorKey(participant)
|
|
52
|
+
if (key === null || !secondActorKeys.has(key) || shared.has(key)) continue
|
|
53
|
+
shared.set(key, { kind: 'person', key, label: participantLabel(participant) || labelOf(second, key) })
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const firstResources = bookedResources(first)
|
|
57
|
+
if (firstResources.length > 0) {
|
|
58
|
+
const secondResources = new Map(bookedResources(second).map((resource) => [resource.id, resource]))
|
|
59
|
+
for (const resource of firstResources) {
|
|
60
|
+
const other = secondResources.get(resource.id)
|
|
61
|
+
if (!other) continue
|
|
62
|
+
const key = `resource:${resource.id}`
|
|
63
|
+
const label = resource.label !== resource.id ? resource.label : other.label !== other.id ? other.label : ''
|
|
64
|
+
shared.set(key, { kind: 'resource', key, label })
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return [...shared.values()]
|
|
27
68
|
}
|
|
28
69
|
|
|
29
70
|
function isActor(item: CalendarItem, userId: string): boolean {
|
|
@@ -52,12 +93,20 @@ export function findConflicts(items: CalendarItem[], options: FindConflictsOptio
|
|
|
52
93
|
const other = candidates[lookahead]
|
|
53
94
|
if (other.start.getTime() >= currentEnd) break
|
|
54
95
|
if (current.start.getTime() >= other.end.getTime()) continue
|
|
55
|
-
// Org-wide rule first: the two events must share at least one actor
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// 'mine'
|
|
60
|
-
|
|
96
|
+
// Org-wide rule first: the two events must share at least one actor or
|
|
97
|
+
// booked resource.
|
|
98
|
+
const shared = sharedConflictSubjects(current, other)
|
|
99
|
+
if (shared.length === 0) continue
|
|
100
|
+
// 'mine' narrows the people overlaps to the current user's OWN
|
|
101
|
+
// double-bookings — they must be an actor of both events. Because they are
|
|
102
|
+
// then the shared actor, 'mine' is always a strict subset of 'all' (it never
|
|
103
|
+
// invents new conflicts). A double-booked resource is not anyone's in
|
|
104
|
+
// particular — a room cannot host two events — so it is flagged in both scopes.
|
|
105
|
+
if (
|
|
106
|
+
restrictToCurrentUser
|
|
107
|
+
&& !shared.some((subject) => subject.kind === 'resource')
|
|
108
|
+
&& !(isActor(current, currentUserId) && isActor(other, currentUserId))
|
|
109
|
+
) continue
|
|
61
110
|
appendConflict(conflicts, current.id, other.id)
|
|
62
111
|
appendConflict(conflicts, other.id, current.id)
|
|
63
112
|
}
|
|
@@ -74,20 +123,29 @@ export type EditorConflictDraft = {
|
|
|
74
123
|
// exactly as the grid excludes canceled items, so the editor never warns about
|
|
75
124
|
// a record the grid would not badge/ring. Defaults to 'planned'.
|
|
76
125
|
status?: CalendarItemStatus
|
|
126
|
+
// Resources the draft books. Omitted when the type has no resources field.
|
|
127
|
+
resources?: Array<{ id: string; label: string }>
|
|
128
|
+
// Display name of `ownerUserId`, when the editor knows it (a task's assignee).
|
|
129
|
+
// An owner who is not also a participant has no name on either event.
|
|
130
|
+
ownerLabel?: string | null
|
|
77
131
|
}
|
|
78
132
|
|
|
133
|
+
export type EditorConflict = { item: CalendarItem; shared: ConflictSubject[] }
|
|
134
|
+
|
|
79
135
|
/**
|
|
80
136
|
* Resolves the items a calendar editor draft conflicts with, using the SAME
|
|
81
|
-
* `findConflicts` rules as the grid (overlap + shared owner/participant)
|
|
82
|
-
* editor's conflict warning is always consistent with the grid
|
|
83
|
-
*
|
|
137
|
+
* `findConflicts` rules as the grid (overlap + shared owner/participant/resource)
|
|
138
|
+
* so the editor's conflict warning is always consistent with the grid
|
|
139
|
+
* badges/rings. Each conflict names the people and resources that are
|
|
140
|
+
* double-booked. The edited record itself (`excludeId`) is never treated as a
|
|
141
|
+
* conflict.
|
|
84
142
|
*/
|
|
85
|
-
export function
|
|
143
|
+
export function findEditorConflicts(
|
|
86
144
|
draft: EditorConflictDraft,
|
|
87
145
|
others: CalendarItem[],
|
|
88
146
|
excludeId: string | null,
|
|
89
147
|
options: FindConflictsOptions = {},
|
|
90
|
-
):
|
|
148
|
+
): EditorConflict[] {
|
|
91
149
|
const draftStatus: CalendarItemStatus = draft.status ?? 'planned'
|
|
92
150
|
const draftItem: CalendarItem = {
|
|
93
151
|
id: EDITOR_DRAFT_CONFLICT_ID,
|
|
@@ -108,7 +166,12 @@ export function findEditorConflictItems(
|
|
|
108
166
|
color: null,
|
|
109
167
|
isRecurringOccurrence: false,
|
|
110
168
|
updatedAt: null,
|
|
111
|
-
raw: {
|
|
169
|
+
raw: {
|
|
170
|
+
id: EDITOR_DRAFT_CONFLICT_ID,
|
|
171
|
+
interactionType: 'meeting',
|
|
172
|
+
status: draftStatus,
|
|
173
|
+
linkedEntities: (draft.resources ?? []).map((resource) => ({ id: resource.id, type: 'resource', label: resource.label })),
|
|
174
|
+
},
|
|
112
175
|
}
|
|
113
176
|
// Exclude the edited record by its underlying interaction id (`raw.id`), which
|
|
114
177
|
// also drops every expanded occurrence of an edited recurring series (those
|
|
@@ -122,4 +185,20 @@ export function findEditorConflictItems(
|
|
|
122
185
|
return conflictIds
|
|
123
186
|
.map((id) => byId.get(id))
|
|
124
187
|
.filter((item): item is CalendarItem => Boolean(item))
|
|
188
|
+
.map((item) => ({
|
|
189
|
+
item,
|
|
190
|
+
shared: sharedConflictSubjects(draftItem, item).map((subject) =>
|
|
191
|
+
!subject.label && draft.ownerLabel && subject.key === `user:${draft.ownerUserId}`
|
|
192
|
+
? { ...subject, label: draft.ownerLabel }
|
|
193
|
+
: subject),
|
|
194
|
+
}))
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function findEditorConflictItems(
|
|
198
|
+
draft: EditorConflictDraft,
|
|
199
|
+
others: CalendarItem[],
|
|
200
|
+
excludeId: string | null,
|
|
201
|
+
options: FindConflictsOptions = {},
|
|
202
|
+
): CalendarItem[] {
|
|
203
|
+
return findEditorConflicts(draft, others, excludeId, options).map((conflict) => conflict.item)
|
|
125
204
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { CalendarItem } from '../../components/calendar/types'
|
|
2
|
+
import { calendarEventTypeKeySchema, resolveCalendarEventType } from '../../calendar-event-types'
|
|
2
3
|
import { parseRecurrenceRule } from './recurrence'
|
|
4
|
+
import { calendarDayEndInstant, calendarDayStartInstant, calendarInstantToWallTime, calendarWallTimeToInstant, defaultCalendarTimezone, isCalendarTimezone } from './timezone'
|
|
3
5
|
|
|
4
6
|
export type EditorKind = 'meeting' | 'call' | 'email' | 'note' | 'event' | 'task'
|
|
5
7
|
|
|
@@ -41,7 +43,11 @@ const KIND_BY_INTERACTION_TYPE: Record<string, EditorKind> = {
|
|
|
41
43
|
deadline: 'task',
|
|
42
44
|
}
|
|
43
45
|
|
|
46
|
+
/** @deprecated Resolve the effective calendar event type and read `behavior.baseKind`. */
|
|
44
47
|
export function editorKindOfInteractionType(interactionType: string): EditorKind {
|
|
48
|
+
const parsedKey = calendarEventTypeKeySchema.safeParse(interactionType)
|
|
49
|
+
const effectiveKind = parsedKey.success ? resolveCalendarEventType(parsedKey.data)?.behavior.baseKind : undefined
|
|
50
|
+
if (effectiveKind) return effectiveKind
|
|
45
51
|
return KIND_BY_INTERACTION_TYPE[interactionType] ?? 'meeting'
|
|
46
52
|
}
|
|
47
53
|
|
|
@@ -161,6 +167,7 @@ export type EditorFormState = {
|
|
|
161
167
|
allDay: boolean
|
|
162
168
|
date: string
|
|
163
169
|
startTime: string
|
|
170
|
+
timezone?: string
|
|
164
171
|
endDate: string
|
|
165
172
|
endTime: string
|
|
166
173
|
repeatFreq: EditorRepeatFreq
|
|
@@ -245,6 +252,7 @@ export function createDefaultFormState(
|
|
|
245
252
|
}
|
|
246
253
|
return {
|
|
247
254
|
kind: 'meeting',
|
|
255
|
+
timezone: defaultCalendarTimezone(),
|
|
248
256
|
title: '',
|
|
249
257
|
relatedTo: null,
|
|
250
258
|
dealId: null,
|
|
@@ -286,21 +294,23 @@ export function buildRecurrenceRule(state: EditorFormState): string | null {
|
|
|
286
294
|
}
|
|
287
295
|
if (state.repeatEndType === 'count') rule += `;COUNT=${state.repeatCount}`
|
|
288
296
|
if (state.repeatEndType === 'date' && state.repeatUntilDate) {
|
|
289
|
-
|
|
297
|
+
const until = state.timezone ? calendarDayEndInstant(state.repeatUntilDate, state.timezone) : null
|
|
298
|
+
rule += `;UNTIL=${until ? until.toISOString().replace(/[-:]/g, '').replace(/\.\d{3}Z$/, 'Z') : `${state.repeatUntilDate.replace(/-/g, '')}T235959Z`}`
|
|
290
299
|
}
|
|
291
300
|
return rule
|
|
292
301
|
}
|
|
293
302
|
|
|
294
303
|
export function computeDurationMinutes(state: EditorFormState): number | null {
|
|
295
|
-
const start = new Date(`${state.date}T${state.startTime}:00`)
|
|
296
|
-
const end = new Date(`${state.endDate}T${state.endTime}:00`)
|
|
297
|
-
if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) return null
|
|
304
|
+
const start = state.timezone ? calendarWallTimeToInstant(state.date, state.startTime, state.timezone) : new Date(`${state.date}T${state.startTime}:00`)
|
|
305
|
+
const end = state.timezone ? calendarWallTimeToInstant(state.endDate, state.endTime, state.timezone) : new Date(`${state.endDate}T${state.endTime}:00`)
|
|
306
|
+
if (!start || !end || Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) return null
|
|
298
307
|
const minutes = Math.round((end.getTime() - start.getTime()) / 60_000)
|
|
299
308
|
return minutes > 0 ? minutes : null
|
|
300
309
|
}
|
|
301
310
|
|
|
302
311
|
export type BuildPayloadOptions = {
|
|
303
312
|
mode: 'create' | 'edit'
|
|
313
|
+
config?: EditorKindConfig
|
|
304
314
|
id?: string
|
|
305
315
|
/**
|
|
306
316
|
* When the resources module is loaded the payload owns `linkedEntities`
|
|
@@ -317,10 +327,16 @@ export type BuildPayloadOptions = {
|
|
|
317
327
|
}
|
|
318
328
|
|
|
319
329
|
export function buildInteractionPayload(state: EditorFormState, options: BuildPayloadOptions): Record<string, unknown> {
|
|
320
|
-
const config = KIND_CONFIG[state.kind]
|
|
330
|
+
const config = options.config ?? KIND_CONFIG[state.kind]
|
|
321
331
|
const time = state.allDay && config.hasAllDay ? '00:00' : state.startTime
|
|
322
|
-
const
|
|
332
|
+
const instant = state.timezone ? (state.allDay && config.hasAllDay ? calendarDayStartInstant(state.date, state.timezone) : calendarWallTimeToInstant(state.date, time, state.timezone)) : new Date(`${state.date}T${time}:00`)
|
|
333
|
+
if (!instant || !Number.isFinite(instant.getTime())) throw new Error('[internal] Invalid calendar local time')
|
|
334
|
+
const scheduledAt = instant.toISOString()
|
|
323
335
|
const recurrenceRule = config.hasRepeat ? buildRecurrenceRule(state) : null
|
|
336
|
+
const recurrenceEnd = recurrenceRule && state.repeatEndType === 'date' && state.repeatUntilDate
|
|
337
|
+
? state.timezone ? calendarDayEndInstant(state.repeatUntilDate, state.timezone) : new Date(state.repeatUntilDate)
|
|
338
|
+
: null
|
|
339
|
+
if (recurrenceRule && state.repeatEndType === 'date' && state.repeatUntilDate && !recurrenceEnd) throw new Error('[internal] Invalid calendar recurrence day')
|
|
324
340
|
const payload: Record<string, unknown> = {
|
|
325
341
|
...(options.mode === 'edit' && options.id ? { id: options.id } : {}),
|
|
326
342
|
entityId: state.relatedTo?.id ?? null,
|
|
@@ -332,14 +348,12 @@ export function buildInteractionPayload(state: EditorFormState, options: BuildPa
|
|
|
332
348
|
date: state.date,
|
|
333
349
|
time,
|
|
334
350
|
scheduledAt,
|
|
351
|
+
...(state.timezone ? { timezone: state.timezone } : {}),
|
|
335
352
|
durationMinutes: config.hasEnd && !(state.allDay && config.hasAllDay) ? computeDurationMinutes(state) : null,
|
|
336
353
|
allDay: config.hasAllDay ? state.allDay : null,
|
|
337
354
|
location: config.location ? state.location.trim() || null : null,
|
|
338
355
|
recurrenceRule,
|
|
339
|
-
recurrenceEnd:
|
|
340
|
-
recurrenceRule && state.repeatEndType === 'date' && state.repeatUntilDate
|
|
341
|
-
? new Date(state.repeatUntilDate).toISOString()
|
|
342
|
-
: null,
|
|
356
|
+
recurrenceEnd: recurrenceEnd?.toISOString() ?? null,
|
|
343
357
|
participants:
|
|
344
358
|
config.people && config.people !== 'assignee' && state.participants.length > 0
|
|
345
359
|
? state.participants.map((participant) => ({
|
|
@@ -374,10 +388,11 @@ function readUnknownNumber(value: unknown): number | null {
|
|
|
374
388
|
|
|
375
389
|
type ParsedRepeat = Pick<EditorFormState, 'repeatFreq' | 'repeatDays' | 'repeatEndType' | 'repeatCount' | 'repeatUntilDate'>
|
|
376
390
|
|
|
377
|
-
function parseRepeatFromRule(rawRule: unknown, start: Date): ParsedRepeat {
|
|
391
|
+
function parseRepeatFromRule(rawRule: unknown, start: Date, timezone?: string): ParsedRepeat {
|
|
392
|
+
const wallStart = timezone ? new Date(`${calendarInstantToWallTime(start, timezone).date}T12:00:00`) : start
|
|
378
393
|
const fallback: ParsedRepeat = {
|
|
379
394
|
repeatFreq: 'none',
|
|
380
|
-
repeatDays: defaultRepeatDays(
|
|
395
|
+
repeatDays: defaultRepeatDays(wallStart),
|
|
381
396
|
repeatEndType: 'never',
|
|
382
397
|
repeatCount: 8,
|
|
383
398
|
repeatUntilDate: '',
|
|
@@ -386,7 +401,7 @@ function parseRepeatFromRule(rawRule: unknown, start: Date): ParsedRepeat {
|
|
|
386
401
|
if (!ruleText) return fallback
|
|
387
402
|
const parsed = parseRecurrenceRule(ruleText)
|
|
388
403
|
if (!parsed) return fallback
|
|
389
|
-
const repeatDays = defaultRepeatDays(
|
|
404
|
+
const repeatDays = defaultRepeatDays(wallStart)
|
|
390
405
|
if (parsed.byDay) {
|
|
391
406
|
repeatDays.fill(false)
|
|
392
407
|
for (const jsWeekday of parsed.byDay) repeatDays[(jsWeekday + 6) % 7] = true
|
|
@@ -399,7 +414,7 @@ function parseRepeatFromRule(rawRule: unknown, start: Date): ParsedRepeat {
|
|
|
399
414
|
repeatCount = parsed.count
|
|
400
415
|
} else if (parsed.until) {
|
|
401
416
|
repeatEndType = 'date'
|
|
402
|
-
repeatUntilDate = `${parsed.until.getUTCFullYear()}-${padDatePart(parsed.until.getUTCMonth() + 1)}-${padDatePart(parsed.until.getUTCDate())}`
|
|
417
|
+
repeatUntilDate = timezone ? calendarInstantToWallTime(parsed.until, timezone).date : `${parsed.until.getUTCFullYear()}-${padDatePart(parsed.until.getUTCMonth() + 1)}-${padDatePart(parsed.until.getUTCDate())}`
|
|
403
418
|
}
|
|
404
419
|
return {
|
|
405
420
|
repeatFreq: parsed.freq === 'DAILY' ? 'daily' : 'weekly',
|
|
@@ -427,6 +442,9 @@ export function parseItemToFormState(item: CalendarItem): EditorFormState {
|
|
|
427
442
|
const kind = editorKindOfInteractionType(item.interactionType)
|
|
428
443
|
const raw = item.raw as Record<string, unknown>
|
|
429
444
|
const { resources, preservedLinkedEntities } = parseLinkedEntities(raw.linkedEntities)
|
|
445
|
+
const timezone = isCalendarTimezone(raw.timezone) ? raw.timezone : defaultCalendarTimezone()
|
|
446
|
+
const startWall = calendarInstantToWallTime(item.start, timezone)
|
|
447
|
+
const endWall = calendarInstantToWallTime(item.end, timezone)
|
|
430
448
|
return {
|
|
431
449
|
kind,
|
|
432
450
|
title: item.title,
|
|
@@ -434,11 +452,13 @@ export function parseItemToFormState(item: CalendarItem): EditorFormState {
|
|
|
434
452
|
dealId: item.dealId,
|
|
435
453
|
dealLabel: null,
|
|
436
454
|
allDay: item.allDay,
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
455
|
+
timezone,
|
|
456
|
+
date: startWall.date,
|
|
457
|
+
startTime: startWall.time,
|
|
458
|
+
endDate: endWall.date,
|
|
459
|
+
endTime: endWall.time,
|
|
460
|
+
...parseRepeatFromRule(item.raw.recurrenceRule, item.start, isCalendarTimezone(raw.timezone) ? timezone : undefined),
|
|
461
|
+
...(item.raw.recurrenceEnd && isCalendarTimezone(raw.timezone) ? { repeatUntilDate: calendarInstantToWallTime(new Date(item.raw.recurrenceEnd), timezone).date } : {}),
|
|
442
462
|
category: item.interactionType,
|
|
443
463
|
location: item.location ?? '',
|
|
444
464
|
participants: parseParticipants(item),
|