@open-mercato/core 0.6.8-develop.6983.1.e4f794099e → 0.6.8-develop.6985.1.fb93574faa
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 +2 -2
- package/dist/generated/entities/notification/index.js +6 -0
- package/dist/generated/entities/notification/index.js.map +2 -2
- package/dist/generated/entities/notification_preference/index.js +19 -0
- package/dist/generated/entities/notification_preference/index.js.map +7 -0
- package/dist/generated/entities/notification_type/index.js +21 -0
- package/dist/generated/entities/notification_type/index.js.map +7 -0
- package/dist/generated/entities/notification_type_override/index.js +17 -0
- package/dist/generated/entities/notification_type_override/index.js.map +7 -0
- package/dist/generated/entities/push_notification_delivery/index.js +41 -0
- package/dist/generated/entities/push_notification_delivery/index.js.map +7 -0
- package/dist/generated/entities/user_device/index.js +35 -0
- package/dist/generated/entities/user_device/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +12 -1
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +72 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/api.js +2 -1
- package/dist/helpers/integration/api.js.map +2 -2
- package/dist/helpers/integration/appRoot.js +9 -0
- package/dist/helpers/integration/appRoot.js.map +7 -0
- package/dist/helpers/integration/dbFixtures.js +1 -4
- package/dist/helpers/integration/dbFixtures.js.map +2 -2
- package/dist/helpers/integration/pushFake.js +126 -0
- package/dist/helpers/integration/pushFake.js.map +7 -0
- package/dist/modules/auth/notifications.js +16 -0
- package/dist/modules/auth/notifications.js.map +2 -2
- package/dist/modules/business_rules/notifications.js +2 -0
- package/dist/modules/business_rules/notifications.js.map +2 -2
- package/dist/modules/catalog/notifications.js +2 -0
- package/dist/modules/catalog/notifications.js.map +2 -2
- package/dist/modules/communication_channels/acl.js +9 -0
- package/dist/modules/communication_channels/acl.js.map +2 -2
- package/dist/modules/communication_channels/api/delete/admin/channels/[id]/route.js +133 -0
- package/dist/modules/communication_channels/api/delete/admin/channels/[id]/route.js.map +7 -0
- package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js +6 -2
- package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js.map +2 -2
- package/dist/modules/communication_channels/api/get/channels/[id]/route.js +6 -2
- package/dist/modules/communication_channels/api/get/channels/[id]/route.js.map +2 -2
- package/dist/modules/communication_channels/api/get/channels/route.js +2 -1
- package/dist/modules/communication_channels/api/get/channels/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js +10 -5
- package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js +29 -1
- 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 +157 -0
- package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js.map +7 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.js +8 -1
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +2 -2
- package/dist/modules/communication_channels/commands/admin-delete-channel.js +141 -0
- package/dist/modules/communication_channels/commands/admin-delete-channel.js.map +7 -0
- package/dist/modules/communication_channels/commands/connect-credential-channel.js +25 -8
- package/dist/modules/communication_channels/commands/connect-credential-channel.js.map +2 -2
- package/dist/modules/communication_channels/commands/deliver-outbound-message.js +2 -2
- package/dist/modules/communication_channels/commands/deliver-outbound-message.js.map +2 -2
- package/dist/modules/communication_channels/commands/push-register.js +1 -1
- package/dist/modules/communication_channels/commands/push-register.js.map +2 -2
- package/dist/modules/communication_channels/commands/push-unregister.js +4 -3
- package/dist/modules/communication_channels/commands/push-unregister.js.map +2 -2
- package/dist/modules/communication_channels/data/entities.js +4 -0
- package/dist/modules/communication_channels/data/entities.js.map +2 -2
- package/dist/modules/communication_channels/data/validators.js +2 -1
- package/dist/modules/communication_channels/data/validators.js.map +2 -2
- package/dist/modules/communication_channels/lib/access-control.js +11 -1
- package/dist/modules/communication_channels/lib/access-control.js.map +2 -2
- package/dist/modules/communication_channels/lib/connect-channel.js +20 -12
- package/dist/modules/communication_channels/lib/connect-channel.js.map +2 -2
- package/dist/modules/communication_channels/lib/push-adapter.js +68 -0
- package/dist/modules/communication_channels/lib/push-adapter.js.map +7 -0
- package/dist/modules/communication_channels/lib/push-capabilities.js +31 -0
- package/dist/modules/communication_channels/lib/push-capabilities.js.map +7 -0
- package/dist/modules/communication_channels/lib/push-connect-error.js +20 -0
- package/dist/modules/communication_channels/lib/push-connect-error.js.map +7 -0
- package/dist/modules/communication_channels/lib/push-credential-errors.js +41 -0
- package/dist/modules/communication_channels/lib/push-credential-errors.js.map +7 -0
- package/dist/modules/communication_channels/lib/push-envelope.js +46 -0
- package/dist/modules/communication_channels/lib/push-envelope.js.map +7 -0
- package/dist/modules/communication_channels/lib/refcounted-client-cache.js +64 -0
- package/dist/modules/communication_channels/lib/refcounted-client-cache.js.map +7 -0
- package/dist/modules/communication_channels/migrations/Migration20260703123630_communication_channels.js +13 -0
- package/dist/modules/communication_channels/migrations/Migration20260703123630_communication_channels.js.map +7 -0
- package/dist/modules/communication_channels/notifications.js +4 -0
- package/dist/modules/communication_channels/notifications.js.map +2 -2
- package/dist/modules/communication_channels/setup.js +2 -0
- package/dist/modules/communication_channels/setup.js.map +2 -2
- package/dist/modules/configs/lib/upgrade-actions.js +21 -0
- package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
- package/dist/modules/customer_accounts/notifications.js +12 -0
- package/dist/modules/customer_accounts/notifications.js.map +2 -2
- package/dist/modules/customers/notifications.js +4 -0
- package/dist/modules/customers/notifications.js.map +2 -2
- package/dist/modules/devices/acl.js +11 -0
- package/dist/modules/devices/acl.js.map +7 -0
- package/dist/modules/devices/api/[id]/route.js +167 -0
- package/dist/modules/devices/api/[id]/route.js.map +7 -0
- package/dist/modules/devices/api/admin/devices/[id]/route.js +219 -0
- package/dist/modules/devices/api/admin/devices/[id]/route.js.map +7 -0
- package/dist/modules/devices/api/admin/devices/route.js +140 -0
- package/dist/modules/devices/api/admin/devices/route.js.map +7 -0
- package/dist/modules/devices/api/auth.js +9 -0
- package/dist/modules/devices/api/auth.js.map +7 -0
- package/dist/modules/devices/api/deviceList.js +53 -0
- package/dist/modules/devices/api/deviceList.js.map +7 -0
- package/dist/modules/devices/api/deviceOps.js +118 -0
- package/dist/modules/devices/api/deviceOps.js.map +7 -0
- package/dist/modules/devices/api/openapi.js +19 -0
- package/dist/modules/devices/api/openapi.js.map +7 -0
- package/dist/modules/devices/api/route.js +143 -0
- package/dist/modules/devices/api/route.js.map +7 -0
- package/dist/modules/devices/backend/devices/[id]/page.js +135 -0
- package/dist/modules/devices/backend/devices/[id]/page.js.map +7 -0
- package/dist/modules/devices/backend/devices/[id]/page.meta.js +16 -0
- package/dist/modules/devices/backend/devices/[id]/page.meta.js.map +7 -0
- package/dist/modules/devices/backend/devices/create/page.js +68 -0
- package/dist/modules/devices/backend/devices/create/page.js.map +7 -0
- package/dist/modules/devices/backend/devices/create/page.meta.js +16 -0
- package/dist/modules/devices/backend/devices/create/page.meta.js.map +7 -0
- package/dist/modules/devices/backend/devices/page.js +235 -0
- package/dist/modules/devices/backend/devices/page.js.map +7 -0
- package/dist/modules/devices/backend/devices/page.meta.js +23 -0
- package/dist/modules/devices/backend/devices/page.meta.js.map +7 -0
- package/dist/modules/devices/commands/deactivate.js +107 -0
- package/dist/modules/devices/commands/deactivate.js.map +7 -0
- package/dist/modules/devices/commands/index.js +4 -0
- package/dist/modules/devices/commands/index.js.map +7 -0
- package/dist/modules/devices/commands/register.js +183 -0
- package/dist/modules/devices/commands/register.js.map +7 -0
- package/dist/modules/devices/commands/shared.js +74 -0
- package/dist/modules/devices/commands/shared.js.map +7 -0
- package/dist/modules/devices/commands/update.js +126 -0
- package/dist/modules/devices/commands/update.js.map +7 -0
- package/dist/modules/devices/data/entities.js +83 -0
- package/dist/modules/devices/data/entities.js.map +7 -0
- package/dist/modules/devices/data/validators.js +49 -0
- package/dist/modules/devices/data/validators.js.map +7 -0
- package/dist/modules/devices/di.js +11 -0
- package/dist/modules/devices/di.js.map +7 -0
- package/dist/modules/devices/encryption.js +12 -0
- package/dist/modules/devices/encryption.js.map +7 -0
- package/dist/modules/devices/events.js +19 -0
- package/dist/modules/devices/events.js.map +7 -0
- package/dist/modules/devices/index.js +17 -0
- package/dist/modules/devices/index.js.map +7 -0
- package/dist/modules/devices/lib/operationMetadata.js +20 -0
- package/dist/modules/devices/lib/operationMetadata.js.map +7 -0
- package/dist/modules/devices/migrations/Migration20260602120000.js +15 -0
- package/dist/modules/devices/migrations/Migration20260602120000.js.map +7 -0
- package/dist/modules/devices/migrations/Migration20260630120000.js +15 -0
- package/dist/modules/devices/migrations/Migration20260630120000.js.map +7 -0
- package/dist/modules/devices/migrations/Migration20260701120000.js +13 -0
- package/dist/modules/devices/migrations/Migration20260701120000.js.map +7 -0
- package/dist/modules/devices/migrations/Migration20260721120000.js +15 -0
- package/dist/modules/devices/migrations/Migration20260721120000.js.map +7 -0
- package/dist/modules/devices/setup.js +13 -0
- package/dist/modules/devices/setup.js.map +7 -0
- package/dist/modules/entities/cli.js +8 -3
- package/dist/modules/entities/cli.js.map +2 -2
- package/dist/modules/entities/migrations/Migration20260722120000.js +28 -0
- package/dist/modules/entities/migrations/Migration20260722120000.js.map +7 -0
- package/dist/modules/inbox_ops/notifications.js +2 -0
- package/dist/modules/inbox_ops/notifications.js.map +2 -2
- package/dist/modules/messages/notifications.js +2 -0
- package/dist/modules/messages/notifications.js.map +2 -2
- package/dist/modules/notifications/acl.js +3 -1
- package/dist/modules/notifications/acl.js.map +2 -2
- package/dist/modules/notifications/api/admin/preferences/route.js +167 -0
- package/dist/modules/notifications/api/admin/preferences/route.js.map +7 -0
- package/dist/modules/notifications/api/channels/route.js +54 -0
- package/dist/modules/notifications/api/channels/route.js.map +7 -0
- package/dist/modules/notifications/api/preferences/route.js +126 -0
- package/dist/modules/notifications/api/preferences/route.js.map +7 -0
- package/dist/modules/notifications/api/route.js +8 -1
- package/dist/modules/notifications/api/route.js.map +2 -2
- package/dist/modules/notifications/api/types/[id]/channels/[channel]/route.js +187 -0
- package/dist/modules/notifications/api/types/[id]/channels/[channel]/route.js.map +7 -0
- package/dist/modules/notifications/api/types/route.js +225 -0
- package/dist/modules/notifications/api/types/route.js.map +7 -0
- package/dist/modules/notifications/api/unread-count/route.js +5 -1
- package/dist/modules/notifications/api/unread-count/route.js.map +2 -2
- package/dist/modules/notifications/backend/notifications/user-preferences/page.js +10 -0
- package/dist/modules/notifications/backend/notifications/user-preferences/page.js.map +7 -0
- package/dist/modules/notifications/backend/notifications/user-preferences/page.meta.js +28 -0
- package/dist/modules/notifications/backend/notifications/user-preferences/page.meta.js.map +7 -0
- package/dist/modules/notifications/backend/profile/notification-preferences/page.js +10 -0
- package/dist/modules/notifications/backend/profile/notification-preferences/page.js.map +7 -0
- package/dist/modules/notifications/backend/profile/notification-preferences/page.meta.js +33 -0
- package/dist/modules/notifications/backend/profile/notification-preferences/page.meta.js.map +7 -0
- package/dist/modules/notifications/data/entities.js +128 -1
- package/dist/modules/notifications/data/entities.js.map +2 -2
- package/dist/modules/notifications/data/validators.js +95 -2
- package/dist/modules/notifications/data/validators.js.map +2 -2
- package/dist/modules/notifications/di.js +7 -0
- package/dist/modules/notifications/di.js.map +2 -2
- package/dist/modules/notifications/events.js +17 -0
- package/dist/modules/notifications/events.js.map +2 -2
- package/dist/modules/notifications/frontend/NotificationPreferenceMatrix.js +190 -0
- package/dist/modules/notifications/frontend/NotificationPreferenceMatrix.js.map +7 -0
- package/dist/modules/notifications/frontend/NotificationPreferencesPageClient.js +124 -0
- package/dist/modules/notifications/frontend/NotificationPreferencesPageClient.js.map +7 -0
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +166 -1
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +2 -2
- package/dist/modules/notifications/frontend/NotificationUserPreferencesAdminPageClient.js +172 -0
- package/dist/modules/notifications/frontend/NotificationUserPreferencesAdminPageClient.js.map +7 -0
- package/dist/modules/notifications/generators.js +63 -0
- package/dist/modules/notifications/generators.js.map +7 -0
- package/dist/modules/notifications/lib/delivery-strategies-registry.js +19 -0
- package/dist/modules/notifications/lib/delivery-strategies-registry.js.map +7 -0
- package/dist/modules/notifications/lib/deliveryStrategies.js.map +2 -2
- package/dist/modules/notifications/lib/derive-category.js +8 -0
- package/dist/modules/notifications/lib/derive-category.js.map +7 -0
- package/dist/modules/notifications/lib/notification-channel-registry.js +35 -0
- package/dist/modules/notifications/lib/notification-channel-registry.js.map +7 -0
- package/dist/modules/notifications/lib/notification-type-registry.js +87 -0
- package/dist/modules/notifications/lib/notification-type-registry.js.map +7 -0
- package/dist/modules/notifications/lib/notificationCopy.js +14 -0
- package/dist/modules/notifications/lib/notificationCopy.js.map +7 -0
- package/dist/modules/notifications/lib/notificationFactory.js +4 -1
- package/dist/modules/notifications/lib/notificationFactory.js.map +2 -2
- package/dist/modules/notifications/lib/notificationMapper.js +2 -0
- package/dist/modules/notifications/lib/notificationMapper.js.map +2 -2
- package/dist/modules/notifications/lib/notificationPreferenceService.js +83 -0
- package/dist/modules/notifications/lib/notificationPreferenceService.js.map +7 -0
- package/dist/modules/notifications/lib/notificationService.js +83 -28
- package/dist/modules/notifications/lib/notificationService.js.map +2 -2
- package/dist/modules/notifications/lib/notificationVisibility.js +24 -0
- package/dist/modules/notifications/lib/notificationVisibility.js.map +7 -0
- package/dist/modules/notifications/lib/routeHelpers.js +11 -1
- package/dist/modules/notifications/lib/routeHelpers.js.map +2 -2
- package/dist/modules/notifications/lib/shouldDeliver.js +46 -0
- package/dist/modules/notifications/lib/shouldDeliver.js.map +7 -0
- package/dist/modules/notifications/lib/strategies/email-delivery-strategy.js +54 -0
- package/dist/modules/notifications/lib/strategies/email-delivery-strategy.js.map +7 -0
- package/dist/modules/notifications/lib/strategies/in-app-delivery-strategy.js +12 -0
- package/dist/modules/notifications/lib/strategies/in-app-delivery-strategy.js.map +7 -0
- package/dist/modules/notifications/lib/typeCatalogue.js +71 -0
- package/dist/modules/notifications/lib/typeCatalogue.js.map +7 -0
- package/dist/modules/notifications/lib/typeChannelSettings.js +12 -0
- package/dist/modules/notifications/lib/typeChannelSettings.js.map +7 -0
- package/dist/modules/notifications/lib/typeOverrides.js +19 -0
- package/dist/modules/notifications/lib/typeOverrides.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260625122947_notifications.js +14 -0
- package/dist/modules/notifications/migrations/Migration20260625122947_notifications.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260626120500_notifications_push_payload.js +15 -0
- package/dist/modules/notifications/migrations/Migration20260626120500_notifications_push_payload.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260626130000_notifications_non_opt_out.js +13 -0
- package/dist/modules/notifications/migrations/Migration20260626130000_notifications_non_opt_out.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260626140000_notifications_type_category_silent.js +15 -0
- package/dist/modules/notifications/migrations/Migration20260626140000_notifications_type_category_silent.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260706120000_notifications_channels.js +13 -0
- package/dist/modules/notifications/migrations/Migration20260706120000_notifications_channels.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260716123121_notifications.js +14 -0
- package/dist/modules/notifications/migrations/Migration20260716123121_notifications.js.map +7 -0
- package/dist/modules/notifications/notification-channels.js +26 -0
- package/dist/modules/notifications/notification-channels.js.map +7 -0
- package/dist/modules/notifications/notifications.delivery-strategies.js +12 -0
- package/dist/modules/notifications/notifications.delivery-strategies.js.map +7 -0
- package/dist/modules/notifications/setup.js +16 -1
- package/dist/modules/notifications/setup.js.map +2 -2
- package/dist/modules/notifications/subscribers/deliver-notification.js +66 -57
- package/dist/modules/notifications/subscribers/deliver-notification.js.map +2 -2
- package/dist/modules/notifications/subscribers/sync-notification-types.js +16 -0
- package/dist/modules/notifications/subscribers/sync-notification-types.js.map +7 -0
- package/dist/modules/notifications/widgets/injection/profile-preferences-menu/widget.js +23 -0
- package/dist/modules/notifications/widgets/injection/profile-preferences-menu/widget.js.map +7 -0
- package/dist/modules/notifications/widgets/injection-table.js +14 -0
- package/dist/modules/notifications/widgets/injection-table.js.map +7 -0
- package/dist/modules/push_notifications/acl.js +13 -0
- package/dist/modules/push_notifications/acl.js.map +7 -0
- package/dist/modules/push_notifications/api/custom-send/route.js +141 -0
- package/dist/modules/push_notifications/api/custom-send/route.js.map +7 -0
- package/dist/modules/push_notifications/api/deliveries/[id]/route.js +110 -0
- package/dist/modules/push_notifications/api/deliveries/[id]/route.js.map +7 -0
- package/dist/modules/push_notifications/api/deliveries/route.js +56 -0
- package/dist/modules/push_notifications/api/deliveries/route.js.map +7 -0
- package/dist/modules/push_notifications/api/openapi.js +19 -0
- package/dist/modules/push_notifications/api/openapi.js.map +7 -0
- package/dist/modules/push_notifications/backend/push_notifications/[id]/page.js +103 -0
- package/dist/modules/push_notifications/backend/push_notifications/[id]/page.js.map +7 -0
- package/dist/modules/push_notifications/backend/push_notifications/[id]/page.meta.js +13 -0
- package/dist/modules/push_notifications/backend/push_notifications/[id]/page.meta.js.map +7 -0
- package/dist/modules/push_notifications/backend/push_notifications/page.js +213 -0
- package/dist/modules/push_notifications/backend/push_notifications/page.js.map +7 -0
- package/dist/modules/push_notifications/backend/push_notifications/page.meta.js +23 -0
- package/dist/modules/push_notifications/backend/push_notifications/page.meta.js.map +7 -0
- package/dist/modules/push_notifications/backend/push_notifications/send/page.js +245 -0
- package/dist/modules/push_notifications/backend/push_notifications/send/page.js.map +7 -0
- package/dist/modules/push_notifications/backend/push_notifications/send/page.meta.js +26 -0
- package/dist/modules/push_notifications/backend/push_notifications/send/page.meta.js.map +7 -0
- package/dist/modules/push_notifications/data/entities.js +92 -0
- package/dist/modules/push_notifications/data/entities.js.map +7 -0
- package/dist/modules/push_notifications/data/extensions.js +23 -0
- package/dist/modules/push_notifications/data/extensions.js.map +7 -0
- package/dist/modules/push_notifications/data/validators.js +89 -0
- package/dist/modules/push_notifications/data/validators.js.map +7 -0
- package/dist/modules/push_notifications/di.js +17 -0
- package/dist/modules/push_notifications/di.js.map +7 -0
- package/dist/modules/push_notifications/events.js +24 -0
- package/dist/modules/push_notifications/events.js.map +7 -0
- package/dist/modules/push_notifications/index.js +16 -0
- package/dist/modules/push_notifications/index.js.map +7 -0
- package/dist/modules/push_notifications/lib/fake-provider-recorder.js +80 -0
- package/dist/modules/push_notifications/lib/fake-provider-recorder.js.map +7 -0
- package/dist/modules/push_notifications/lib/push-delivery-strategy.js +54 -0
- package/dist/modules/push_notifications/lib/push-delivery-strategy.js.map +7 -0
- package/dist/modules/push_notifications/lib/push-delivery.js +297 -0
- package/dist/modules/push_notifications/lib/push-delivery.js.map +7 -0
- package/dist/modules/push_notifications/lib/push-fanout.js +138 -0
- package/dist/modules/push_notifications/lib/push-fanout.js.map +7 -0
- package/dist/modules/push_notifications/lib/push-health.js +32 -0
- package/dist/modules/push_notifications/lib/push-health.js.map +7 -0
- package/dist/modules/push_notifications/lib/push-reaper.js +87 -0
- package/dist/modules/push_notifications/lib/push-reaper.js.map +7 -0
- package/dist/modules/push_notifications/lib/push-receipt-reaper.js +136 -0
- package/dist/modules/push_notifications/lib/push-receipt-reaper.js.map +7 -0
- package/dist/modules/push_notifications/lib/push-stub-adapter.js +63 -0
- package/dist/modules/push_notifications/lib/push-stub-adapter.js.map +7 -0
- package/dist/modules/push_notifications/lib/queue.js +53 -0
- package/dist/modules/push_notifications/lib/queue.js.map +7 -0
- package/dist/modules/push_notifications/lib/reclaim-window.js +13 -0
- package/dist/modules/push_notifications/lib/reclaim-window.js.map +7 -0
- package/dist/modules/push_notifications/lib/send-custom-push.js +48 -0
- package/dist/modules/push_notifications/lib/send-custom-push.js.map +7 -0
- package/dist/modules/push_notifications/migrations/Migration20260625150049_push_notifications.js +16 -0
- package/dist/modules/push_notifications/migrations/Migration20260625150049_push_notifications.js.map +7 -0
- package/dist/modules/push_notifications/migrations/Migration20260626120000_push_notifications_silent.js +13 -0
- package/dist/modules/push_notifications/migrations/Migration20260626120000_push_notifications_silent.js.map +7 -0
- package/dist/modules/push_notifications/notifications.delivery-strategies.js +8 -0
- package/dist/modules/push_notifications/notifications.delivery-strategies.js.map +7 -0
- package/dist/modules/push_notifications/notifications.js +40 -0
- package/dist/modules/push_notifications/notifications.js.map +7 -0
- package/dist/modules/push_notifications/setup.js +52 -0
- package/dist/modules/push_notifications/setup.js.map +7 -0
- package/dist/modules/push_notifications/workers/reclaim-stuck.worker.js +57 -0
- package/dist/modules/push_notifications/workers/reclaim-stuck.worker.js.map +7 -0
- package/dist/modules/push_notifications/workers/send-push.worker.js +27 -0
- package/dist/modules/push_notifications/workers/send-push.worker.js.map +7 -0
- package/dist/modules/sales/notifications.js +8 -0
- package/dist/modules/sales/notifications.js.map +2 -2
- package/dist/modules/staff/notifications.js +6 -0
- package/dist/modules/staff/notifications.js.map +2 -2
- package/dist/modules/workflows/notifications.js +2 -0
- package/dist/modules/workflows/notifications.js.map +2 -2
- package/generated/entities/notification/index.ts +3 -0
- package/generated/entities/notification_preference/index.ts +8 -0
- package/generated/entities/notification_type/index.ts +9 -0
- package/generated/entities/notification_type_override/index.ts +7 -0
- package/generated/entities/push_notification_delivery/index.ts +19 -0
- package/generated/entities/user_device/index.ts +16 -0
- package/generated/entities.ids.generated.ts +12 -1
- package/generated/entity-fields-registry.ts +72 -0
- package/package.json +7 -7
- package/src/helpers/integration/api.ts +2 -1
- package/src/helpers/integration/appRoot.ts +13 -0
- package/src/helpers/integration/dbFixtures.ts +1 -5
- package/src/helpers/integration/pushFake.ts +219 -0
- package/src/modules/auth/i18n/de.json +1 -0
- package/src/modules/auth/i18n/en.json +1 -0
- package/src/modules/auth/i18n/es.json +1 -0
- package/src/modules/auth/i18n/ko.json +1 -0
- package/src/modules/auth/i18n/pl.json +1 -0
- package/src/modules/auth/notifications.ts +16 -0
- package/src/modules/business_rules/i18n/de.json +2 -1
- package/src/modules/business_rules/i18n/en.json +2 -1
- package/src/modules/business_rules/i18n/es.json +2 -1
- package/src/modules/business_rules/i18n/ko.json +2 -1
- package/src/modules/business_rules/i18n/pl.json +2 -1
- package/src/modules/business_rules/notifications.ts +2 -0
- package/src/modules/catalog/i18n/de.json +2 -1
- package/src/modules/catalog/i18n/en.json +2 -1
- package/src/modules/catalog/i18n/es.json +2 -1
- package/src/modules/catalog/i18n/ko.json +2 -1
- package/src/modules/catalog/i18n/pl.json +2 -1
- package/src/modules/catalog/notifications.ts +2 -0
- package/src/modules/communication_channels/acl.ts +9 -0
- package/src/modules/communication_channels/api/delete/admin/channels/[id]/route.ts +168 -0
- package/src/modules/communication_channels/api/get/channels/[id]/health/route.ts +6 -2
- package/src/modules/communication_channels/api/get/channels/[id]/route.ts +6 -2
- package/src/modules/communication_channels/api/get/channels/route.ts +2 -1
- package/src/modules/communication_channels/api/post/channels/[id]/test-send/route.ts +16 -5
- package/src/modules/communication_channels/api/post/channels/connect/credentials/route.ts +37 -1
- package/src/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.ts +175 -0
- package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +11 -1
- package/src/modules/communication_channels/commands/admin-delete-channel.ts +203 -0
- package/src/modules/communication_channels/commands/connect-credential-channel.ts +63 -15
- package/src/modules/communication_channels/commands/deliver-outbound-message.ts +10 -2
- package/src/modules/communication_channels/commands/push-register.ts +7 -1
- package/src/modules/communication_channels/commands/push-unregister.ts +14 -3
- package/src/modules/communication_channels/data/entities.ts +10 -0
- package/src/modules/communication_channels/data/validators.ts +1 -0
- package/src/modules/communication_channels/i18n/de.json +35 -1
- package/src/modules/communication_channels/i18n/en.json +35 -1
- package/src/modules/communication_channels/i18n/es.json +35 -1
- package/src/modules/communication_channels/i18n/ko.json +35 -1
- package/src/modules/communication_channels/i18n/pl.json +35 -1
- package/src/modules/communication_channels/lib/access-control.ts +42 -0
- package/src/modules/communication_channels/lib/adapter.ts +25 -0
- package/src/modules/communication_channels/lib/connect-channel.ts +61 -26
- package/src/modules/communication_channels/lib/push-adapter.ts +139 -0
- package/src/modules/communication_channels/lib/push-capabilities.ts +42 -0
- package/src/modules/communication_channels/lib/push-connect-error.ts +52 -0
- package/src/modules/communication_channels/lib/push-credential-errors.ts +58 -0
- package/src/modules/communication_channels/lib/push-envelope.ts +104 -0
- package/src/modules/communication_channels/lib/refcounted-client-cache.ts +123 -0
- package/src/modules/communication_channels/migrations/.snapshot-open-mercato.json +9 -0
- package/src/modules/communication_channels/migrations/Migration20260703123630_communication_channels.ts +20 -0
- package/src/modules/communication_channels/notifications.ts +4 -0
- package/src/modules/communication_channels/setup.ts +2 -0
- package/src/modules/configs/i18n/de.json +5 -1
- package/src/modules/configs/i18n/en.json +5 -1
- package/src/modules/configs/i18n/es.json +5 -1
- package/src/modules/configs/i18n/ko.json +5 -1
- package/src/modules/configs/i18n/pl.json +5 -1
- package/src/modules/configs/lib/upgrade-actions.ts +21 -0
- package/src/modules/customer_accounts/i18n/de.json +2 -1
- package/src/modules/customer_accounts/i18n/en.json +2 -1
- package/src/modules/customer_accounts/i18n/es.json +2 -1
- package/src/modules/customer_accounts/i18n/ko.json +2 -1
- package/src/modules/customer_accounts/i18n/pl.json +2 -1
- package/src/modules/customer_accounts/notifications.ts +12 -0
- package/src/modules/customers/i18n/de.json +2 -1
- package/src/modules/customers/i18n/en.json +2 -1
- package/src/modules/customers/i18n/es.json +2 -1
- package/src/modules/customers/i18n/ko.json +2 -1
- package/src/modules/customers/i18n/pl.json +2 -1
- package/src/modules/customers/notifications.ts +4 -0
- package/src/modules/devices/AGENTS.md +101 -0
- package/src/modules/devices/acl.ts +7 -0
- package/src/modules/devices/api/[id]/route.ts +180 -0
- package/src/modules/devices/api/admin/devices/[id]/route.ts +235 -0
- package/src/modules/devices/api/admin/devices/route.ts +160 -0
- package/src/modules/devices/api/auth.ts +13 -0
- package/src/modules/devices/api/deviceList.ts +54 -0
- package/src/modules/devices/api/deviceOps.ts +186 -0
- package/src/modules/devices/api/openapi.ts +23 -0
- package/src/modules/devices/api/route.ts +165 -0
- package/src/modules/devices/backend/devices/[id]/page.meta.ts +12 -0
- package/src/modules/devices/backend/devices/[id]/page.tsx +164 -0
- package/src/modules/devices/backend/devices/create/page.meta.ts +12 -0
- package/src/modules/devices/backend/devices/create/page.tsx +81 -0
- package/src/modules/devices/backend/devices/page.meta.ts +21 -0
- package/src/modules/devices/backend/devices/page.tsx +259 -0
- package/src/modules/devices/commands/deactivate.ts +126 -0
- package/src/modules/devices/commands/index.ts +3 -0
- package/src/modules/devices/commands/register.ts +199 -0
- package/src/modules/devices/commands/shared.ts +119 -0
- package/src/modules/devices/commands/update.ts +142 -0
- package/src/modules/devices/data/entities.ts +79 -0
- package/src/modules/devices/data/validators.ts +65 -0
- package/src/modules/devices/di.ts +9 -0
- package/src/modules/devices/encryption.ts +15 -0
- package/src/modules/devices/events.ts +16 -0
- package/src/modules/devices/i18n/de.json +45 -0
- package/src/modules/devices/i18n/en.json +45 -0
- package/src/modules/devices/i18n/es.json +45 -0
- package/src/modules/devices/i18n/ko.json +45 -0
- package/src/modules/devices/i18n/pl.json +45 -0
- package/src/modules/devices/index.ts +15 -0
- package/src/modules/devices/lib/operationMetadata.ts +35 -0
- package/src/modules/devices/migrations/.snapshot-open-mercato.json +200 -0
- package/src/modules/devices/migrations/Migration20260602120000.ts +15 -0
- package/src/modules/devices/migrations/Migration20260630120000.ts +20 -0
- package/src/modules/devices/migrations/Migration20260701120000.ts +13 -0
- package/src/modules/devices/migrations/Migration20260721120000.ts +18 -0
- package/src/modules/devices/setup.ts +11 -0
- package/src/modules/entities/cli.ts +16 -2
- package/src/modules/entities/migrations/Migration20260722120000.ts +38 -0
- package/src/modules/inbox_ops/i18n/de.json +2 -1
- package/src/modules/inbox_ops/i18n/en.json +2 -1
- package/src/modules/inbox_ops/i18n/es.json +2 -1
- package/src/modules/inbox_ops/i18n/ko.json +2 -1
- package/src/modules/inbox_ops/i18n/pl.json +2 -1
- package/src/modules/inbox_ops/notifications.ts +2 -0
- package/src/modules/messages/i18n/de.json +2 -1
- package/src/modules/messages/i18n/en.json +2 -1
- package/src/modules/messages/i18n/es.json +2 -1
- package/src/modules/messages/i18n/ko.json +2 -1
- package/src/modules/messages/i18n/pl.json +2 -1
- package/src/modules/messages/notifications.ts +2 -0
- package/src/modules/notifications/AGENTS.md +54 -0
- package/src/modules/notifications/acl.ts +2 -0
- package/src/modules/notifications/api/admin/preferences/route.ts +193 -0
- package/src/modules/notifications/api/channels/route.ts +54 -0
- package/src/modules/notifications/api/preferences/route.ts +131 -0
- package/src/modules/notifications/api/route.ts +8 -1
- package/src/modules/notifications/api/types/[id]/channels/[channel]/route.ts +226 -0
- package/src/modules/notifications/api/types/route.ts +239 -0
- package/src/modules/notifications/api/unread-count/route.ts +5 -1
- package/src/modules/notifications/backend/notifications/user-preferences/page.meta.ts +26 -0
- package/src/modules/notifications/backend/notifications/user-preferences/page.tsx +12 -0
- package/src/modules/notifications/backend/profile/notification-preferences/page.meta.ts +31 -0
- package/src/modules/notifications/backend/profile/notification-preferences/page.tsx +12 -0
- package/src/modules/notifications/data/entities.ts +144 -0
- package/src/modules/notifications/data/validators.ts +116 -0
- package/src/modules/notifications/di.ts +7 -0
- package/src/modules/notifications/events.ts +23 -0
- package/src/modules/notifications/frontend/NotificationPreferenceMatrix.tsx +287 -0
- package/src/modules/notifications/frontend/NotificationPreferencesPageClient.tsx +155 -0
- package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +273 -2
- package/src/modules/notifications/frontend/NotificationUserPreferencesAdminPageClient.tsx +214 -0
- package/src/modules/notifications/generators.ts +82 -0
- package/src/modules/notifications/i18n/de.json +43 -0
- package/src/modules/notifications/i18n/en.json +43 -0
- package/src/modules/notifications/i18n/es.json +43 -0
- package/src/modules/notifications/i18n/ko.json +43 -0
- package/src/modules/notifications/i18n/pl.json +44 -1
- package/src/modules/notifications/lib/delivery-strategies-registry.ts +30 -0
- package/src/modules/notifications/lib/deliveryStrategies.ts +40 -4
- package/src/modules/notifications/lib/derive-category.ts +15 -0
- package/src/modules/notifications/lib/notification-channel-registry.ts +59 -0
- package/src/modules/notifications/lib/notification-type-registry.ts +182 -0
- package/src/modules/notifications/lib/notificationCopy.ts +34 -0
- package/src/modules/notifications/lib/notificationFactory.ts +5 -1
- package/src/modules/notifications/lib/notificationMapper.ts +2 -0
- package/src/modules/notifications/lib/notificationPreferenceService.ts +138 -0
- package/src/modules/notifications/lib/notificationService.ts +129 -25
- package/src/modules/notifications/lib/notificationVisibility.ts +48 -0
- package/src/modules/notifications/lib/routeHelpers.ts +14 -1
- package/src/modules/notifications/lib/shouldDeliver.ts +112 -0
- package/src/modules/notifications/lib/strategies/email-delivery-strategy.ts +63 -0
- package/src/modules/notifications/lib/strategies/in-app-delivery-strategy.ts +21 -0
- package/src/modules/notifications/lib/typeCatalogue.ts +141 -0
- package/src/modules/notifications/lib/typeChannelSettings.ts +29 -0
- package/src/modules/notifications/lib/typeOverrides.ts +46 -0
- package/src/modules/notifications/migrations/.snapshot-open-mercato.json +874 -100
- package/src/modules/notifications/migrations/Migration20260625122947_notifications.ts +14 -0
- package/src/modules/notifications/migrations/Migration20260626120500_notifications_push_payload.ts +15 -0
- package/src/modules/notifications/migrations/Migration20260626130000_notifications_non_opt_out.ts +13 -0
- package/src/modules/notifications/migrations/Migration20260626140000_notifications_type_category_silent.ts +15 -0
- package/src/modules/notifications/migrations/Migration20260706120000_notifications_channels.ts +13 -0
- package/src/modules/notifications/migrations/Migration20260716123121_notifications.ts +14 -0
- package/src/modules/notifications/notification-channels.ts +30 -0
- package/src/modules/notifications/notifications.delivery-strategies.ts +15 -0
- package/src/modules/notifications/setup.ts +23 -1
- package/src/modules/notifications/subscribers/deliver-notification.ts +105 -71
- package/src/modules/notifications/subscribers/sync-notification-types.ts +18 -0
- package/src/modules/notifications/widgets/injection/profile-preferences-menu/widget.ts +35 -0
- package/src/modules/notifications/widgets/injection-table.ts +18 -0
- package/src/modules/push_notifications/AGENTS.md +92 -0
- package/src/modules/push_notifications/acl.ts +9 -0
- package/src/modules/push_notifications/api/custom-send/route.ts +175 -0
- package/src/modules/push_notifications/api/deliveries/[id]/route.ts +122 -0
- package/src/modules/push_notifications/api/deliveries/route.ts +58 -0
- package/src/modules/push_notifications/api/openapi.ts +22 -0
- package/src/modules/push_notifications/backend/push_notifications/[id]/page.meta.ts +9 -0
- package/src/modules/push_notifications/backend/push_notifications/[id]/page.tsx +149 -0
- package/src/modules/push_notifications/backend/push_notifications/page.meta.ts +21 -0
- package/src/modules/push_notifications/backend/push_notifications/page.tsx +239 -0
- package/src/modules/push_notifications/backend/push_notifications/send/page.meta.ts +24 -0
- package/src/modules/push_notifications/backend/push_notifications/send/page.tsx +327 -0
- package/src/modules/push_notifications/data/entities.ts +111 -0
- package/src/modules/push_notifications/data/extensions.ts +29 -0
- package/src/modules/push_notifications/data/validators.ts +102 -0
- package/src/modules/push_notifications/di.ts +20 -0
- package/src/modules/push_notifications/events.ts +21 -0
- package/src/modules/push_notifications/i18n/de.json +79 -0
- package/src/modules/push_notifications/i18n/en.json +79 -0
- package/src/modules/push_notifications/i18n/es.json +79 -0
- package/src/modules/push_notifications/i18n/ko.json +79 -0
- package/src/modules/push_notifications/i18n/pl.json +79 -0
- package/src/modules/push_notifications/index.ts +15 -0
- package/src/modules/push_notifications/lib/fake-provider-recorder.ts +154 -0
- package/src/modules/push_notifications/lib/push-delivery-strategy.ts +90 -0
- package/src/modules/push_notifications/lib/push-delivery.ts +455 -0
- package/src/modules/push_notifications/lib/push-fanout.ts +237 -0
- package/src/modules/push_notifications/lib/push-health.ts +47 -0
- package/src/modules/push_notifications/lib/push-reaper.ts +140 -0
- package/src/modules/push_notifications/lib/push-receipt-reaper.ts +210 -0
- package/src/modules/push_notifications/lib/push-stub-adapter.ts +80 -0
- package/src/modules/push_notifications/lib/queue.ts +74 -0
- package/src/modules/push_notifications/lib/reclaim-window.ts +16 -0
- package/src/modules/push_notifications/lib/send-custom-push.ts +100 -0
- package/src/modules/push_notifications/migrations/.snapshot-open-mercato.json +386 -0
- package/src/modules/push_notifications/migrations/Migration20260625150049_push_notifications.ts +16 -0
- package/src/modules/push_notifications/migrations/Migration20260626120000_push_notifications_silent.ts +13 -0
- package/src/modules/push_notifications/notifications.delivery-strategies.ts +8 -0
- package/src/modules/push_notifications/notifications.ts +50 -0
- package/src/modules/push_notifications/setup.ts +92 -0
- package/src/modules/push_notifications/workers/reclaim-stuck.worker.ts +78 -0
- package/src/modules/push_notifications/workers/send-push.worker.ts +34 -0
- package/src/modules/sales/i18n/de.json +1 -0
- package/src/modules/sales/i18n/en.json +1 -0
- package/src/modules/sales/i18n/es.json +1 -0
- package/src/modules/sales/i18n/ko.json +1 -0
- package/src/modules/sales/i18n/pl.json +1 -0
- package/src/modules/sales/notifications.ts +8 -0
- package/src/modules/staff/i18n/de.json +1 -0
- package/src/modules/staff/i18n/en.json +1 -0
- package/src/modules/staff/i18n/es.json +1 -0
- package/src/modules/staff/i18n/ko.json +1 -0
- package/src/modules/staff/i18n/pl.json +1 -0
- package/src/modules/staff/notifications.ts +6 -0
- package/src/modules/workflows/i18n/de.json +1 -0
- package/src/modules/workflows/i18n/en.json +1 -0
- package/src/modules/workflows/i18n/es.json +1 -0
- package/src/modules/workflows/i18n/ko.json +1 -0
- package/src/modules/workflows/i18n/pl.json +1 -0
- package/src/modules/workflows/notifications.ts +2 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
4
|
+
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
5
|
+
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
6
|
+
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
7
|
+
import { isUniqueViolation } from '@open-mercato/shared/lib/crud/errors'
|
|
8
|
+
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
9
|
+
import { NotificationType } from '../../../../../data/entities'
|
|
10
|
+
import { syncNotificationTypes } from '../../../../../lib/notification-type-registry'
|
|
11
|
+
import { computeChannelsPatch } from '../../../../../lib/typeChannelSettings'
|
|
12
|
+
import {
|
|
13
|
+
applyTypeOverride,
|
|
14
|
+
loadTypeOverrideForUpdate,
|
|
15
|
+
resolveCatalogueTranslate,
|
|
16
|
+
resolveEffectiveChannels,
|
|
17
|
+
resolveRegisteredChannelIds,
|
|
18
|
+
typeItem,
|
|
19
|
+
} from '../../../../../lib/typeCatalogue'
|
|
20
|
+
import { notificationTypeItemSchema } from '../../../../../data/validators'
|
|
21
|
+
import { errorResponseSchema } from '../../../../openapi'
|
|
22
|
+
import {
|
|
23
|
+
NOTIFICATION_SETTINGS_RESOURCE_KIND,
|
|
24
|
+
notificationCrudErrorResponse,
|
|
25
|
+
runGuardedNotificationWrite,
|
|
26
|
+
} from '../../../../../lib/routeHelpers'
|
|
27
|
+
|
|
28
|
+
const logger = createLogger('notifications').child({ component: 'type-channels-api' })
|
|
29
|
+
|
|
30
|
+
const paramsSchema = z.object({
|
|
31
|
+
id: z.string().min(1),
|
|
32
|
+
channel: z.string().min(1),
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
export const metadata = {
|
|
36
|
+
PUT: { requireAuth: true, requireFeatures: ['notifications.manage'] },
|
|
37
|
+
DELETE: { requireAuth: true, requireFeatures: ['notifications.manage'] },
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type RouteContext = { params?: { id?: string; channel?: string } | Promise<{ id?: string; channel?: string }> }
|
|
41
|
+
|
|
42
|
+
async function readParams(ctx: RouteContext) {
|
|
43
|
+
const raw = await ctx.params
|
|
44
|
+
return paramsSchema.parse({ id: raw?.id, channel: raw?.channel })
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Enable (`PUT`) or disable (`DELETE`) ONE channel for a notification type in the caller's tenant.
|
|
49
|
+
*
|
|
50
|
+
* Why a per-channel sub-resource rather than PATCHing the whole `channels` array: the array form
|
|
51
|
+
* makes the client read-modify-write the full set, so two operators editing the same type from
|
|
52
|
+
* stale views silently clobber each other — and while no override row exists yet there is no
|
|
53
|
+
* `updated_at` to version-lock against, so the optimistic-lock guard cannot even detect it. Here
|
|
54
|
+
* the server derives the next set from the CURRENT stored state under a row lock, so a concurrent
|
|
55
|
+
* toggle of a different channel survives. Same shape as the other member-collection endpoints in
|
|
56
|
+
* the repo (`customers/people/{id}/companies/{companyId}`, `catalog` media attach/detach).
|
|
57
|
+
*
|
|
58
|
+
* The full-array `PATCH /api/notifications/types` stays supported and unchanged for callers that
|
|
59
|
+
* genuinely want to set the whole set at once.
|
|
60
|
+
*/
|
|
61
|
+
async function toggleChannel(req: Request, ctx: RouteContext, enabled: boolean): Promise<Response> {
|
|
62
|
+
const { t } = await resolveTranslations()
|
|
63
|
+
const auth = await getAuthFromRequest(req)
|
|
64
|
+
if (!auth?.sub || !auth.tenantId) {
|
|
65
|
+
return NextResponse.json({ error: t('api.errors.unauthorized', 'Unauthorized') }, { status: 401 })
|
|
66
|
+
}
|
|
67
|
+
const tenantId = auth.tenantId
|
|
68
|
+
|
|
69
|
+
let params: z.infer<typeof paramsSchema>
|
|
70
|
+
try {
|
|
71
|
+
params = await readParams(ctx)
|
|
72
|
+
} catch {
|
|
73
|
+
return NextResponse.json(
|
|
74
|
+
{ error: t('api.errors.invalidPayload', 'Invalid request body') },
|
|
75
|
+
{ status: 400 },
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const container = await createRequestContainer()
|
|
80
|
+
try {
|
|
81
|
+
const em = container.resolve('em') as EntityManager
|
|
82
|
+
await syncNotificationTypes(em)
|
|
83
|
+
const row = await em.findOne(NotificationType, {
|
|
84
|
+
id: params.id,
|
|
85
|
+
$or: [{ tenantId: null }, { tenantId }],
|
|
86
|
+
})
|
|
87
|
+
if (!row) {
|
|
88
|
+
return NextResponse.json(
|
|
89
|
+
{ error: t('notifications.types.unknownType', 'Unknown notification type') },
|
|
90
|
+
{ status: 404 },
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Reject unregistered channels up front: silently storing one would put a dead entry in the
|
|
95
|
+
// eligibility set that no delivery strategy can ever satisfy.
|
|
96
|
+
const registeredChannelIds = resolveRegisteredChannelIds()
|
|
97
|
+
if (!registeredChannelIds.includes(params.channel)) {
|
|
98
|
+
return NextResponse.json(
|
|
99
|
+
{ error: t('notifications.types.unknownChannel', 'Unknown notification channel') },
|
|
100
|
+
{ status: 404 },
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const translate = await resolveCatalogueTranslate(req)
|
|
105
|
+
|
|
106
|
+
const guarded = await runGuardedNotificationWrite(
|
|
107
|
+
container,
|
|
108
|
+
{
|
|
109
|
+
tenantId,
|
|
110
|
+
organizationId: auth.orgId ?? null,
|
|
111
|
+
userId: auth.sub ?? null,
|
|
112
|
+
},
|
|
113
|
+
req,
|
|
114
|
+
{
|
|
115
|
+
resourceKind: NOTIFICATION_SETTINGS_RESOURCE_KIND,
|
|
116
|
+
resourceId: row.id,
|
|
117
|
+
operation: 'update',
|
|
118
|
+
payload: { id: row.id, channel: params.channel, enabled },
|
|
119
|
+
},
|
|
120
|
+
async () => {
|
|
121
|
+
// optimistic-lock-exempt: notification type channel add/remove. The next set is derived
|
|
122
|
+
// server-side from the current stored state inside a transaction that holds a row lock on
|
|
123
|
+
// the override, so a concurrent toggle cannot be lost — there is no shared aggregate for a
|
|
124
|
+
// version check to protect. Mirrors the add/remove endpoints in customers/catalog/staff.
|
|
125
|
+
const applyOnce = async () => {
|
|
126
|
+
return em.transactional(async (tem) => {
|
|
127
|
+
const existing = await loadTypeOverrideForUpdate(tem, tenantId, row.id)
|
|
128
|
+
const base = resolveEffectiveChannels(row.id, existing?.channels, registeredChannelIds)
|
|
129
|
+
// Unchecking the last channel maps to `null` (clear the override → the code-declared
|
|
130
|
+
// default reapplies) rather than an empty set, which would black-hole the type.
|
|
131
|
+
const nextChannels = computeChannelsPatch(base, params.channel, enabled)
|
|
132
|
+
const override = applyTypeOverride(tem, {
|
|
133
|
+
tenantId,
|
|
134
|
+
notificationTypeId: row.id,
|
|
135
|
+
existing,
|
|
136
|
+
nextChannels,
|
|
137
|
+
nextNonOptOut: existing?.nonOptOut ?? null,
|
|
138
|
+
})
|
|
139
|
+
await tem.flush()
|
|
140
|
+
return typeItem(row, override, translate)
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
return await applyOnce()
|
|
146
|
+
} catch (err) {
|
|
147
|
+
// First-write race: no row existed to lock, so a concurrent INSERT for this
|
|
148
|
+
// (tenant, type) won and the partial unique index rejected ours. A single-channel
|
|
149
|
+
// toggle is idempotent, so re-apply it on top of the winner instead of surfacing a
|
|
150
|
+
// conflict the operator would only have to resolve by clicking again.
|
|
151
|
+
if (!isUniqueViolation(err)) throw err
|
|
152
|
+
em.clear()
|
|
153
|
+
return applyOnce()
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
)
|
|
157
|
+
if (!guarded.ok) return guarded.response
|
|
158
|
+
return NextResponse.json({ ok: true, item: guarded.result })
|
|
159
|
+
} catch (error) {
|
|
160
|
+
const crudResponse = notificationCrudErrorResponse(error)
|
|
161
|
+
if (crudResponse) return crudResponse
|
|
162
|
+
logger.error('notification type channel toggle failed', {
|
|
163
|
+
typeId: params.id,
|
|
164
|
+
channel: params.channel,
|
|
165
|
+
enabled,
|
|
166
|
+
error: error instanceof Error ? error.message : String(error),
|
|
167
|
+
})
|
|
168
|
+
return NextResponse.json(
|
|
169
|
+
{ error: t('api.errors.internal', 'Internal error') },
|
|
170
|
+
{ status: 500 },
|
|
171
|
+
)
|
|
172
|
+
} finally {
|
|
173
|
+
const disposable = container as unknown as { dispose?: () => Promise<void> }
|
|
174
|
+
if (typeof disposable.dispose === 'function') await disposable.dispose()
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export async function PUT(req: Request, ctx: RouteContext): Promise<Response> {
|
|
179
|
+
return toggleChannel(req, ctx, true)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export async function DELETE(req: Request, ctx: RouteContext): Promise<Response> {
|
|
183
|
+
return toggleChannel(req, ctx, false)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const toggleResponseSchema = z.object({
|
|
187
|
+
ok: z.literal(true),
|
|
188
|
+
item: notificationTypeItemSchema,
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
export const openApi = {
|
|
192
|
+
PUT: {
|
|
193
|
+
summary: 'Enable one delivery channel for a notification type',
|
|
194
|
+
description:
|
|
195
|
+
'Adds a single channel to the tenant\'s eligibility set for this notification type. The next set is derived server-side from the currently stored state under a row lock, so concurrent toggles of different channels do not overwrite each other — prefer this over `PATCH /api/notifications/types` with a full `channels` array whenever a UI toggles one cell at a time. Idempotent: enabling an already-enabled channel is a no-op. Returns the refreshed catalogue item.',
|
|
196
|
+
tags: ['Notifications'],
|
|
197
|
+
responses: {
|
|
198
|
+
200: {
|
|
199
|
+
description: 'Updated notification type',
|
|
200
|
+
content: { 'application/json': { schema: toggleResponseSchema } },
|
|
201
|
+
},
|
|
202
|
+
401: { description: 'Unauthorized', content: { 'application/json': { schema: errorResponseSchema } } },
|
|
203
|
+
404: {
|
|
204
|
+
description: 'Unknown notification type or unregistered channel',
|
|
205
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
DELETE: {
|
|
210
|
+
summary: 'Disable one delivery channel for a notification type',
|
|
211
|
+
description:
|
|
212
|
+
'Removes a single channel from the tenant\'s eligibility set for this notification type, with the same concurrency-safe server-side derivation as `PUT`. Removing the last remaining channel clears the override entirely so the code-declared default reapplies (an empty set is never stored — it would leave the type undeliverable). Idempotent: disabling an already-disabled channel is a no-op.',
|
|
213
|
+
tags: ['Notifications'],
|
|
214
|
+
responses: {
|
|
215
|
+
200: {
|
|
216
|
+
description: 'Updated notification type',
|
|
217
|
+
content: { 'application/json': { schema: toggleResponseSchema } },
|
|
218
|
+
},
|
|
219
|
+
401: { description: 'Unauthorized', content: { 'application/json': { schema: errorResponseSchema } } },
|
|
220
|
+
404: {
|
|
221
|
+
description: 'Unknown notification type or unregistered channel',
|
|
222
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
3
|
+
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
4
|
+
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
5
|
+
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
6
|
+
import { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
|
|
7
|
+
import { conflict, isUniqueViolation } from '@open-mercato/shared/lib/crud/errors'
|
|
8
|
+
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
9
|
+
import { z } from 'zod'
|
|
10
|
+
import { NotificationType, NotificationTypeOverride } from '../../data/entities'
|
|
11
|
+
import { syncNotificationTypes } from '../../lib/notification-type-registry'
|
|
12
|
+
import { applyTypeOverride, resolveCatalogueTranslate, typeItem } from '../../lib/typeCatalogue'
|
|
13
|
+
import { notificationTypeItemSchema, updateNotificationTypeSchema } from '../../data/validators'
|
|
14
|
+
import { errorResponseSchema } from '../openapi'
|
|
15
|
+
import {
|
|
16
|
+
NOTIFICATION_SETTINGS_RESOURCE_KIND,
|
|
17
|
+
notificationCrudErrorResponse,
|
|
18
|
+
runGuardedNotificationWrite,
|
|
19
|
+
} from '../../lib/routeHelpers'
|
|
20
|
+
|
|
21
|
+
const logger = createLogger('notifications').child({ component: 'types-api' })
|
|
22
|
+
|
|
23
|
+
export const metadata = {
|
|
24
|
+
GET: { requireAuth: true, requireFeatures: ['notifications.view'] },
|
|
25
|
+
PATCH: { requireAuth: true, requireFeatures: ['notifications.manage'] },
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function GET(req: Request) {
|
|
29
|
+
const { t } = await resolveTranslations()
|
|
30
|
+
const auth = await getAuthFromRequest(req)
|
|
31
|
+
if (!auth?.sub || !auth.tenantId) {
|
|
32
|
+
return NextResponse.json({ error: t('api.errors.unauthorized', 'Unauthorized') }, { status: 401 })
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const container = await createRequestContainer()
|
|
36
|
+
try {
|
|
37
|
+
const em = container.resolve('em') as EntityManager
|
|
38
|
+
// Read-through mirror: ensure the catalogue is reflected in the DB (once per process).
|
|
39
|
+
await syncNotificationTypes(em)
|
|
40
|
+
const rows = await em.find(
|
|
41
|
+
NotificationType,
|
|
42
|
+
{ $or: [{ tenantId: null }, { tenantId: auth.tenantId }] },
|
|
43
|
+
{ orderBy: { id: 'asc' } },
|
|
44
|
+
)
|
|
45
|
+
const overrides = await em.find(NotificationTypeOverride, {
|
|
46
|
+
tenantId: auth.tenantId,
|
|
47
|
+
notificationTypeId: { $in: rows.map((row) => row.id) },
|
|
48
|
+
})
|
|
49
|
+
const overrideByType = new Map(overrides.map((override) => [override.notificationTypeId, override]))
|
|
50
|
+
const translate = await resolveCatalogueTranslate(req)
|
|
51
|
+
return NextResponse.json({
|
|
52
|
+
items: rows.map((row) => typeItem(row, overrideByType.get(row.id), translate)),
|
|
53
|
+
})
|
|
54
|
+
} finally {
|
|
55
|
+
const disposable = container as unknown as { dispose?: () => Promise<void> }
|
|
56
|
+
if (typeof disposable.dispose === 'function') await disposable.dispose()
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function PATCH(req: Request) {
|
|
61
|
+
// Admin write: the echoed item stays on the request-context locale (no `?locale=`
|
|
62
|
+
// override), so the confirmation matches the operator's own session.
|
|
63
|
+
const { t, translate } = await resolveTranslations()
|
|
64
|
+
const auth = await getAuthFromRequest(req)
|
|
65
|
+
if (!auth?.sub || !auth.tenantId) {
|
|
66
|
+
return NextResponse.json({ error: t('api.errors.unauthorized', 'Unauthorized') }, { status: 401 })
|
|
67
|
+
}
|
|
68
|
+
const tenantId = auth.tenantId
|
|
69
|
+
|
|
70
|
+
let body: unknown
|
|
71
|
+
try {
|
|
72
|
+
body = await req.json()
|
|
73
|
+
} catch {
|
|
74
|
+
return NextResponse.json(
|
|
75
|
+
{ error: t('api.errors.invalidPayload', 'Invalid request body') },
|
|
76
|
+
{ status: 400 },
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const parsed = updateNotificationTypeSchema.safeParse(body)
|
|
81
|
+
if (!parsed.success) {
|
|
82
|
+
return NextResponse.json(
|
|
83
|
+
{ error: t('notifications.types.invalidChannels', 'Invalid channels payload') },
|
|
84
|
+
{ status: 400 },
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const container = await createRequestContainer()
|
|
89
|
+
try {
|
|
90
|
+
const em = container.resolve('em') as EntityManager
|
|
91
|
+
await syncNotificationTypes(em)
|
|
92
|
+
const row = await em.findOne(NotificationType, {
|
|
93
|
+
id: parsed.data.id,
|
|
94
|
+
$or: [{ tenantId: null }, { tenantId }],
|
|
95
|
+
})
|
|
96
|
+
if (!row) {
|
|
97
|
+
return NextResponse.json(
|
|
98
|
+
{ error: t('notifications.types.unknownType', 'Unknown notification type') },
|
|
99
|
+
{ status: 404 },
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
const existing = await em.findOne(NotificationTypeOverride, {
|
|
103
|
+
tenantId,
|
|
104
|
+
notificationTypeId: row.id,
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
const guarded = await runGuardedNotificationWrite(
|
|
108
|
+
container,
|
|
109
|
+
{
|
|
110
|
+
tenantId,
|
|
111
|
+
organizationId: auth.orgId ?? null,
|
|
112
|
+
userId: auth.sub ?? null,
|
|
113
|
+
},
|
|
114
|
+
req,
|
|
115
|
+
{
|
|
116
|
+
resourceKind: NOTIFICATION_SETTINGS_RESOURCE_KIND,
|
|
117
|
+
resourceId: row.id,
|
|
118
|
+
operation: 'update',
|
|
119
|
+
payload: parsed.data as unknown as Record<string, unknown>,
|
|
120
|
+
},
|
|
121
|
+
async () => {
|
|
122
|
+
// Same 409 contract as the CRUD guard: a stale admin view (another operator saved
|
|
123
|
+
// since this client loaded the catalogue) must not silently clobber the newer
|
|
124
|
+
// override — the full `channels` array replaces, so a blind write loses edits.
|
|
125
|
+
enforceCommandOptimisticLock({
|
|
126
|
+
resourceKind: NOTIFICATION_SETTINGS_RESOURCE_KIND,
|
|
127
|
+
resourceId: row.id,
|
|
128
|
+
current: existing?.updatedAt ?? null,
|
|
129
|
+
request: req,
|
|
130
|
+
})
|
|
131
|
+
const nextChannels = parsed.data.channels !== undefined ? parsed.data.channels : existing?.channels ?? null
|
|
132
|
+
const nextNonOptOut = parsed.data.nonOptOut !== undefined ? parsed.data.nonOptOut : existing?.nonOptOut ?? null
|
|
133
|
+
const override = applyTypeOverride(em, {
|
|
134
|
+
tenantId,
|
|
135
|
+
notificationTypeId: row.id,
|
|
136
|
+
existing,
|
|
137
|
+
nextChannels,
|
|
138
|
+
nextNonOptOut,
|
|
139
|
+
})
|
|
140
|
+
try {
|
|
141
|
+
await em.flush()
|
|
142
|
+
} catch (err) {
|
|
143
|
+
// Concurrent first-write race: another operator created the override for this (tenant, type)
|
|
144
|
+
// between our read (existing === null) and this flush, so the partial unique index rejects
|
|
145
|
+
// ours. There was no version to compare, so the optimistic-lock guard couldn't fire — surface
|
|
146
|
+
// the same 409 it would rather than letting the unique violation fall through to a generic 500.
|
|
147
|
+
if (isUniqueViolation(err)) {
|
|
148
|
+
throw conflict(
|
|
149
|
+
t(
|
|
150
|
+
'notifications.types.overrideConflict',
|
|
151
|
+
'Another operator just saved this notification type. Reload and try again.',
|
|
152
|
+
),
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
throw err
|
|
156
|
+
}
|
|
157
|
+
return typeItem(row, override, translate)
|
|
158
|
+
},
|
|
159
|
+
)
|
|
160
|
+
if (!guarded.ok) return guarded.response
|
|
161
|
+
return NextResponse.json({ ok: true, item: guarded.result })
|
|
162
|
+
} catch (error) {
|
|
163
|
+
const crudResponse = notificationCrudErrorResponse(error)
|
|
164
|
+
if (crudResponse) return crudResponse
|
|
165
|
+
logger.error('notification type override update failed', {
|
|
166
|
+
typeId: parsed.data.id,
|
|
167
|
+
error: error instanceof Error ? error.message : String(error),
|
|
168
|
+
})
|
|
169
|
+
return NextResponse.json(
|
|
170
|
+
{ error: t('api.errors.internal', 'Internal error') },
|
|
171
|
+
{ status: 500 },
|
|
172
|
+
)
|
|
173
|
+
} finally {
|
|
174
|
+
const disposable = container as unknown as { dispose?: () => Promise<void> }
|
|
175
|
+
if (typeof disposable.dispose === 'function') await disposable.dispose()
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export const openApi = {
|
|
180
|
+
GET: {
|
|
181
|
+
summary: 'List notification types',
|
|
182
|
+
description: 'Returns the notification type catalogue (system-wide + tenant) so clients can render a preferences screen. `channels` is the effective channel eligibility for the caller\'s tenant (stored override, else the code-declared set; `null` = every channel); a channel outside it never delivers in that tenant and cannot be enabled by users. `updatedAt` is the override row\'s optimistic-lock version (`null` when the tenant stores no override). `label`, `description` and `categoryLabel` are resolved server-side using the request locale (`?locale=`, `x-locale`, the `locale` cookie, or `Accept-Language`) so clients without the app dictionary can render them directly; `categoryLabel` falls back to the raw category key when the owning module ships no `notifications.categories.<key>` translation, so `categoryLabel === category` signals "no server-side label". `category` defaults to the prefix before the first dot in the type id and is the stable grouping key — group on `category`, display `categoryLabel`.',
|
|
183
|
+
tags: ['Notifications'],
|
|
184
|
+
responses: {
|
|
185
|
+
200: {
|
|
186
|
+
description: 'Notification type catalogue',
|
|
187
|
+
content: {
|
|
188
|
+
'application/json': {
|
|
189
|
+
schema: z.object({ items: z.array(notificationTypeItemSchema) }),
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
401: {
|
|
194
|
+
description: 'Unauthorized',
|
|
195
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
PATCH: {
|
|
200
|
+
summary: 'Override a notification type\'s channel eligibility and opt-out governance for the caller\'s tenant',
|
|
201
|
+
description: 'Tenant-scoped operator overrides for a notification type (stored in `notification_type_overrides`; other tenants are unaffected). `channels` replaces the code-declared eligibility (a channel outside the effective set is completely off for the tenant: it beats user preferences and `nonOptOut`, and preference UIs lock the cell). `nonOptOut` overrides the code-declared opt-out governance (`true` forces the type on for users, `false` makes a required type user-editable). Omitted fields stay untouched; pass `null` to clear a stored override and inherit the code declaration. Sends the standard optimistic-lock header contract: pass the item\'s `updatedAt` as `x-om-ext-optimistic-lock-expected-updated-at` to detect concurrent edits (409 on mismatch).',
|
|
202
|
+
tags: ['Notifications'],
|
|
203
|
+
requestBody: {
|
|
204
|
+
required: true,
|
|
205
|
+
content: {
|
|
206
|
+
'application/json': { schema: updateNotificationTypeSchema },
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
responses: {
|
|
210
|
+
200: {
|
|
211
|
+
description: 'Override saved',
|
|
212
|
+
content: {
|
|
213
|
+
'application/json': {
|
|
214
|
+
schema: z.object({
|
|
215
|
+
ok: z.literal(true),
|
|
216
|
+
item: notificationTypeItemSchema,
|
|
217
|
+
}),
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
400: {
|
|
222
|
+
description: 'Invalid request body',
|
|
223
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
224
|
+
},
|
|
225
|
+
401: {
|
|
226
|
+
description: 'Unauthorized',
|
|
227
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
228
|
+
},
|
|
229
|
+
404: {
|
|
230
|
+
description: 'Unknown notification type',
|
|
231
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
232
|
+
},
|
|
233
|
+
409: {
|
|
234
|
+
description: 'Optimistic-lock conflict — another operator saved a newer override',
|
|
235
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
}
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
buildNotificationReadScopeWhere,
|
|
14
14
|
getNotificationReadScopeTagOrganizationIds,
|
|
15
15
|
} from '../../lib/notificationScope'
|
|
16
|
+
import { inAppVisibleFilter } from '../../lib/notificationVisibility'
|
|
16
17
|
|
|
17
18
|
export const metadata = {
|
|
18
19
|
GET: { requireAuth: true },
|
|
@@ -75,7 +76,10 @@ export async function GET(req: Request) {
|
|
|
75
76
|
recipientUserId: userId,
|
|
76
77
|
tenantId: scope.tenantId,
|
|
77
78
|
status: 'unread',
|
|
78
|
-
|
|
79
|
+
$and: [
|
|
80
|
+
buildNotificationReadScopeWhere(scope),
|
|
81
|
+
inAppVisibleFilter(),
|
|
82
|
+
],
|
|
79
83
|
})
|
|
80
84
|
|
|
81
85
|
if (cache && cacheKey) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
const usersCogIcon = React.createElement(
|
|
4
|
+
'svg',
|
|
5
|
+
{ width: 16, height: 16, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' },
|
|
6
|
+
React.createElement('path', { d: 'M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2' }),
|
|
7
|
+
React.createElement('circle', { cx: 9, cy: 7, r: 4 }),
|
|
8
|
+
React.createElement('circle', { cx: 19, cy: 16, r: 2 }),
|
|
9
|
+
React.createElement('path', { d: 'M19 12v1' }),
|
|
10
|
+
React.createElement('path', { d: 'M19 19v1' }),
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
export const metadata = {
|
|
14
|
+
requireAuth: true,
|
|
15
|
+
requireFeatures: ['notifications.manage_user_preferences'],
|
|
16
|
+
pageTitle: 'User Notification Preferences',
|
|
17
|
+
pageTitleKey: 'notifications.preferences.admin.pageTitle',
|
|
18
|
+
pageGroup: 'Auth',
|
|
19
|
+
pageGroupKey: 'settings.sections.auth',
|
|
20
|
+
pageOrder: 5,
|
|
21
|
+
icon: usersCogIcon,
|
|
22
|
+
pageContext: 'settings' as const,
|
|
23
|
+
breadcrumb: [
|
|
24
|
+
{ label: 'User Notification Preferences', labelKey: 'notifications.preferences.admin.pageTitle' },
|
|
25
|
+
],
|
|
26
|
+
} as const
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Page, PageBody } from '@open-mercato/ui/backend/Page'
|
|
2
|
+
import { NotificationUserPreferencesAdminPageClient } from '../../../frontend/NotificationUserPreferencesAdminPageClient'
|
|
3
|
+
|
|
4
|
+
export default async function NotificationUserPreferencesAdminPage() {
|
|
5
|
+
return (
|
|
6
|
+
<Page>
|
|
7
|
+
<PageBody>
|
|
8
|
+
<NotificationUserPreferencesAdminPageClient />
|
|
9
|
+
</PageBody>
|
|
10
|
+
</Page>
|
|
11
|
+
)
|
|
12
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
const slidersIcon = React.createElement(
|
|
4
|
+
'svg',
|
|
5
|
+
{ width: 16, height: 16, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' },
|
|
6
|
+
React.createElement('line', { x1: 4, y1: 21, x2: 4, y2: 14 }),
|
|
7
|
+
React.createElement('line', { x1: 4, y1: 10, x2: 4, y2: 3 }),
|
|
8
|
+
React.createElement('line', { x1: 12, y1: 21, x2: 12, y2: 12 }),
|
|
9
|
+
React.createElement('line', { x1: 12, y1: 8, x2: 12, y2: 3 }),
|
|
10
|
+
React.createElement('line', { x1: 20, y1: 21, x2: 20, y2: 16 }),
|
|
11
|
+
React.createElement('line', { x1: 20, y1: 12, x2: 20, y2: 3 }),
|
|
12
|
+
React.createElement('line', { x1: 1, y1: 14, x2: 7, y2: 14 }),
|
|
13
|
+
React.createElement('line', { x1: 9, y1: 8, x2: 15, y2: 8 }),
|
|
14
|
+
React.createElement('line', { x1: 17, y1: 16, x2: 23, y2: 16 }),
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
export const metadata = {
|
|
18
|
+
requireAuth: true,
|
|
19
|
+
requireFeatures: ['notifications.manage_preferences'],
|
|
20
|
+
pageTitle: 'Notification Preferences',
|
|
21
|
+
pageTitleKey: 'notifications.preferences.pageTitle',
|
|
22
|
+
pageGroup: 'Profile',
|
|
23
|
+
pageGroupKey: 'notifications.preferences.profileGroup',
|
|
24
|
+
pageOrder: 30,
|
|
25
|
+
icon: slidersIcon,
|
|
26
|
+
pageContext: 'profile' as const,
|
|
27
|
+
breadcrumb: [
|
|
28
|
+
{ label: 'Profile', labelKey: 'notifications.preferences.profileGroup' },
|
|
29
|
+
{ label: 'Notification Preferences', labelKey: 'notifications.preferences.pageTitle' },
|
|
30
|
+
],
|
|
31
|
+
} as const
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Page, PageBody } from '@open-mercato/ui/backend/Page'
|
|
2
|
+
import { NotificationPreferencesPageClient } from '../../../frontend/NotificationPreferencesPageClient'
|
|
3
|
+
|
|
4
|
+
export default async function NotificationPreferencesProfilePage() {
|
|
5
|
+
return (
|
|
6
|
+
<Page>
|
|
7
|
+
<PageBody>
|
|
8
|
+
<NotificationPreferencesPageClient />
|
|
9
|
+
</PageBody>
|
|
10
|
+
</Page>
|
|
11
|
+
)
|
|
12
|
+
}
|