@open-mercato/core 0.6.6-develop.6205.1.109e4b6a84 → 0.6.6-develop.6227.1.2695efff30
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/modules/currencies/backend/currencies/[id]/page.js +33 -8
- package/dist/modules/currencies/backend/currencies/[id]/page.js.map +2 -2
- package/dist/modules/currencies/backend/currencies/page.js +63 -27
- package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
- package/dist/modules/currencies/backend/exchange-rates/page.js +35 -14
- package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
- package/dist/modules/currencies/components/CurrencyFetchingConfig.js +104 -41
- package/dist/modules/currencies/components/CurrencyFetchingConfig.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/roles/[id]/acl.js +57 -8
- package/dist/modules/customer_accounts/api/admin/roles/[id]/acl.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js +5 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js +30 -14
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +2 -2
- package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js +29 -19
- package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js.map +2 -2
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js +166 -64
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js.map +2 -2
- package/dist/modules/customers/components/AddressFormatSettings.js +31 -16
- package/dist/modules/customers/components/AddressFormatSettings.js.map +2 -2
- package/dist/modules/customers/components/DictionarySettings.js +57 -31
- package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
- package/dist/modules/customers/components/PipelineSettings.js +156 -76
- package/dist/modules/customers/components/PipelineSettings.js.map +2 -2
- package/dist/modules/customers/components/calendar/AgendaList.js +10 -4
- package/dist/modules/customers/components/calendar/AgendaList.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js +18 -2
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js.map +2 -2
- package/dist/modules/customers/components/calendar/MonthGrid.js +5 -2
- package/dist/modules/customers/components/calendar/MonthGrid.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/PeopleField.js +6 -0
- package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/RelatedToField.js +2 -0
- package/dist/modules/customers/components/calendar/editor/RelatedToField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/ScheduleSection.js +4 -1
- package/dist/modules/customers/components/calendar/editor/ScheduleSection.js.map +2 -2
- package/dist/modules/customers/lib/calendar/labels.js +33 -0
- package/dist/modules/customers/lib/calendar/labels.js.map +7 -0
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +32 -11
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +2 -2
- package/dist/modules/data_sync/api/mappings/[id]/route.js +59 -0
- package/dist/modules/data_sync/api/mappings/[id]/route.js.map +2 -2
- package/dist/modules/data_sync/api/mappings/route.js +44 -0
- package/dist/modules/data_sync/api/mappings/route.js.map +2 -2
- package/dist/modules/data_sync/api/run.js +31 -0
- package/dist/modules/data_sync/api/run.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/cancel.js +31 -0
- package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/retry.js +31 -0
- package/dist/modules/data_sync/api/runs/[id]/retry.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/[id]/route.js +59 -3
- package/dist/modules/data_sync/api/schedules/[id]/route.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/route.js +33 -4
- package/dist/modules/data_sync/api/schedules/route.js.map +2 -2
- package/dist/modules/directory/api/organization-branding/route.js +54 -2
- package/dist/modules/directory/api/organization-branding/route.js.map +2 -2
- package/dist/modules/directory/backend/directory/branding/page.js +15 -9
- package/dist/modules/directory/backend/directory/branding/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/organizations/page.js +28 -9
- package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/tenants/page.js +29 -13
- package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
- package/dist/modules/entities/api/definitions.batch.js +15 -0
- package/dist/modules/entities/api/definitions.batch.js.map +2 -2
- package/dist/modules/entities/api/definitions.js +26 -0
- package/dist/modules/entities/api/definitions.js.map +2 -2
- package/dist/modules/entities/api/definitions.mutation-guard.js +57 -0
- package/dist/modules/entities/api/definitions.mutation-guard.js.map +7 -0
- package/dist/modules/entities/api/definitions.restore.js +15 -0
- package/dist/modules/entities/api/definitions.restore.js.map +2 -2
- package/dist/modules/entities/api/entities.js +31 -3
- package/dist/modules/entities/api/entities.js.map +2 -2
- package/dist/modules/entities/api/records.js +18 -0
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +28 -10
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
- package/dist/modules/feature_toggles/api/overrides/route.js +38 -1
- package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/emails/[id]/route.js +30 -0
- package/dist/modules/inbox_ops/api/emails/[id]/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/proposals/[id]/accept-all/route.js +30 -0
- package/dist/modules/inbox_ops/api/proposals/[id]/accept-all/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js +31 -0
- package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/proposals/[id]/categorize/route.js +31 -0
- package/dist/modules/inbox_ops/api/proposals/[id]/categorize/route.js.map +2 -2
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsActions.js +68 -39
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsActions.js.map +2 -2
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +14 -4
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +2 -2
- package/dist/modules/payment_gateways/api/cancel/route.js +37 -0
- package/dist/modules/payment_gateways/api/cancel/route.js.map +2 -2
- package/dist/modules/payment_gateways/api/capture/route.js +37 -0
- package/dist/modules/payment_gateways/api/capture/route.js.map +2 -2
- package/dist/modules/payment_gateways/api/guards.js +31 -0
- package/dist/modules/payment_gateways/api/guards.js.map +7 -0
- package/dist/modules/payment_gateways/api/refund/route.js +37 -0
- package/dist/modules/payment_gateways/api/refund/route.js.map +2 -2
- package/dist/modules/payment_gateways/api/sessions/route.js +37 -0
- package/dist/modules/payment_gateways/api/sessions/route.js.map +2 -2
- package/dist/modules/planner/api/availability-date-specific.js +11 -1
- package/dist/modules/planner/api/availability-date-specific.js.map +2 -2
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js +20 -3
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
- package/dist/modules/planner/commands/availability-date-specific.js +16 -0
- package/dist/modules/planner/commands/availability-date-specific.js.map +2 -2
- package/dist/modules/planner/components/AvailabilityRulesEditor.js +109 -40
- package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
- package/dist/modules/query_index/api/purge.js +35 -3
- package/dist/modules/query_index/api/purge.js.map +2 -2
- package/dist/modules/query_index/api/reindex.js +41 -3
- package/dist/modules/query_index/api/reindex.js.map +2 -2
- package/dist/modules/query_index/components/QueryIndexesTable.js +57 -24
- package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
- package/dist/modules/shipping_carriers/api/shipments/route.js +31 -0
- package/dist/modules/shipping_carriers/api/shipments/route.js.map +2 -2
- package/dist/modules/shipping_carriers/api/tracking/refresh/route.js +55 -0
- package/dist/modules/shipping_carriers/api/tracking/refresh/route.js.map +7 -0
- package/dist/modules/shipping_carriers/data/validators.js +6 -1
- package/dist/modules/shipping_carriers/data/validators.js.map +2 -2
- package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.js +28 -8
- package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.js.map +2 -2
- package/dist/modules/shipping_carriers/lib/shipping-service.js +37 -7
- package/dist/modules/shipping_carriers/lib/shipping-service.js.map +2 -2
- package/dist/modules/shipping_carriers/workers/status-poller.js +1 -1
- package/dist/modules/shipping_carriers/workers/status-poller.js.map +2 -2
- package/dist/modules/translations/components/TranslationManager.js +49 -20
- package/dist/modules/translations/components/TranslationManager.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/currencies/backend/currencies/[id]/page.tsx +40 -10
- package/src/modules/currencies/backend/currencies/page.tsx +68 -29
- package/src/modules/currencies/backend/exchange-rates/page.tsx +40 -15
- package/src/modules/currencies/components/CurrencyFetchingConfig.tsx +110 -41
- package/src/modules/customer_accounts/api/admin/roles/[id]/acl.ts +69 -7
- package/src/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.tsx +16 -8
- package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +32 -14
- package/src/modules/customer_accounts/widgets/injection/account-status/widget.client.tsx +32 -20
- package/src/modules/customers/backend/config/customers/pipeline-stages/page.tsx +171 -64
- package/src/modules/customers/components/AddressFormatSettings.tsx +39 -19
- package/src/modules/customers/components/DictionarySettings.tsx +63 -29
- package/src/modules/customers/components/PipelineSettings.tsx +165 -80
- package/src/modules/customers/components/calendar/AgendaList.tsx +13 -8
- package/src/modules/customers/components/calendar/CalendarSettingsModal.tsx +16 -2
- package/src/modules/customers/components/calendar/MonthGrid.tsx +5 -2
- package/src/modules/customers/components/calendar/editor/PeopleField.tsx +6 -0
- package/src/modules/customers/components/calendar/editor/RelatedToField.tsx +2 -0
- package/src/modules/customers/components/calendar/editor/ScheduleSection.tsx +7 -0
- package/src/modules/customers/i18n/de.json +11 -7
- package/src/modules/customers/i18n/en.json +6 -2
- package/src/modules/customers/i18n/es.json +11 -7
- package/src/modules/customers/i18n/pl.json +11 -7
- package/src/modules/customers/lib/calendar/labels.ts +42 -0
- package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +39 -11
- package/src/modules/data_sync/api/mappings/[id]/route.ts +63 -0
- package/src/modules/data_sync/api/mappings/route.ts +48 -0
- package/src/modules/data_sync/api/run.ts +33 -0
- package/src/modules/data_sync/api/runs/[id]/cancel.ts +34 -0
- package/src/modules/data_sync/api/runs/[id]/retry.ts +33 -0
- package/src/modules/data_sync/api/schedules/[id]/route.ts +64 -2
- package/src/modules/data_sync/api/schedules/route.ts +36 -4
- package/src/modules/directory/api/organization-branding/route.ts +61 -0
- package/src/modules/directory/backend/directory/branding/page.tsx +16 -10
- package/src/modules/directory/backend/directory/organizations/page.tsx +35 -8
- package/src/modules/directory/backend/directory/tenants/page.tsx +37 -13
- package/src/modules/entities/api/definitions.batch.ts +17 -0
- package/src/modules/entities/api/definitions.mutation-guard.ts +80 -0
- package/src/modules/entities/api/definitions.restore.ts +17 -0
- package/src/modules/entities/api/definitions.ts +30 -0
- package/src/modules/entities/api/entities.ts +35 -3
- package/src/modules/entities/api/records.ts +20 -0
- package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +33 -10
- package/src/modules/feature_toggles/api/overrides/route.ts +44 -1
- package/src/modules/inbox_ops/api/emails/[id]/route.ts +32 -0
- package/src/modules/inbox_ops/api/proposals/[id]/accept-all/route.ts +32 -0
- package/src/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.ts +33 -0
- package/src/modules/inbox_ops/api/proposals/[id]/categorize/route.ts +33 -0
- package/src/modules/messages/components/message-detail/hooks/useMessageDetailsActions.ts +80 -42
- package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +21 -4
- package/src/modules/payment_gateways/api/cancel/route.ts +39 -0
- package/src/modules/payment_gateways/api/capture/route.ts +39 -0
- package/src/modules/payment_gateways/api/guards.ts +59 -0
- package/src/modules/payment_gateways/api/refund/route.ts +39 -0
- package/src/modules/payment_gateways/api/sessions/route.ts +40 -0
- package/src/modules/planner/api/availability-date-specific.ts +10 -0
- package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +26 -5
- package/src/modules/planner/commands/availability-date-specific.ts +24 -0
- package/src/modules/planner/components/AvailabilityRulesEditor.tsx +122 -41
- package/src/modules/query_index/api/purge.ts +37 -3
- package/src/modules/query_index/api/reindex.ts +43 -3
- package/src/modules/query_index/components/QueryIndexesTable.tsx +66 -24
- package/src/modules/shipping_carriers/api/shipments/route.ts +31 -0
- package/src/modules/shipping_carriers/api/tracking/refresh/route.ts +53 -0
- package/src/modules/shipping_carriers/data/validators.ts +5 -0
- package/src/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.ts +29 -8
- package/src/modules/shipping_carriers/lib/shipping-service.ts +43 -7
- package/src/modules/shipping_carriers/workers/status-poller.ts +1 -1
- package/src/modules/translations/components/TranslationManager.tsx +65 -21
|
@@ -151,11 +151,11 @@
|
|
|
151
151
|
"customers.audit.entityRoles.create": "Create entity role",
|
|
152
152
|
"customers.audit.entityRoles.delete": "Delete entity role",
|
|
153
153
|
"customers.audit.entityRoles.update": "Update entity role",
|
|
154
|
-
"customers.audit.interactions.cancel": "Cancelar
|
|
155
|
-
"customers.audit.interactions.complete": "Completar
|
|
156
|
-
"customers.audit.interactions.create": "Crear
|
|
157
|
-
"customers.audit.interactions.delete": "Eliminar
|
|
158
|
-
"customers.audit.interactions.update": "Actualizar
|
|
154
|
+
"customers.audit.interactions.cancel": "Cancelar interacción",
|
|
155
|
+
"customers.audit.interactions.complete": "Completar interacción",
|
|
156
|
+
"customers.audit.interactions.create": "Crear interacción",
|
|
157
|
+
"customers.audit.interactions.delete": "Eliminar interacción",
|
|
158
|
+
"customers.audit.interactions.update": "Actualizar interacción",
|
|
159
159
|
"customers.audit.labels.assign": "Asignar etiqueta",
|
|
160
160
|
"customers.audit.labels.create": "Crear etiqueta",
|
|
161
161
|
"customers.audit.labels.unassign": "Quitar etiqueta",
|
|
@@ -174,8 +174,10 @@
|
|
|
174
174
|
"customers.audit.todos.link": "Vincular tarea",
|
|
175
175
|
"customers.audit.todos.unlink": "Desvincular tarea",
|
|
176
176
|
"customers.calendar.actions.newEvent": "Nuevo evento",
|
|
177
|
-
"customers.calendar.agenda.
|
|
178
|
-
"customers.calendar.agenda.eventsCount": "{count} eventos",
|
|
177
|
+
"customers.calendar.agenda.eventsCount.few": "{count} eventos",
|
|
178
|
+
"customers.calendar.agenda.eventsCount.many": "{count} eventos",
|
|
179
|
+
"customers.calendar.agenda.eventsCount.one": "{count} evento",
|
|
180
|
+
"customers.calendar.agenda.eventsCount.other": "{count} eventos",
|
|
179
181
|
"customers.calendar.agenda.today": "Hoy",
|
|
180
182
|
"customers.calendar.agenda.tomorrow": "Mañana",
|
|
181
183
|
"customers.calendar.cards.allDay": "Todo el día",
|
|
@@ -230,6 +232,7 @@
|
|
|
230
232
|
"customers.calendar.editor.error": "No se pudo guardar el evento",
|
|
231
233
|
"customers.calendar.editor.location": "Ubicación",
|
|
232
234
|
"customers.calendar.editor.locationPlaceholder": "Añadir una ubicación o enlace…",
|
|
235
|
+
"customers.calendar.editor.multiDayHint": "Evento de varios días · {count} días",
|
|
233
236
|
"customers.calendar.editor.noResults": "Sin resultados",
|
|
234
237
|
"customers.calendar.editor.participants": "Participantes",
|
|
235
238
|
"customers.calendar.editor.phoneLink": "Teléfono / enlace",
|
|
@@ -334,6 +337,7 @@
|
|
|
334
337
|
"customers.calendar.settings.eventCategories": "Categorías de eventos",
|
|
335
338
|
"customers.calendar.settings.eventCategoriesHint": "Tus propias etiquetas de agrupación (p. ej., Reunión de equipo, Llamada de ventas). Se ofrecen al crear un evento.",
|
|
336
339
|
"customers.calendar.settings.max": "(máx. {count})",
|
|
340
|
+
"customers.calendar.settings.removeTag": "Eliminar {tag}",
|
|
337
341
|
"customers.calendar.settings.save": "Guardar cambios",
|
|
338
342
|
"customers.calendar.settings.saved": "Configuración del calendario guardada",
|
|
339
343
|
"customers.calendar.settings.showCrmActivities": "Mostrar actividades de CRM en el calendario",
|
|
@@ -151,11 +151,11 @@
|
|
|
151
151
|
"customers.audit.entityRoles.create": "Create entity role",
|
|
152
152
|
"customers.audit.entityRoles.delete": "Delete entity role",
|
|
153
153
|
"customers.audit.entityRoles.update": "Update entity role",
|
|
154
|
-
"customers.audit.interactions.cancel": "Anuluj
|
|
155
|
-
"customers.audit.interactions.complete": "
|
|
156
|
-
"customers.audit.interactions.create": "
|
|
157
|
-
"customers.audit.interactions.delete": "
|
|
158
|
-
"customers.audit.interactions.update": "Zaktualizuj
|
|
154
|
+
"customers.audit.interactions.cancel": "Anuluj interakcję",
|
|
155
|
+
"customers.audit.interactions.complete": "Zakończ interakcję",
|
|
156
|
+
"customers.audit.interactions.create": "Utwórz interakcję",
|
|
157
|
+
"customers.audit.interactions.delete": "Usuń interakcję",
|
|
158
|
+
"customers.audit.interactions.update": "Zaktualizuj interakcję",
|
|
159
159
|
"customers.audit.labels.assign": "Przypisz etykietę",
|
|
160
160
|
"customers.audit.labels.create": "Utwórz etykietę",
|
|
161
161
|
"customers.audit.labels.unassign": "Usuń przypisanie etykiety",
|
|
@@ -174,8 +174,10 @@
|
|
|
174
174
|
"customers.audit.todos.link": "Powiąż zadanie",
|
|
175
175
|
"customers.audit.todos.unlink": "Odepnij zadanie",
|
|
176
176
|
"customers.calendar.actions.newEvent": "Nowe wydarzenie",
|
|
177
|
-
"customers.calendar.agenda.
|
|
178
|
-
"customers.calendar.agenda.eventsCount": "{count} wydarzeń",
|
|
177
|
+
"customers.calendar.agenda.eventsCount.few": "{count} wydarzenia",
|
|
178
|
+
"customers.calendar.agenda.eventsCount.many": "{count} wydarzeń",
|
|
179
|
+
"customers.calendar.agenda.eventsCount.one": "{count} wydarzenie",
|
|
180
|
+
"customers.calendar.agenda.eventsCount.other": "{count} wydarzeń",
|
|
179
181
|
"customers.calendar.agenda.today": "Dzisiaj",
|
|
180
182
|
"customers.calendar.agenda.tomorrow": "Jutro",
|
|
181
183
|
"customers.calendar.cards.allDay": "Cały dzień",
|
|
@@ -230,6 +232,7 @@
|
|
|
230
232
|
"customers.calendar.editor.error": "Nie udało się zapisać wydarzenia",
|
|
231
233
|
"customers.calendar.editor.location": "Lokalizacja",
|
|
232
234
|
"customers.calendar.editor.locationPlaceholder": "Dodaj lokalizację lub link…",
|
|
235
|
+
"customers.calendar.editor.multiDayHint": "Wydarzenie wielodniowe · {count} dni",
|
|
233
236
|
"customers.calendar.editor.noResults": "Brak wyników",
|
|
234
237
|
"customers.calendar.editor.participants": "Uczestnicy",
|
|
235
238
|
"customers.calendar.editor.phoneLink": "Telefon / link",
|
|
@@ -334,6 +337,7 @@
|
|
|
334
337
|
"customers.calendar.settings.eventCategories": "Kategorie wydarzeń",
|
|
335
338
|
"customers.calendar.settings.eventCategoriesHint": "Twoje własne etykiety grupujące (np. Spotkanie zespołu, Telefon sprzedażowy). Pojawiają się podczas tworzenia wydarzenia.",
|
|
336
339
|
"customers.calendar.settings.max": "(maks. {count})",
|
|
340
|
+
"customers.calendar.settings.removeTag": "Usuń {tag}",
|
|
337
341
|
"customers.calendar.settings.save": "Zapisz zmiany",
|
|
338
342
|
"customers.calendar.settings.saved": "Zapisano ustawienia kalendarza",
|
|
339
343
|
"customers.calendar.settings.showCrmActivities": "Pokaż aktywności CRM w kalendarzu",
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const MS_PER_DAY = 24 * 60 * 60 * 1000
|
|
2
|
+
|
|
3
|
+
export function pluralCategory(locale: string, count: number): Intl.LDMLPluralRule {
|
|
4
|
+
try {
|
|
5
|
+
return new Intl.PluralRules(locale).select(count)
|
|
6
|
+
} catch {
|
|
7
|
+
return count === 1 ? 'one' : 'other'
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function eventDisplayTitle(title: string | null | undefined, fallback: string): string {
|
|
12
|
+
return title && title.trim().length > 0 ? title : fallback
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function composeAccessibleName(
|
|
16
|
+
parts: Array<string | null | undefined>,
|
|
17
|
+
separator = ', ',
|
|
18
|
+
): string {
|
|
19
|
+
return parts
|
|
20
|
+
.map((part) => (typeof part === 'string' ? part.trim() : ''))
|
|
21
|
+
.filter((part) => part.length > 0)
|
|
22
|
+
.join(separator)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function parseDateInput(value: string): number {
|
|
26
|
+
const parsed = new Date(`${value}T00:00:00`)
|
|
27
|
+
return parsed.getTime()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Number of calendar days a scheduled event spans when its end date falls on a
|
|
32
|
+
* later day than its start date. Returns 0 for same-day, end-before-start, or
|
|
33
|
+
* unparseable inputs, so callers can treat 0 as "not multi-day".
|
|
34
|
+
*/
|
|
35
|
+
export function multiDayEventSpan(startDate: string, endDate: string): number {
|
|
36
|
+
const start = parseDateInput(startDate)
|
|
37
|
+
const end = parseDateInput(endDate)
|
|
38
|
+
if (Number.isNaN(start) || Number.isNaN(end)) return 0
|
|
39
|
+
const dayDiff = Math.round((end - start) / MS_PER_DAY)
|
|
40
|
+
if (dayDiff < 1) return 0
|
|
41
|
+
return dayDiff + 1
|
|
42
|
+
}
|
|
@@ -6,9 +6,12 @@ import { Spinner } from '@open-mercato/ui/primitives/spinner'
|
|
|
6
6
|
import { RadioGroup } from '@open-mercato/ui/primitives/radio'
|
|
7
7
|
import { RadioField } from '@open-mercato/ui/primitives/radio-field'
|
|
8
8
|
import { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
|
|
9
|
+
import { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'
|
|
9
10
|
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
10
11
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
11
12
|
|
|
13
|
+
const WIDGET_VISIBILITY_CONTEXT_ID = 'dashboards-widget-visibility:save'
|
|
14
|
+
|
|
12
15
|
type WidgetCatalogItem = {
|
|
13
16
|
id: string
|
|
14
17
|
title: string
|
|
@@ -94,6 +97,13 @@ export const WidgetVisibilityEditor = React.forwardRef<WidgetVisibilityEditorHan
|
|
|
94
97
|
[t],
|
|
95
98
|
)
|
|
96
99
|
const { kind, targetId, tenantId, organizationId, preserveOnTenantChange = false } = props
|
|
100
|
+
const { runMutation, retryLastMutation } = useGuardedMutation<{
|
|
101
|
+
formId: string
|
|
102
|
+
resourceKind: string
|
|
103
|
+
retryLastMutation: () => Promise<boolean>
|
|
104
|
+
}>({
|
|
105
|
+
contextId: WIDGET_VISIBILITY_CONTEXT_ID,
|
|
106
|
+
})
|
|
97
107
|
const [catalog, setCatalog] = React.useState<WidgetCatalogItem[]>([])
|
|
98
108
|
const [loading, setLoading] = React.useState(true)
|
|
99
109
|
const [saving, setSaving] = React.useState(false)
|
|
@@ -243,11 +253,20 @@ export const WidgetVisibilityEditor = React.forwardRef<WidgetVisibilityEditorHan
|
|
|
243
253
|
widgetIds: selected,
|
|
244
254
|
}
|
|
245
255
|
// optimistic-lock-exempt: per-role widget visibility preference
|
|
246
|
-
await
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
256
|
+
await runMutation({
|
|
257
|
+
operation: () =>
|
|
258
|
+
apiCallOrThrow('/api/dashboards/roles/widgets', {
|
|
259
|
+
method: 'PUT',
|
|
260
|
+
headers: { 'content-type': 'application/json' },
|
|
261
|
+
body: JSON.stringify(payload),
|
|
262
|
+
}, { errorMessage: saveError }),
|
|
263
|
+
context: {
|
|
264
|
+
formId: WIDGET_VISIBILITY_CONTEXT_ID,
|
|
265
|
+
resourceKind: 'dashboards.role_widget_visibility',
|
|
266
|
+
retryLastMutation,
|
|
267
|
+
},
|
|
268
|
+
mutationPayload: payload,
|
|
269
|
+
})
|
|
251
270
|
setOriginal(selected)
|
|
252
271
|
setOriginalMode('override')
|
|
253
272
|
setEffective(selected)
|
|
@@ -260,11 +279,20 @@ export const WidgetVisibilityEditor = React.forwardRef<WidgetVisibilityEditorHan
|
|
|
260
279
|
widgetIds: selected,
|
|
261
280
|
}
|
|
262
281
|
// optimistic-lock-exempt: per-user widget visibility preference
|
|
263
|
-
await
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
282
|
+
await runMutation({
|
|
283
|
+
operation: () =>
|
|
284
|
+
apiCallOrThrow('/api/dashboards/users/widgets', {
|
|
285
|
+
method: 'PUT',
|
|
286
|
+
headers: { 'content-type': 'application/json' },
|
|
287
|
+
body: JSON.stringify(payload),
|
|
288
|
+
}, { errorMessage: saveError }),
|
|
289
|
+
context: {
|
|
290
|
+
formId: WIDGET_VISIBILITY_CONTEXT_ID,
|
|
291
|
+
resourceKind: 'dashboards.user_widget_visibility',
|
|
292
|
+
retryLastMutation,
|
|
293
|
+
},
|
|
294
|
+
mutationPayload: payload,
|
|
295
|
+
})
|
|
268
296
|
setOriginal(selected)
|
|
269
297
|
if (mode === 'inherit') {
|
|
270
298
|
const refreshed = await readApiResultOrThrow<UserResponse>(
|
|
@@ -286,7 +314,7 @@ export const WidgetVisibilityEditor = React.forwardRef<WidgetVisibilityEditorHan
|
|
|
286
314
|
} finally {
|
|
287
315
|
setSaving(false)
|
|
288
316
|
}
|
|
289
|
-
}, [catalog.length, dirty, error, kind, loading, mode, organizationId, selected, t, targetId, tenantId])
|
|
317
|
+
}, [catalog.length, dirty, error, kind, loading, mode, organizationId, retryLastMutation, runMutation, selected, t, targetId, tenantId])
|
|
290
318
|
|
|
291
319
|
React.useImperativeHandle(ref, () => ({ save }), [save])
|
|
292
320
|
|
|
@@ -5,6 +5,10 @@ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
|
5
5
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
6
6
|
import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
7
7
|
import { SyncMapping } from '@open-mercato/core/modules/data_sync/data/entities'
|
|
8
|
+
import {
|
|
9
|
+
runCrudMutationGuardAfterSuccess,
|
|
10
|
+
validateCrudMutationGuard,
|
|
11
|
+
} from '@open-mercato/shared/lib/crud/mutation-guard'
|
|
8
12
|
|
|
9
13
|
const idParamsSchema = z.object({ id: z.string().uuid() })
|
|
10
14
|
|
|
@@ -108,9 +112,38 @@ export async function PUT(req: Request, ctx: { params?: Promise<{ id?: string }>
|
|
|
108
112
|
return NextResponse.json({ error: 'Mapping not found' }, { status: 404 })
|
|
109
113
|
}
|
|
110
114
|
|
|
115
|
+
const guardResult = await validateCrudMutationGuard(container, {
|
|
116
|
+
tenantId: scope.tenantId,
|
|
117
|
+
organizationId: scope.organizationId,
|
|
118
|
+
userId: auth.sub,
|
|
119
|
+
resourceKind: 'data_sync.mapping',
|
|
120
|
+
resourceId: mapping.id,
|
|
121
|
+
operation: 'update',
|
|
122
|
+
requestMethod: req.method,
|
|
123
|
+
requestHeaders: req.headers,
|
|
124
|
+
mutationPayload: parsedBody.data,
|
|
125
|
+
})
|
|
126
|
+
if (guardResult && !guardResult.ok) {
|
|
127
|
+
return NextResponse.json(guardResult.body, { status: guardResult.status })
|
|
128
|
+
}
|
|
129
|
+
|
|
111
130
|
mapping.mapping = parsedBody.data.mapping
|
|
112
131
|
await em.flush()
|
|
113
132
|
|
|
133
|
+
if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
134
|
+
await runCrudMutationGuardAfterSuccess(container, {
|
|
135
|
+
tenantId: scope.tenantId,
|
|
136
|
+
organizationId: scope.organizationId,
|
|
137
|
+
userId: auth.sub,
|
|
138
|
+
resourceKind: 'data_sync.mapping',
|
|
139
|
+
resourceId: mapping.id,
|
|
140
|
+
operation: 'update',
|
|
141
|
+
requestMethod: req.method,
|
|
142
|
+
requestHeaders: req.headers,
|
|
143
|
+
metadata: guardResult.metadata ?? null,
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
|
|
114
147
|
return NextResponse.json({
|
|
115
148
|
id: mapping.id,
|
|
116
149
|
integrationId: mapping.integrationId,
|
|
@@ -151,8 +184,38 @@ export async function DELETE(req: Request, ctx: { params?: Promise<{ id?: string
|
|
|
151
184
|
return NextResponse.json({ error: 'Mapping not found' }, { status: 404 })
|
|
152
185
|
}
|
|
153
186
|
|
|
187
|
+
const guardResult = await validateCrudMutationGuard(container, {
|
|
188
|
+
tenantId: scope.tenantId,
|
|
189
|
+
organizationId: scope.organizationId,
|
|
190
|
+
userId: auth.sub,
|
|
191
|
+
resourceKind: 'data_sync.mapping',
|
|
192
|
+
resourceId: mapping.id,
|
|
193
|
+
operation: 'delete',
|
|
194
|
+
requestMethod: req.method,
|
|
195
|
+
requestHeaders: req.headers,
|
|
196
|
+
mutationPayload: null,
|
|
197
|
+
})
|
|
198
|
+
if (guardResult && !guardResult.ok) {
|
|
199
|
+
return NextResponse.json(guardResult.body, { status: guardResult.status })
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const mappingId = mapping.id
|
|
154
203
|
em.remove(mapping)
|
|
155
204
|
await em.flush()
|
|
156
205
|
|
|
206
|
+
if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
207
|
+
await runCrudMutationGuardAfterSuccess(container, {
|
|
208
|
+
tenantId: scope.tenantId,
|
|
209
|
+
organizationId: scope.organizationId,
|
|
210
|
+
userId: auth.sub,
|
|
211
|
+
resourceKind: 'data_sync.mapping',
|
|
212
|
+
resourceId: mappingId,
|
|
213
|
+
operation: 'delete',
|
|
214
|
+
requestMethod: req.method,
|
|
215
|
+
requestHeaders: req.headers,
|
|
216
|
+
metadata: guardResult.metadata ?? null,
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
|
|
157
220
|
return NextResponse.json({ deleted: true })
|
|
158
221
|
}
|
|
@@ -5,6 +5,10 @@ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
|
5
5
|
import type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'
|
|
6
6
|
import { findAndCountWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
7
7
|
import { SyncMapping } from '@open-mercato/core/modules/data_sync/data/entities'
|
|
8
|
+
import {
|
|
9
|
+
runCrudMutationGuardAfterSuccess,
|
|
10
|
+
validateCrudMutationGuard,
|
|
11
|
+
} from '@open-mercato/shared/lib/crud/mutation-guard'
|
|
8
12
|
|
|
9
13
|
const listMappingsQuerySchema = z.object({
|
|
10
14
|
integrationId: z.string().min(1).optional(),
|
|
@@ -115,9 +119,39 @@ export async function POST(req: Request) {
|
|
|
115
119
|
scope,
|
|
116
120
|
)
|
|
117
121
|
|
|
122
|
+
const guardResult = await validateCrudMutationGuard(container, {
|
|
123
|
+
tenantId: scope.tenantId,
|
|
124
|
+
organizationId: scope.organizationId,
|
|
125
|
+
userId: auth.sub,
|
|
126
|
+
resourceKind: 'data_sync.mapping',
|
|
127
|
+
resourceId: existing?.id ?? scope.organizationId,
|
|
128
|
+
operation: existing ? 'update' : 'create',
|
|
129
|
+
requestMethod: req.method,
|
|
130
|
+
requestHeaders: req.headers,
|
|
131
|
+
mutationPayload: parsed.data,
|
|
132
|
+
})
|
|
133
|
+
if (guardResult && !guardResult.ok) {
|
|
134
|
+
return NextResponse.json(guardResult.body, { status: guardResult.status })
|
|
135
|
+
}
|
|
136
|
+
|
|
118
137
|
if (existing) {
|
|
119
138
|
existing.mapping = parsed.data.mapping
|
|
120
139
|
await em.flush()
|
|
140
|
+
|
|
141
|
+
if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
142
|
+
await runCrudMutationGuardAfterSuccess(container, {
|
|
143
|
+
tenantId: scope.tenantId,
|
|
144
|
+
organizationId: scope.organizationId,
|
|
145
|
+
userId: auth.sub,
|
|
146
|
+
resourceKind: 'data_sync.mapping',
|
|
147
|
+
resourceId: existing.id,
|
|
148
|
+
operation: 'update',
|
|
149
|
+
requestMethod: req.method,
|
|
150
|
+
requestHeaders: req.headers,
|
|
151
|
+
metadata: guardResult.metadata ?? null,
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
|
|
121
155
|
return NextResponse.json({
|
|
122
156
|
id: existing.id,
|
|
123
157
|
integrationId: existing.integrationId,
|
|
@@ -135,6 +169,20 @@ export async function POST(req: Request) {
|
|
|
135
169
|
})
|
|
136
170
|
await em.persist(created).flush()
|
|
137
171
|
|
|
172
|
+
if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
173
|
+
await runCrudMutationGuardAfterSuccess(container, {
|
|
174
|
+
tenantId: scope.tenantId,
|
|
175
|
+
organizationId: scope.organizationId,
|
|
176
|
+
userId: auth.sub,
|
|
177
|
+
resourceKind: 'data_sync.mapping',
|
|
178
|
+
resourceId: created.id,
|
|
179
|
+
operation: 'create',
|
|
180
|
+
requestMethod: req.method,
|
|
181
|
+
requestHeaders: req.headers,
|
|
182
|
+
metadata: guardResult.metadata ?? null,
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
|
|
138
186
|
return NextResponse.json({
|
|
139
187
|
id: created.id,
|
|
140
188
|
integrationId: created.integrationId,
|
|
@@ -9,6 +9,10 @@ import type { SyncRunService } from '../lib/sync-run-service'
|
|
|
9
9
|
import { runSyncSchema } from '../data/validators'
|
|
10
10
|
import { startDataSyncRun } from '../lib/start-run'
|
|
11
11
|
import { getDataSyncAdapter } from '../lib/adapter-registry'
|
|
12
|
+
import {
|
|
13
|
+
runCrudMutationGuardAfterSuccess,
|
|
14
|
+
validateCrudMutationGuard,
|
|
15
|
+
} from '@open-mercato/shared/lib/crud/mutation-guard'
|
|
12
16
|
|
|
13
17
|
export const metadata = {
|
|
14
18
|
POST: { requireAuth: true, requireFeatures: ['data_sync.run'] },
|
|
@@ -80,6 +84,21 @@ export async function POST(req: Request) {
|
|
|
80
84
|
return NextResponse.json({ error: 'A sync run is already in progress for this integration and entity direction' }, { status: 409 })
|
|
81
85
|
}
|
|
82
86
|
|
|
87
|
+
const guardResult = await validateCrudMutationGuard(container, {
|
|
88
|
+
tenantId: auth.tenantId,
|
|
89
|
+
organizationId: scope.organizationId,
|
|
90
|
+
userId: auth.sub,
|
|
91
|
+
resourceKind: 'data_sync.run',
|
|
92
|
+
resourceId: parsed.data.integrationId,
|
|
93
|
+
operation: 'custom',
|
|
94
|
+
requestMethod: req.method,
|
|
95
|
+
requestHeaders: req.headers,
|
|
96
|
+
mutationPayload: parsed.data,
|
|
97
|
+
})
|
|
98
|
+
if (guardResult && !guardResult.ok) {
|
|
99
|
+
return NextResponse.json(guardResult.body, { status: guardResult.status })
|
|
100
|
+
}
|
|
101
|
+
|
|
83
102
|
const cursor = parsed.data.fullSync
|
|
84
103
|
? null
|
|
85
104
|
: await syncRunService.resolveCursor(parsed.data.integrationId, parsed.data.entityType, parsed.data.direction, scope)
|
|
@@ -101,6 +120,20 @@ export async function POST(req: Request) {
|
|
|
101
120
|
},
|
|
102
121
|
})
|
|
103
122
|
|
|
123
|
+
if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
124
|
+
await runCrudMutationGuardAfterSuccess(container, {
|
|
125
|
+
tenantId: auth.tenantId,
|
|
126
|
+
organizationId: scope.organizationId,
|
|
127
|
+
userId: auth.sub,
|
|
128
|
+
resourceKind: 'data_sync.run',
|
|
129
|
+
resourceId: run.id,
|
|
130
|
+
operation: 'custom',
|
|
131
|
+
requestMethod: req.method,
|
|
132
|
+
requestHeaders: req.headers,
|
|
133
|
+
metadata: guardResult.metadata ?? null,
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
104
137
|
return NextResponse.json({ id: run.id, progressJobId: progressJob?.id ?? null }, { status: 201 })
|
|
105
138
|
} catch (error) {
|
|
106
139
|
const message = error instanceof Error ? error.message : String(error)
|
|
@@ -6,6 +6,10 @@ import type { IntegrationLogService } from '../../../../integrations/lib/log-ser
|
|
|
6
6
|
import type { IntegrationStateService } from '../../../../integrations/lib/state-service'
|
|
7
7
|
import type { ProgressService } from '../../../../progress/lib/progressService'
|
|
8
8
|
import type { SyncRunService } from '../../../lib/sync-run-service'
|
|
9
|
+
import {
|
|
10
|
+
runCrudMutationGuardAfterSuccess,
|
|
11
|
+
validateCrudMutationGuard,
|
|
12
|
+
} from '@open-mercato/shared/lib/crud/mutation-guard'
|
|
9
13
|
|
|
10
14
|
const paramsSchema = z.object({ id: z.string().uuid() })
|
|
11
15
|
|
|
@@ -48,6 +52,21 @@ export async function POST(req: Request, ctx: { params?: Promise<{ id?: string }
|
|
|
48
52
|
return NextResponse.json({ error: 'Only pending or running runs can be cancelled' }, { status: 409 })
|
|
49
53
|
}
|
|
50
54
|
|
|
55
|
+
const guardResult = await validateCrudMutationGuard(container, {
|
|
56
|
+
tenantId: auth.tenantId,
|
|
57
|
+
organizationId: scope.organizationId,
|
|
58
|
+
userId: auth.sub,
|
|
59
|
+
resourceKind: 'data_sync.run',
|
|
60
|
+
resourceId: run.id,
|
|
61
|
+
operation: 'custom',
|
|
62
|
+
requestMethod: req.method,
|
|
63
|
+
requestHeaders: req.headers,
|
|
64
|
+
mutationPayload: { action: 'cancel' },
|
|
65
|
+
})
|
|
66
|
+
if (guardResult && !guardResult.ok) {
|
|
67
|
+
return NextResponse.json(guardResult.body, { status: guardResult.status })
|
|
68
|
+
}
|
|
69
|
+
|
|
51
70
|
const progressCtx = {
|
|
52
71
|
tenantId: auth.tenantId,
|
|
53
72
|
organizationId: auth.orgId,
|
|
@@ -84,5 +103,20 @@ export async function POST(req: Request, ctx: { params?: Promise<{ id?: string }
|
|
|
84
103
|
summary: 'The sync run was cancelled before completion.',
|
|
85
104
|
},
|
|
86
105
|
}, scope)
|
|
106
|
+
|
|
107
|
+
if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
108
|
+
await runCrudMutationGuardAfterSuccess(container, {
|
|
109
|
+
tenantId: auth.tenantId,
|
|
110
|
+
organizationId: scope.organizationId,
|
|
111
|
+
userId: auth.sub,
|
|
112
|
+
resourceKind: 'data_sync.run',
|
|
113
|
+
resourceId: run.id,
|
|
114
|
+
operation: 'custom',
|
|
115
|
+
requestMethod: req.method,
|
|
116
|
+
requestHeaders: req.headers,
|
|
117
|
+
metadata: guardResult.metadata ?? null,
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
87
121
|
return NextResponse.json({ ok: true })
|
|
88
122
|
}
|
|
@@ -7,6 +7,10 @@ import type { ProgressService } from '../../../../progress/lib/progressService'
|
|
|
7
7
|
import type { SyncRunService } from '../../../lib/sync-run-service'
|
|
8
8
|
import { retrySyncSchema } from '../../../data/validators'
|
|
9
9
|
import { startDataSyncRun } from '../../../lib/start-run'
|
|
10
|
+
import {
|
|
11
|
+
runCrudMutationGuardAfterSuccess,
|
|
12
|
+
validateCrudMutationGuard,
|
|
13
|
+
} from '@open-mercato/shared/lib/crud/mutation-guard'
|
|
10
14
|
|
|
11
15
|
const paramsSchema = z.object({ id: z.string().uuid() })
|
|
12
16
|
|
|
@@ -63,6 +67,21 @@ export async function POST(req: Request, ctx: { params?: Promise<{ id?: string }
|
|
|
63
67
|
return NextResponse.json({ error: 'A sync run is already in progress for this integration and entity direction' }, { status: 409 })
|
|
64
68
|
}
|
|
65
69
|
|
|
70
|
+
const guardResult = await validateCrudMutationGuard(container, {
|
|
71
|
+
tenantId: auth.tenantId,
|
|
72
|
+
organizationId: scope.organizationId,
|
|
73
|
+
userId: auth.sub,
|
|
74
|
+
resourceKind: 'data_sync.run',
|
|
75
|
+
resourceId: previous.id,
|
|
76
|
+
operation: 'custom',
|
|
77
|
+
requestMethod: req.method,
|
|
78
|
+
requestHeaders: req.headers,
|
|
79
|
+
mutationPayload: { action: 'retry', ...parsedBody.data },
|
|
80
|
+
})
|
|
81
|
+
if (guardResult && !guardResult.ok) {
|
|
82
|
+
return NextResponse.json(guardResult.body, { status: guardResult.status })
|
|
83
|
+
}
|
|
84
|
+
|
|
66
85
|
const cursor = parsedBody.data.fromBeginning
|
|
67
86
|
? null
|
|
68
87
|
: previous.cursor ?? await syncRunService.resolveCursor(previous.integrationId, previous.entityType, previous.direction, scope)
|
|
@@ -87,5 +106,19 @@ export async function POST(req: Request, ctx: { params?: Promise<{ id?: string }
|
|
|
87
106
|
},
|
|
88
107
|
})
|
|
89
108
|
|
|
109
|
+
if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
110
|
+
await runCrudMutationGuardAfterSuccess(container, {
|
|
111
|
+
tenantId: auth.tenantId,
|
|
112
|
+
organizationId: scope.organizationId,
|
|
113
|
+
userId: auth.sub,
|
|
114
|
+
resourceKind: 'data_sync.run',
|
|
115
|
+
resourceId: run.id,
|
|
116
|
+
operation: 'custom',
|
|
117
|
+
requestMethod: req.method,
|
|
118
|
+
requestHeaders: req.headers,
|
|
119
|
+
metadata: guardResult.metadata ?? null,
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
90
123
|
return NextResponse.json({ id: run.id, progressJobId: progressJob?.id ?? null }, { status: 201 })
|
|
91
124
|
}
|
|
@@ -6,6 +6,10 @@ import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
|
|
|
6
6
|
import { updateSyncScheduleSchema } from '../../../data/validators'
|
|
7
7
|
import type { SyncScheduleService } from '../../../lib/sync-schedule-service'
|
|
8
8
|
import { serializeSchedule } from '../serialize'
|
|
9
|
+
import {
|
|
10
|
+
runCrudMutationGuardAfterSuccess,
|
|
11
|
+
validateCrudMutationGuard,
|
|
12
|
+
} from '@open-mercato/shared/lib/crud/mutation-guard'
|
|
9
13
|
|
|
10
14
|
const paramsSchema = z.object({
|
|
11
15
|
id: z.string().uuid(),
|
|
@@ -81,6 +85,21 @@ export async function PUT(req: Request, ctx: { params?: Promise<{ id?: string }>
|
|
|
81
85
|
return NextResponse.json({ error: 'Schedule not found' }, { status: 404 })
|
|
82
86
|
}
|
|
83
87
|
|
|
88
|
+
const guardResult = await validateCrudMutationGuard(container, {
|
|
89
|
+
tenantId: auth.tenantId,
|
|
90
|
+
organizationId: scope.organizationId,
|
|
91
|
+
userId: auth.sub,
|
|
92
|
+
resourceKind: 'data_sync.schedule',
|
|
93
|
+
resourceId: current.id,
|
|
94
|
+
operation: 'update',
|
|
95
|
+
requestMethod: req.method,
|
|
96
|
+
requestHeaders: req.headers,
|
|
97
|
+
mutationPayload: parsed.data,
|
|
98
|
+
})
|
|
99
|
+
if (guardResult && !guardResult.ok) {
|
|
100
|
+
return NextResponse.json(guardResult.body, { status: guardResult.status })
|
|
101
|
+
}
|
|
102
|
+
|
|
84
103
|
try {
|
|
85
104
|
const schedule = await scheduleService.saveSchedule({
|
|
86
105
|
id: current.id,
|
|
@@ -93,6 +112,21 @@ export async function PUT(req: Request, ctx: { params?: Promise<{ id?: string }>
|
|
|
93
112
|
fullSync: parsed.data.fullSync ?? current.fullSync,
|
|
94
113
|
isEnabled: parsed.data.isEnabled ?? current.isEnabled,
|
|
95
114
|
}, scope)
|
|
115
|
+
|
|
116
|
+
if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
117
|
+
await runCrudMutationGuardAfterSuccess(container, {
|
|
118
|
+
tenantId: auth.tenantId,
|
|
119
|
+
organizationId: scope.organizationId,
|
|
120
|
+
userId: auth.sub,
|
|
121
|
+
resourceKind: 'data_sync.schedule',
|
|
122
|
+
resourceId: schedule.id,
|
|
123
|
+
operation: 'update',
|
|
124
|
+
requestMethod: req.method,
|
|
125
|
+
requestHeaders: req.headers,
|
|
126
|
+
metadata: guardResult.metadata ?? null,
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
96
130
|
return NextResponse.json(serializeSchedule(schedule))
|
|
97
131
|
} catch (error) {
|
|
98
132
|
const message = error instanceof Error ? error.message : 'Failed to update sync schedule'
|
|
@@ -117,14 +151,42 @@ export async function DELETE(req: Request, ctx: { params?: Promise<{ id?: string
|
|
|
117
151
|
|
|
118
152
|
const container = await createRequestContainer()
|
|
119
153
|
const scheduleService = container.resolve('dataSyncScheduleService') as SyncScheduleService
|
|
120
|
-
const
|
|
121
|
-
|
|
154
|
+
const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }
|
|
155
|
+
|
|
156
|
+
const guardResult = await validateCrudMutationGuard(container, {
|
|
122
157
|
tenantId: auth.tenantId,
|
|
158
|
+
organizationId: scope.organizationId,
|
|
159
|
+
userId: auth.sub,
|
|
160
|
+
resourceKind: 'data_sync.schedule',
|
|
161
|
+
resourceId: parsedParams.data.id,
|
|
162
|
+
operation: 'delete',
|
|
163
|
+
requestMethod: req.method,
|
|
164
|
+
requestHeaders: req.headers,
|
|
165
|
+
mutationPayload: null,
|
|
123
166
|
})
|
|
167
|
+
if (guardResult && !guardResult.ok) {
|
|
168
|
+
return NextResponse.json(guardResult.body, { status: guardResult.status })
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const deleted = await scheduleService.deleteSchedule(parsedParams.data.id, scope)
|
|
124
172
|
|
|
125
173
|
if (!deleted) {
|
|
126
174
|
return NextResponse.json({ error: 'Schedule not found' }, { status: 404 })
|
|
127
175
|
}
|
|
128
176
|
|
|
177
|
+
if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
178
|
+
await runCrudMutationGuardAfterSuccess(container, {
|
|
179
|
+
tenantId: auth.tenantId,
|
|
180
|
+
organizationId: scope.organizationId,
|
|
181
|
+
userId: auth.sub,
|
|
182
|
+
resourceKind: 'data_sync.schedule',
|
|
183
|
+
resourceId: parsedParams.data.id,
|
|
184
|
+
operation: 'delete',
|
|
185
|
+
requestMethod: req.method,
|
|
186
|
+
requestHeaders: req.headers,
|
|
187
|
+
metadata: guardResult.metadata ?? null,
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
|
|
129
191
|
return NextResponse.json({ deleted: true })
|
|
130
192
|
}
|