@open-mercato/core 0.4.2-canary-e6bf6a353e → 0.4.2-canary-49d47ff90e
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/dist/generated/entities/notification/index.js +57 -0
- package/dist/generated/entities/notification/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +5 -1
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +2 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/api_docs/frontend/docs/api/page.js +3 -2
- package/dist/modules/api_docs/frontend/docs/api/page.js.map +2 -2
- package/dist/modules/api_keys/backend/api-keys/page.js +1 -1
- package/dist/modules/api_keys/backend/api-keys/page.js.map +2 -2
- package/dist/modules/attachments/components/AttachmentLibrary.js +4 -0
- package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
- package/dist/modules/attachments/components/AttachmentPartitionSettings.js +2 -0
- package/dist/modules/attachments/components/AttachmentPartitionSettings.js.map +2 -2
- package/dist/modules/auth/api/admin/nav.js +4 -3
- package/dist/modules/auth/api/admin/nav.js.map +2 -2
- package/dist/modules/auth/api/profile/route.js +157 -0
- package/dist/modules/auth/api/profile/route.js.map +7 -0
- package/dist/modules/auth/api/reset/confirm.js +25 -2
- package/dist/modules/auth/api/reset/confirm.js.map +2 -2
- package/dist/modules/auth/api/reset.js +23 -0
- package/dist/modules/auth/api/reset.js.map +2 -2
- package/dist/modules/auth/api/sidebar/preferences/route.js +14 -9
- package/dist/modules/auth/api/sidebar/preferences/route.js.map +2 -2
- package/dist/modules/auth/api/users/route.js +4 -2
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/auth/backend/auth/profile/page.js +141 -0
- package/dist/modules/auth/backend/auth/profile/page.js.map +7 -0
- package/dist/modules/auth/backend/auth/profile/page.meta.js +13 -0
- package/dist/modules/auth/backend/auth/profile/page.meta.js.map +7 -0
- package/dist/modules/auth/backend/roles/page.js +3 -3
- package/dist/modules/auth/backend/roles/page.js.map +2 -2
- package/dist/modules/auth/backend/users/[id]/edit/page.js +14 -2
- package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/users/create/page.js +15 -2
- package/dist/modules/auth/backend/users/create/page.js.map +2 -2
- package/dist/modules/auth/backend/users/page.js +3 -3
- package/dist/modules/auth/backend/users/page.js.map +2 -2
- package/dist/modules/auth/cli.js +13 -0
- package/dist/modules/auth/cli.js.map +2 -2
- package/dist/modules/auth/commands/users.js +59 -2
- package/dist/modules/auth/commands/users.js.map +2 -2
- package/dist/modules/auth/data/validators.js +4 -2
- package/dist/modules/auth/data/validators.js.map +2 -2
- package/dist/modules/auth/frontend/reset/[token]/page.js +20 -10
- package/dist/modules/auth/frontend/reset/[token]/page.js.map +2 -2
- package/dist/modules/auth/lib/setup-app.js +1 -0
- package/dist/modules/auth/lib/setup-app.js.map +2 -2
- package/dist/modules/auth/notifications.js +112 -0
- package/dist/modules/auth/notifications.js.map +7 -0
- package/dist/modules/auth/services/authService.js +3 -3
- package/dist/modules/auth/services/authService.js.map +2 -2
- package/dist/modules/business_rules/backend/rules/page.js +4 -0
- package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
- package/dist/modules/business_rules/backend/sets/page.js +3 -0
- package/dist/modules/business_rules/backend/sets/page.js.map +2 -2
- package/dist/modules/business_rules/notifications.js +28 -0
- package/dist/modules/business_rules/notifications.js.map +7 -0
- package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js +37 -0
- package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js.map +7 -0
- package/dist/modules/catalog/components/PriceKindSettings.js +2 -0
- package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
- package/dist/modules/catalog/components/categories/CategoriesDataTable.js +2 -2
- package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +2 -2
- package/dist/modules/catalog/components/products/ProductsDataTable.js +2 -0
- package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
- package/dist/modules/catalog/notifications.js +28 -0
- package/dist/modules/catalog/notifications.js.map +7 -0
- package/dist/modules/catalog/subscribers/low-stock-notification.js +38 -0
- package/dist/modules/catalog/subscribers/low-stock-notification.js.map +7 -0
- package/dist/modules/configs/cli.js +6 -0
- package/dist/modules/configs/cli.js.map +2 -2
- package/dist/modules/configs/lib/upgrade-actions.js +18 -0
- package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
- package/dist/modules/currencies/backend/currencies/page.js +3 -0
- package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
- package/dist/modules/currencies/backend/exchange-rates/page.js +2 -0
- package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies/page.js +3 -0
- package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +3 -0
- package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people/page.js +3 -0
- package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +31 -0
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/components/CustomerTodosTable.js +1 -0
- package/dist/modules/customers/components/CustomerTodosTable.js.map +2 -2
- package/dist/modules/customers/notifications.js +48 -0
- package/dist/modules/customers/notifications.js.map +7 -0
- package/dist/modules/dashboards/cli.js +32 -1
- package/dist/modules/dashboards/cli.js.map +2 -2
- package/dist/modules/dashboards/lib/role-widgets.js +58 -0
- package/dist/modules/dashboards/lib/role-widgets.js.map +7 -0
- package/dist/modules/dictionaries/components/DictionaryTable.js +2 -0
- package/dist/modules/dictionaries/components/DictionaryTable.js.map +2 -2
- package/dist/modules/directory/backend/directory/organizations/page.js +2 -2
- package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/tenants/page.js +2 -2
- package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
- package/dist/modules/entities/components/SystemEntitiesTable.js +1 -1
- package/dist/modules/entities/components/SystemEntitiesTable.js.map +2 -2
- package/dist/modules/entities/components/UserEntitiesTable.js +2 -2
- package/dist/modules/entities/components/UserEntitiesTable.js.map +2 -2
- package/dist/modules/feature_toggles/components/FeatureTogglesTable.js +3 -3
- package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
- package/dist/modules/feature_toggles/components/OverridesTable.js +1 -1
- package/dist/modules/feature_toggles/components/OverridesTable.js.map +2 -2
- package/dist/modules/notifications/acl.js +11 -0
- package/dist/modules/notifications/acl.js.map +7 -0
- package/dist/modules/notifications/api/[id]/action/route.js +74 -0
- package/dist/modules/notifications/api/[id]/action/route.js.map +7 -0
- package/dist/modules/notifications/api/[id]/dismiss/route.js +15 -0
- package/dist/modules/notifications/api/[id]/dismiss/route.js.map +7 -0
- package/dist/modules/notifications/api/[id]/read/route.js +15 -0
- package/dist/modules/notifications/api/[id]/read/route.js.map +7 -0
- package/dist/modules/notifications/api/[id]/restore/route.js +53 -0
- package/dist/modules/notifications/api/[id]/restore/route.js.map +7 -0
- package/dist/modules/notifications/api/batch/route.js +17 -0
- package/dist/modules/notifications/api/batch/route.js.map +7 -0
- package/dist/modules/notifications/api/feature/route.js +17 -0
- package/dist/modules/notifications/api/feature/route.js.map +7 -0
- package/dist/modules/notifications/api/mark-all-read/route.js +35 -0
- package/dist/modules/notifications/api/mark-all-read/route.js.map +7 -0
- package/dist/modules/notifications/api/openapi.js +76 -0
- package/dist/modules/notifications/api/openapi.js.map +7 -0
- package/dist/modules/notifications/api/role/route.js +17 -0
- package/dist/modules/notifications/api/role/route.js.map +7 -0
- package/dist/modules/notifications/api/route.js +85 -0
- package/dist/modules/notifications/api/route.js.map +7 -0
- package/dist/modules/notifications/api/settings/route.js +155 -0
- package/dist/modules/notifications/api/settings/route.js.map +7 -0
- package/dist/modules/notifications/api/unread-count/route.js +38 -0
- package/dist/modules/notifications/api/unread-count/route.js.map +7 -0
- package/dist/modules/notifications/backend/config/notifications/page.js +10 -0
- package/dist/modules/notifications/backend/config/notifications/page.js.map +7 -0
- package/dist/modules/notifications/backend/config/notifications/page.meta.js +24 -0
- package/dist/modules/notifications/backend/config/notifications/page.meta.js.map +7 -0
- package/dist/modules/notifications/cli.js +16 -0
- package/dist/modules/notifications/cli.js.map +7 -0
- package/dist/modules/notifications/data/entities.js +112 -0
- package/dist/modules/notifications/data/entities.js.map +7 -0
- package/dist/modules/notifications/data/validators.js +94 -0
- package/dist/modules/notifications/data/validators.js.map +7 -0
- package/dist/modules/notifications/di.js +13 -0
- package/dist/modules/notifications/di.js.map +7 -0
- package/dist/modules/notifications/emails/NotificationEmail.js +58 -0
- package/dist/modules/notifications/emails/NotificationEmail.js.map +7 -0
- package/dist/modules/notifications/frontend/NotificationInboxPageClient.js +44 -0
- package/dist/modules/notifications/frontend/NotificationInboxPageClient.js.map +7 -0
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +219 -0
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +7 -0
- package/dist/modules/notifications/index.js +14 -0
- package/dist/modules/notifications/index.js.map +7 -0
- package/dist/modules/notifications/lib/deliveryConfig.js +105 -0
- package/dist/modules/notifications/lib/deliveryConfig.js.map +7 -0
- package/dist/modules/notifications/lib/events.js +12 -0
- package/dist/modules/notifications/lib/events.js.map +7 -0
- package/dist/modules/notifications/lib/notificationBuilder.js +66 -0
- package/dist/modules/notifications/lib/notificationBuilder.js.map +7 -0
- package/dist/modules/notifications/lib/notificationFactory.js +54 -0
- package/dist/modules/notifications/lib/notificationFactory.js.map +7 -0
- package/dist/modules/notifications/lib/notificationMapper.js +34 -0
- package/dist/modules/notifications/lib/notificationMapper.js.map +7 -0
- package/dist/modules/notifications/lib/notificationRecipients.js +35 -0
- package/dist/modules/notifications/lib/notificationRecipients.js.map +7 -0
- package/dist/modules/notifications/lib/notificationService.js +279 -0
- package/dist/modules/notifications/lib/notificationService.js.map +7 -0
- package/dist/modules/notifications/lib/routeHelpers.js +101 -0
- package/dist/modules/notifications/lib/routeHelpers.js.map +7 -0
- package/dist/modules/notifications/lib/safeHref.js +24 -0
- package/dist/modules/notifications/lib/safeHref.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260123000001.js +70 -0
- package/dist/modules/notifications/migrations/Migration20260123000001.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260126150000.js +37 -0
- package/dist/modules/notifications/migrations/Migration20260126150000.js.map +7 -0
- package/dist/modules/notifications/subscribers/deliver-notification.js +139 -0
- package/dist/modules/notifications/subscribers/deliver-notification.js.map +7 -0
- package/dist/modules/notifications/workers/create-notification.worker.js +70 -0
- package/dist/modules/notifications/workers/create-notification.worker.js.map +7 -0
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js +2 -2
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
- package/dist/modules/query_index/components/QueryIndexesTable.js +7 -1
- package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
- package/dist/modules/resources/backend/resources/resource-types/page.js +2 -2
- package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
- package/dist/modules/resources/backend/resources/resources/page.js +2 -2
- package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/offers/page.js +2 -0
- package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/page.js +2 -0
- package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +53 -0
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/commands/payments.js +26 -0
- package/dist/modules/sales/commands/payments.js.map +2 -2
- package/dist/modules/sales/components/AdjustmentKindSettings.js +2 -2
- package/dist/modules/sales/components/AdjustmentKindSettings.js.map +2 -2
- package/dist/modules/sales/components/PaymentMethodsSettings.js +2 -2
- package/dist/modules/sales/components/PaymentMethodsSettings.js.map +2 -2
- package/dist/modules/sales/components/ShippingMethodsSettings.js +2 -2
- package/dist/modules/sales/components/ShippingMethodsSettings.js.map +2 -2
- package/dist/modules/sales/components/TaxRatesSettings.js +2 -2
- package/dist/modules/sales/components/TaxRatesSettings.js.map +2 -2
- package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +2 -0
- package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
- package/dist/modules/sales/components/documents/AdjustmentsSection.js +2 -0
- package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/PaymentsSection.js +2 -1
- package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js +2 -0
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
- package/dist/modules/sales/notifications.client.js +51 -0
- package/dist/modules/sales/notifications.client.js.map +7 -0
- package/dist/modules/sales/notifications.js +88 -0
- package/dist/modules/sales/notifications.js.map +7 -0
- package/dist/modules/sales/subscribers/quote-expiring-notification.js +38 -0
- package/dist/modules/sales/subscribers/quote-expiring-notification.js.map +7 -0
- package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js +137 -0
- package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js.map +7 -0
- package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js +137 -0
- package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js.map +7 -0
- package/dist/modules/sales/widgets/notifications/index.js +7 -0
- package/dist/modules/sales/widgets/notifications/index.js.map +7 -0
- package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js +60 -0
- package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js.map +7 -0
- package/dist/modules/staff/backend/staff/team-members/page.js +1 -1
- package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-roles/page.js +2 -2
- package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +2 -2
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/page.js +2 -2
- package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
- package/dist/modules/staff/commands/leave-requests.js +79 -0
- package/dist/modules/staff/commands/leave-requests.js.map +2 -2
- package/dist/modules/staff/notifications.js +75 -0
- package/dist/modules/staff/notifications.js.map +7 -0
- package/dist/modules/workflows/backend/definitions/page.js +5 -0
- package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
- package/dist/modules/workflows/backend/instances/page.js +3 -0
- package/dist/modules/workflows/backend/instances/page.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/page.js +3 -0
- package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
- package/dist/modules/workflows/notifications.js +28 -0
- package/dist/modules/workflows/notifications.js.map +7 -0
- package/dist/modules/workflows/subscribers/task-assigned-notification.js +38 -0
- package/dist/modules/workflows/subscribers/task-assigned-notification.js.map +7 -0
- package/generated/entities/notification/index.ts +27 -0
- package/generated/entities.ids.generated.ts +5 -1
- package/generated/entity-fields-registry.ts +2 -0
- package/package.json +2 -2
- package/src/modules/api_docs/frontend/docs/api/page.tsx +3 -2
- package/src/modules/api_keys/backend/api-keys/page.tsx +1 -1
- package/src/modules/attachments/components/AttachmentLibrary.tsx +4 -0
- package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +2 -0
- package/src/modules/auth/api/admin/nav.ts +10 -6
- package/src/modules/auth/api/profile/route.ts +163 -0
- package/src/modules/auth/api/reset/confirm.ts +25 -2
- package/src/modules/auth/api/reset.ts +23 -0
- package/src/modules/auth/api/sidebar/preferences/route.ts +21 -12
- package/src/modules/auth/api/users/route.ts +5 -2
- package/src/modules/auth/backend/auth/profile/page.meta.ts +9 -0
- package/src/modules/auth/backend/auth/profile/page.tsx +174 -0
- package/src/modules/auth/backend/roles/page.tsx +3 -3
- package/src/modules/auth/backend/users/[id]/edit/page.tsx +18 -2
- package/src/modules/auth/backend/users/create/page.tsx +19 -2
- package/src/modules/auth/backend/users/page.tsx +3 -3
- package/src/modules/auth/cli.ts +14 -0
- package/src/modules/auth/commands/users.ts +73 -2
- package/src/modules/auth/data/validators.ts +5 -2
- package/src/modules/auth/frontend/reset/[token]/page.tsx +24 -11
- package/src/modules/auth/i18n/de.json +43 -1
- package/src/modules/auth/i18n/en.json +43 -1
- package/src/modules/auth/i18n/es.json +43 -1
- package/src/modules/auth/i18n/pl.json +43 -1
- package/src/modules/auth/lib/setup-app.ts +1 -0
- package/src/modules/auth/notifications.ts +109 -0
- package/src/modules/auth/services/authService.ts +4 -4
- package/src/modules/business_rules/backend/rules/page.tsx +4 -0
- package/src/modules/business_rules/backend/sets/page.tsx +3 -0
- package/src/modules/business_rules/i18n/en.json +3 -1
- package/src/modules/business_rules/notifications.ts +25 -0
- package/src/modules/business_rules/subscribers/rule-execution-failed-notification.ts +50 -0
- package/src/modules/catalog/components/PriceKindSettings.tsx +2 -0
- package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +2 -2
- package/src/modules/catalog/components/products/ProductsDataTable.tsx +2 -0
- package/src/modules/catalog/i18n/en.json +3 -1
- package/src/modules/catalog/notifications.ts +25 -0
- package/src/modules/catalog/subscribers/low-stock-notification.ts +52 -0
- package/src/modules/configs/cli.ts +6 -0
- package/src/modules/configs/lib/upgrade-actions.ts +18 -0
- package/src/modules/currencies/backend/currencies/page.tsx +3 -0
- package/src/modules/currencies/backend/exchange-rates/page.tsx +2 -0
- package/src/modules/customers/backend/customers/companies/page.tsx +3 -0
- package/src/modules/customers/backend/customers/deals/page.tsx +3 -0
- package/src/modules/customers/backend/customers/people/page.tsx +3 -0
- package/src/modules/customers/commands/deals.ts +39 -0
- package/src/modules/customers/components/CustomerTodosTable.tsx +1 -0
- package/src/modules/customers/i18n/en.json +5 -1
- package/src/modules/customers/notifications.ts +44 -0
- package/src/modules/dashboards/cli.ts +41 -1
- package/src/modules/dashboards/lib/role-widgets.ts +80 -0
- package/src/modules/dictionaries/components/DictionaryTable.tsx +2 -0
- package/src/modules/directory/backend/directory/organizations/page.tsx +2 -2
- package/src/modules/directory/backend/directory/tenants/page.tsx +2 -2
- package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +2 -2
- package/src/modules/entities/components/SystemEntitiesTable.tsx +1 -1
- package/src/modules/entities/components/UserEntitiesTable.tsx +2 -2
- package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +3 -4
- package/src/modules/feature_toggles/components/OverridesTable.tsx +1 -1
- package/src/modules/notifications/acl.ts +7 -0
- package/src/modules/notifications/api/[id]/action/route.ts +75 -0
- package/src/modules/notifications/api/[id]/dismiss/route.ts +12 -0
- package/src/modules/notifications/api/[id]/read/route.ts +12 -0
- package/src/modules/notifications/api/[id]/restore/route.ts +53 -0
- package/src/modules/notifications/api/batch/route.ts +14 -0
- package/src/modules/notifications/api/feature/route.ts +14 -0
- package/src/modules/notifications/api/mark-all-read/route.ts +34 -0
- package/src/modules/notifications/api/openapi.ts +76 -0
- package/src/modules/notifications/api/role/route.ts +14 -0
- package/src/modules/notifications/api/route.ts +92 -0
- package/src/modules/notifications/api/settings/route.ts +157 -0
- package/src/modules/notifications/api/unread-count/route.ts +38 -0
- package/src/modules/notifications/backend/config/notifications/page.meta.ts +22 -0
- package/src/modules/notifications/backend/config/notifications/page.tsx +12 -0
- package/src/modules/notifications/cli.ts +18 -0
- package/src/modules/notifications/data/entities.ts +99 -0
- package/src/modules/notifications/data/validators.ts +110 -0
- package/src/modules/notifications/di.ts +11 -0
- package/src/modules/notifications/emails/NotificationEmail.tsx +98 -0
- package/src/modules/notifications/frontend/NotificationInboxPageClient.tsx +42 -0
- package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +231 -0
- package/src/modules/notifications/i18n/de.json +50 -0
- package/src/modules/notifications/i18n/en.json +50 -0
- package/src/modules/notifications/i18n/es.json +50 -0
- package/src/modules/notifications/i18n/pl.json +50 -0
- package/src/modules/notifications/index.ts +12 -0
- package/src/modules/notifications/lib/deliveryConfig.ts +145 -0
- package/src/modules/notifications/lib/events.ts +48 -0
- package/src/modules/notifications/lib/notificationBuilder.ts +121 -0
- package/src/modules/notifications/lib/notificationFactory.ts +76 -0
- package/src/modules/notifications/lib/notificationMapper.ts +33 -0
- package/src/modules/notifications/lib/notificationRecipients.ts +83 -0
- package/src/modules/notifications/lib/notificationService.ts +414 -0
- package/src/modules/notifications/lib/routeHelpers.ts +151 -0
- package/src/modules/notifications/lib/safeHref.ts +29 -0
- package/src/modules/notifications/migrations/.snapshot-open-mercato.json +300 -0
- package/src/modules/notifications/migrations/Migration20260123000001.ts +73 -0
- package/src/modules/notifications/migrations/Migration20260126150000.ts +39 -0
- package/src/modules/notifications/subscribers/deliver-notification.ts +175 -0
- package/src/modules/notifications/workers/create-notification.worker.ts +122 -0
- package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +2 -2
- package/src/modules/query_index/components/QueryIndexesTable.tsx +8 -2
- package/src/modules/resources/backend/resources/resource-types/page.tsx +2 -2
- package/src/modules/resources/backend/resources/resources/page.tsx +2 -2
- package/src/modules/sales/backend/sales/channels/offers/page.tsx +2 -0
- package/src/modules/sales/backend/sales/channels/page.tsx +2 -0
- package/src/modules/sales/commands/documents.ts +65 -0
- package/src/modules/sales/commands/payments.ts +33 -0
- package/src/modules/sales/components/AdjustmentKindSettings.tsx +2 -2
- package/src/modules/sales/components/PaymentMethodsSettings.tsx +2 -2
- package/src/modules/sales/components/ShippingMethodsSettings.tsx +2 -2
- package/src/modules/sales/components/TaxRatesSettings.tsx +2 -2
- package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +2 -0
- package/src/modules/sales/components/documents/AdjustmentsSection.tsx +2 -0
- package/src/modules/sales/components/documents/PaymentsSection.tsx +2 -1
- package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +2 -0
- package/src/modules/sales/i18n/de.json +20 -0
- package/src/modules/sales/i18n/en.json +25 -1
- package/src/modules/sales/i18n/es.json +20 -0
- package/src/modules/sales/i18n/pl.json +20 -0
- package/src/modules/sales/notifications.client.ts +65 -0
- package/src/modules/sales/notifications.ts +82 -0
- package/src/modules/sales/subscribers/quote-expiring-notification.ts +53 -0
- package/src/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.tsx +156 -0
- package/src/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.tsx +156 -0
- package/src/modules/sales/widgets/notifications/index.ts +2 -0
- package/src/modules/sales/widgets/notifications/useSalesDocumentTotals.ts +81 -0
- package/src/modules/staff/backend/staff/team-members/page.tsx +1 -1
- package/src/modules/staff/backend/staff/team-roles/page.tsx +2 -2
- package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +2 -2
- package/src/modules/staff/backend/staff/teams/page.tsx +2 -2
- package/src/modules/staff/commands/leave-requests.ts +94 -0
- package/src/modules/staff/i18n/de.json +4 -0
- package/src/modules/staff/i18n/en.json +9 -1
- package/src/modules/staff/i18n/es.json +4 -0
- package/src/modules/staff/i18n/pl.json +4 -0
- package/src/modules/staff/notifications.ts +71 -0
- package/src/modules/workflows/backend/definitions/page.tsx +5 -0
- package/src/modules/workflows/backend/instances/page.tsx +4 -1
- package/src/modules/workflows/backend/tasks/page.tsx +4 -1
- package/src/modules/workflows/i18n/en.json +3 -1
- package/src/modules/workflows/notifications.ts +25 -0
- package/src/modules/workflows/subscribers/task-assigned-notification.ts +53 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"notifications.nav.title": "Notifications",
|
|
3
|
+
"notifications.title": "Notifications",
|
|
4
|
+
"notifications.empty": "No notifications",
|
|
5
|
+
"notifications.markAllRead": "Mark all read",
|
|
6
|
+
"notifications.actionTaken": "Action taken: {action}",
|
|
7
|
+
"notifications.filters.all": "All",
|
|
8
|
+
"notifications.filters.unread": "Unread",
|
|
9
|
+
"notifications.filters.actionRequired": "Action Required",
|
|
10
|
+
"notifications.actions.dismiss": "Dismiss",
|
|
11
|
+
"notifications.actions.undo": "Undo",
|
|
12
|
+
"notifications.actions.markRead": "Mark as read",
|
|
13
|
+
"notifications.toast.new": "New notification",
|
|
14
|
+
"notifications.toast.dismissed": "Notification dismissed",
|
|
15
|
+
"notifications.toast.actionSuccess": "Action completed successfully",
|
|
16
|
+
"notifications.toast.actionError": "Failed to execute action",
|
|
17
|
+
"notifications.error.load": "Failed to load notifications",
|
|
18
|
+
"notifications.error.action": "Failed to execute action",
|
|
19
|
+
"notifications.badge.unread": "{count} unread notifications",
|
|
20
|
+
"notifications.delivery.email.preview": "New notification",
|
|
21
|
+
"notifications.delivery.email.heading": "You have a new notification",
|
|
22
|
+
"notifications.delivery.email.bodyIntro": "Review the notification details and take any required actions.",
|
|
23
|
+
"notifications.delivery.email.actionNotice": "Actions are available in Open Mercato and are read-only in this email.",
|
|
24
|
+
"notifications.delivery.email.openCta": "Open notification center",
|
|
25
|
+
"notifications.delivery.email.footer": "Open Mercato notifications",
|
|
26
|
+
"notifications.delivery.settings.invalid": "Invalid delivery settings",
|
|
27
|
+
"notifications.settings.pageTitle": "Notification Delivery",
|
|
28
|
+
"notifications.settings.pageDescription": "Configure delivery strategies for in-app notifications.",
|
|
29
|
+
"notifications.settings.loadError": "Failed to load notification settings",
|
|
30
|
+
"notifications.settings.saveError": "Failed to save notification settings",
|
|
31
|
+
"notifications.settings.saveSuccess": "Notification settings saved",
|
|
32
|
+
"notifications.settings.loading": "Loading notification settings...",
|
|
33
|
+
"notifications.settings.saving": "Saving...",
|
|
34
|
+
"notifications.settings.save": "Save settings",
|
|
35
|
+
"notifications.settings.core.title": "Core delivery",
|
|
36
|
+
"notifications.settings.core.description": "Control the default notification center and panel link used by external channels.",
|
|
37
|
+
"notifications.settings.core.appUrl": "Application URL",
|
|
38
|
+
"notifications.settings.core.appUrlHint": "Used to build absolute links in email notifications.",
|
|
39
|
+
"notifications.settings.core.panelPath": "Notification panel path",
|
|
40
|
+
"notifications.settings.core.panelPathHint": "Relative path for the read-only notification panel.",
|
|
41
|
+
"notifications.settings.core.databaseLabel": "In-app notifications",
|
|
42
|
+
"notifications.settings.core.databaseHint": "Store notifications in the database for the panel and bell.",
|
|
43
|
+
"notifications.settings.email.title": "Email strategy",
|
|
44
|
+
"notifications.settings.email.description": "Send notification copies via Resend using React templates.",
|
|
45
|
+
"notifications.settings.email.enabledLabel": "Enable email delivery",
|
|
46
|
+
"notifications.settings.email.enabledHint": "Email actions are read-only and link back to the notification center.",
|
|
47
|
+
"notifications.settings.email.from": "From address",
|
|
48
|
+
"notifications.settings.email.replyTo": "Reply-to",
|
|
49
|
+
"notifications.settings.email.subjectPrefix": "Subject prefix"
|
|
50
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"notifications.nav.title": "Notificaciones",
|
|
3
|
+
"notifications.title": "Notificaciones",
|
|
4
|
+
"notifications.empty": "Sin notificaciones",
|
|
5
|
+
"notifications.markAllRead": "Marcar todo como leído",
|
|
6
|
+
"notifications.actionTaken": "Acción realizada: {action}",
|
|
7
|
+
"notifications.filters.all": "Todas",
|
|
8
|
+
"notifications.filters.unread": "No leídas",
|
|
9
|
+
"notifications.filters.actionRequired": "Acción requerida",
|
|
10
|
+
"notifications.actions.dismiss": "Descartar",
|
|
11
|
+
"notifications.actions.undo": "Deshacer",
|
|
12
|
+
"notifications.actions.markRead": "Marcar como leída",
|
|
13
|
+
"notifications.toast.new": "Nueva notificación",
|
|
14
|
+
"notifications.toast.dismissed": "Notificación descartada",
|
|
15
|
+
"notifications.toast.actionSuccess": "Acción completada correctamente",
|
|
16
|
+
"notifications.toast.actionError": "No se pudo ejecutar la acción",
|
|
17
|
+
"notifications.error.load": "No se pudieron cargar las notificaciones",
|
|
18
|
+
"notifications.error.action": "No se pudo ejecutar la acción",
|
|
19
|
+
"notifications.badge.unread": "{count} notificaciones no leídas",
|
|
20
|
+
"notifications.delivery.email.preview": "Nueva notificación",
|
|
21
|
+
"notifications.delivery.email.heading": "Tienes una nueva notificación",
|
|
22
|
+
"notifications.delivery.email.bodyIntro": "Revisa los detalles de la notificación y realiza las acciones necesarias.",
|
|
23
|
+
"notifications.delivery.email.actionNotice": "Las acciones están disponibles en Open Mercato y son de solo lectura en este correo.",
|
|
24
|
+
"notifications.delivery.email.openCta": "Abrir centro de notificaciones",
|
|
25
|
+
"notifications.delivery.email.footer": "Notificaciones de Open Mercato",
|
|
26
|
+
"notifications.delivery.settings.invalid": "Configuración de entrega no válida",
|
|
27
|
+
"notifications.settings.pageTitle": "Entrega de notificaciones",
|
|
28
|
+
"notifications.settings.pageDescription": "Configura las estrategias de entrega para las notificaciones en la app.",
|
|
29
|
+
"notifications.settings.loadError": "No se pudieron cargar los ajustes de notificaciones",
|
|
30
|
+
"notifications.settings.saveError": "No se pudieron guardar los ajustes de notificaciones",
|
|
31
|
+
"notifications.settings.saveSuccess": "Ajustes de notificaciones guardados",
|
|
32
|
+
"notifications.settings.loading": "Cargando ajustes de notificaciones...",
|
|
33
|
+
"notifications.settings.saving": "Guardando...",
|
|
34
|
+
"notifications.settings.save": "Guardar ajustes",
|
|
35
|
+
"notifications.settings.core.title": "Entrega principal",
|
|
36
|
+
"notifications.settings.core.description": "Controla el centro de notificaciones y el enlace del panel para canales externos.",
|
|
37
|
+
"notifications.settings.core.appUrl": "URL de la aplicación",
|
|
38
|
+
"notifications.settings.core.appUrlHint": "Se usa para construir enlaces absolutos en notificaciones por email.",
|
|
39
|
+
"notifications.settings.core.panelPath": "Ruta del panel de notificaciones",
|
|
40
|
+
"notifications.settings.core.panelPathHint": "Ruta relativa para el panel de notificaciones de solo lectura.",
|
|
41
|
+
"notifications.settings.core.databaseLabel": "Notificaciones en la app",
|
|
42
|
+
"notifications.settings.core.databaseHint": "Guarda las notificaciones en la base de datos para el panel y la campana.",
|
|
43
|
+
"notifications.settings.email.title": "Estrategia de email",
|
|
44
|
+
"notifications.settings.email.description": "Envía copias de notificaciones vía Resend con plantillas React.",
|
|
45
|
+
"notifications.settings.email.enabledLabel": "Habilitar entrega por email",
|
|
46
|
+
"notifications.settings.email.enabledHint": "Las acciones del email son de solo lectura y enlazan al centro de notificaciones.",
|
|
47
|
+
"notifications.settings.email.from": "Dirección del remitente",
|
|
48
|
+
"notifications.settings.email.replyTo": "Responder a",
|
|
49
|
+
"notifications.settings.email.subjectPrefix": "Prefijo de asunto"
|
|
50
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"notifications.nav.title": "Powiadomienia",
|
|
3
|
+
"notifications.title": "Powiadomienia",
|
|
4
|
+
"notifications.empty": "Brak powiadomień",
|
|
5
|
+
"notifications.markAllRead": "Oznacz wszystkie jako przeczytane",
|
|
6
|
+
"notifications.actionTaken": "Wykonana akcja: {action}",
|
|
7
|
+
"notifications.filters.all": "Wszystkie",
|
|
8
|
+
"notifications.filters.unread": "Nieprzeczytane",
|
|
9
|
+
"notifications.filters.actionRequired": "Wymagana akcja",
|
|
10
|
+
"notifications.actions.dismiss": "Odrzuć",
|
|
11
|
+
"notifications.actions.undo": "Cofnij",
|
|
12
|
+
"notifications.actions.markRead": "Oznacz jako przeczytane",
|
|
13
|
+
"notifications.toast.new": "Nowe powiadomienie",
|
|
14
|
+
"notifications.toast.dismissed": "Powiadomienie odrzucone",
|
|
15
|
+
"notifications.toast.actionSuccess": "Akcja wykonana pomyślnie",
|
|
16
|
+
"notifications.toast.actionError": "Nie udało się wykonać akcji",
|
|
17
|
+
"notifications.error.load": "Nie udało się załadować powiadomień",
|
|
18
|
+
"notifications.error.action": "Nie udało się wykonać akcji",
|
|
19
|
+
"notifications.badge.unread": "{count} nieprzeczytanych powiadomień",
|
|
20
|
+
"notifications.delivery.email.preview": "Nowe powiadomienie",
|
|
21
|
+
"notifications.delivery.email.heading": "Masz nowe powiadomienie",
|
|
22
|
+
"notifications.delivery.email.bodyIntro": "Sprawdź szczegóły powiadomienia i wykonaj wymagane działania.",
|
|
23
|
+
"notifications.delivery.email.actionNotice": "Akcje są dostępne w Open Mercato i w tym e-mailu są tylko do odczytu.",
|
|
24
|
+
"notifications.delivery.email.openCta": "Otwórz centrum powiadomień",
|
|
25
|
+
"notifications.delivery.email.footer": "Powiadomienia Open Mercato",
|
|
26
|
+
"notifications.delivery.settings.invalid": "Nieprawidłowe ustawienia dostarczania",
|
|
27
|
+
"notifications.settings.pageTitle": "Dostarczanie powiadomień",
|
|
28
|
+
"notifications.settings.pageDescription": "Skonfiguruj strategie dostarczania powiadomień w aplikacji.",
|
|
29
|
+
"notifications.settings.loadError": "Nie udało się załadować ustawień powiadomień",
|
|
30
|
+
"notifications.settings.saveError": "Nie udało się zapisać ustawień powiadomień",
|
|
31
|
+
"notifications.settings.saveSuccess": "Ustawienia powiadomień zapisane",
|
|
32
|
+
"notifications.settings.loading": "Ładowanie ustawień powiadomień...",
|
|
33
|
+
"notifications.settings.saving": "Zapisywanie...",
|
|
34
|
+
"notifications.settings.save": "Zapisz ustawienia",
|
|
35
|
+
"notifications.settings.core.title": "Podstawowe dostarczanie",
|
|
36
|
+
"notifications.settings.core.description": "Kontroluj centrum powiadomień i link panelu dla kanałów zewnętrznych.",
|
|
37
|
+
"notifications.settings.core.appUrl": "URL aplikacji",
|
|
38
|
+
"notifications.settings.core.appUrlHint": "Służy do budowania bezwzględnych linków w powiadomieniach e-mail.",
|
|
39
|
+
"notifications.settings.core.panelPath": "Ścieżka panelu powiadomień",
|
|
40
|
+
"notifications.settings.core.panelPathHint": "Względna ścieżka do panelu powiadomień tylko do odczytu.",
|
|
41
|
+
"notifications.settings.core.databaseLabel": "Powiadomienia w aplikacji",
|
|
42
|
+
"notifications.settings.core.databaseHint": "Zapisuje powiadomienia w bazie danych dla panelu i dzwonka.",
|
|
43
|
+
"notifications.settings.email.title": "Strategia e-mail",
|
|
44
|
+
"notifications.settings.email.description": "Wysyłaj kopie powiadomień przez Resend z szablonami React.",
|
|
45
|
+
"notifications.settings.email.enabledLabel": "Włącz dostarczanie e-mail",
|
|
46
|
+
"notifications.settings.email.enabledHint": "Akcje w e-mailu są tylko do odczytu i prowadzą do centrum powiadomień.",
|
|
47
|
+
"notifications.settings.email.from": "Adres nadawcy",
|
|
48
|
+
"notifications.settings.email.replyTo": "Odpowiedz do",
|
|
49
|
+
"notifications.settings.email.subjectPrefix": "Prefiks tematu"
|
|
50
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ModuleInfo } from '@open-mercato/shared/modules/registry'
|
|
2
|
+
|
|
3
|
+
export const metadata: ModuleInfo = {
|
|
4
|
+
name: 'notifications',
|
|
5
|
+
title: 'Notifications',
|
|
6
|
+
version: '0.1.0',
|
|
7
|
+
description: 'In-app notifications with module-extensible types and actions.',
|
|
8
|
+
author: 'Open Mercato Team',
|
|
9
|
+
license: 'Proprietary',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { features } from './acl'
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { ModuleConfigService } from '@open-mercato/core/modules/configs/lib/module-config-service'
|
|
2
|
+
import { parseBooleanWithDefault } from '@open-mercato/shared/lib/boolean'
|
|
3
|
+
import { notificationDeliveryConfigSchema, type NotificationDeliveryConfigInput } from '../data/validators'
|
|
4
|
+
|
|
5
|
+
export const NOTIFICATIONS_DELIVERY_CONFIG_KEY = 'delivery_strategies'
|
|
6
|
+
|
|
7
|
+
export type NotificationDeliveryStrategyState = {
|
|
8
|
+
enabled: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type NotificationEmailDeliveryConfig = NotificationDeliveryStrategyState & {
|
|
12
|
+
from?: string
|
|
13
|
+
replyTo?: string
|
|
14
|
+
subjectPrefix?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type NotificationDeliveryConfig = {
|
|
18
|
+
appUrl?: string
|
|
19
|
+
panelPath: string
|
|
20
|
+
strategies: {
|
|
21
|
+
database: NotificationDeliveryStrategyState
|
|
22
|
+
email: NotificationEmailDeliveryConfig
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const envString = (value: string | undefined | null) => {
|
|
27
|
+
if (!value) return undefined
|
|
28
|
+
const trimmed = value.trim()
|
|
29
|
+
return trimmed.length ? trimmed : undefined
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const resolveEnvDefaults = () => {
|
|
33
|
+
const appUrl = envString(
|
|
34
|
+
process.env.NOTIFICATIONS_APP_URL ||
|
|
35
|
+
process.env.APPLICATION_URL ||
|
|
36
|
+
process.env.NEXT_PUBLIC_APP_URL ||
|
|
37
|
+
process.env.APP_URL
|
|
38
|
+
)
|
|
39
|
+
const panelPath = envString(process.env.NOTIFICATIONS_PANEL_PATH)
|
|
40
|
+
const emailEnabled = parseBooleanWithDefault(process.env.NOTIFICATIONS_EMAIL_ENABLED, true)
|
|
41
|
+
const emailFrom = envString(process.env.NOTIFICATIONS_EMAIL_FROM || process.env.EMAIL_FROM)
|
|
42
|
+
const emailReplyTo = envString(process.env.NOTIFICATIONS_EMAIL_REPLY_TO || process.env.ADMIN_EMAIL)
|
|
43
|
+
const emailSubjectPrefix = envString(process.env.NOTIFICATIONS_EMAIL_SUBJECT_PREFIX)
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
appUrl,
|
|
47
|
+
panelPath,
|
|
48
|
+
emailEnabled,
|
|
49
|
+
emailFrom,
|
|
50
|
+
emailReplyTo,
|
|
51
|
+
emailSubjectPrefix,
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const DEFAULT_NOTIFICATION_DELIVERY_CONFIG: NotificationDeliveryConfig = (() => {
|
|
56
|
+
const env = resolveEnvDefaults()
|
|
57
|
+
return {
|
|
58
|
+
appUrl: env.appUrl,
|
|
59
|
+
panelPath: env.panelPath ?? '/backend/notifications',
|
|
60
|
+
strategies: {
|
|
61
|
+
database: { enabled: true },
|
|
62
|
+
email: {
|
|
63
|
+
enabled: env.emailEnabled,
|
|
64
|
+
from: env.emailFrom,
|
|
65
|
+
replyTo: env.emailReplyTo,
|
|
66
|
+
subjectPrefix: env.emailSubjectPrefix,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
})()
|
|
71
|
+
|
|
72
|
+
const normalizeDeliveryConfig = (input?: unknown | null): NotificationDeliveryConfig => {
|
|
73
|
+
const parsed = notificationDeliveryConfigSchema.safeParse(input ?? {})
|
|
74
|
+
if (!parsed.success) {
|
|
75
|
+
return { ...DEFAULT_NOTIFICATION_DELIVERY_CONFIG }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const value = parsed.data ?? {}
|
|
79
|
+
const strategies = value.strategies ?? {}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
appUrl: value.appUrl,
|
|
83
|
+
panelPath: value.panelPath ?? DEFAULT_NOTIFICATION_DELIVERY_CONFIG.panelPath,
|
|
84
|
+
strategies: {
|
|
85
|
+
database: {
|
|
86
|
+
enabled: DEFAULT_NOTIFICATION_DELIVERY_CONFIG.strategies.database.enabled,
|
|
87
|
+
},
|
|
88
|
+
email: {
|
|
89
|
+
enabled: strategies.email?.enabled ?? DEFAULT_NOTIFICATION_DELIVERY_CONFIG.strategies.email.enabled,
|
|
90
|
+
from: strategies.email?.from,
|
|
91
|
+
replyTo: strategies.email?.replyTo,
|
|
92
|
+
subjectPrefix: strategies.email?.subjectPrefix,
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
type Resolver = {
|
|
99
|
+
resolve: <T = unknown>(name: string) => T
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function resolveNotificationDeliveryConfig(
|
|
103
|
+
resolver: Resolver,
|
|
104
|
+
options?: { defaultValue?: NotificationDeliveryConfig }
|
|
105
|
+
): Promise<NotificationDeliveryConfig> {
|
|
106
|
+
const fallback = options?.defaultValue ?? DEFAULT_NOTIFICATION_DELIVERY_CONFIG
|
|
107
|
+
let service: ModuleConfigService
|
|
108
|
+
try {
|
|
109
|
+
service = resolver.resolve<ModuleConfigService>('moduleConfigService')
|
|
110
|
+
} catch {
|
|
111
|
+
return { ...fallback }
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
const value = await service.getValue('notifications', NOTIFICATIONS_DELIVERY_CONFIG_KEY, { defaultValue: fallback })
|
|
115
|
+
return normalizeDeliveryConfig(value)
|
|
116
|
+
} catch {
|
|
117
|
+
return { ...fallback }
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export async function saveNotificationDeliveryConfig(
|
|
122
|
+
resolver: Resolver,
|
|
123
|
+
config: NotificationDeliveryConfigInput
|
|
124
|
+
): Promise<void> {
|
|
125
|
+
let service: ModuleConfigService
|
|
126
|
+
try {
|
|
127
|
+
service = resolver.resolve<ModuleConfigService>('moduleConfigService')
|
|
128
|
+
} catch {
|
|
129
|
+
throw new Error('Configuration service unavailable')
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const normalized = normalizeDeliveryConfig(config)
|
|
133
|
+
await service.setValue('notifications', NOTIFICATIONS_DELIVERY_CONFIG_KEY, normalized)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function resolveNotificationPanelUrl(config: NotificationDeliveryConfig): string | null {
|
|
137
|
+
const base = config.appUrl
|
|
138
|
+
|| process.env.APPLICATION_URL
|
|
139
|
+
|| process.env.NEXT_PUBLIC_APP_URL
|
|
140
|
+
|| process.env.APP_URL
|
|
141
|
+
if (!base || !base.trim()) {
|
|
142
|
+
return config.panelPath
|
|
143
|
+
}
|
|
144
|
+
return `${base.replace(/\/$/, '')}${config.panelPath}`
|
|
145
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export const NOTIFICATION_EVENTS = {
|
|
2
|
+
CREATED: 'notifications.created',
|
|
3
|
+
READ: 'notifications.read',
|
|
4
|
+
ACTIONED: 'notifications.actioned',
|
|
5
|
+
DISMISSED: 'notifications.dismissed',
|
|
6
|
+
RESTORED: 'notifications.restored',
|
|
7
|
+
EXPIRED: 'notifications.expired',
|
|
8
|
+
} as const
|
|
9
|
+
|
|
10
|
+
export type NotificationCreatedPayload = {
|
|
11
|
+
notificationId: string
|
|
12
|
+
recipientUserId: string
|
|
13
|
+
type: string
|
|
14
|
+
title: string
|
|
15
|
+
tenantId: string
|
|
16
|
+
organizationId?: string | null
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type NotificationReadPayload = {
|
|
20
|
+
notificationId: string
|
|
21
|
+
userId: string
|
|
22
|
+
tenantId: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type NotificationActionedPayload = {
|
|
26
|
+
notificationId: string
|
|
27
|
+
actionId: string
|
|
28
|
+
userId: string
|
|
29
|
+
tenantId: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type NotificationDismissedPayload = {
|
|
33
|
+
notificationId: string
|
|
34
|
+
userId: string
|
|
35
|
+
tenantId: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type NotificationRestoredPayload = {
|
|
39
|
+
notificationId: string
|
|
40
|
+
userId: string
|
|
41
|
+
tenantId: string
|
|
42
|
+
status: 'read' | 'unread'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type NotificationExpiredPayload = {
|
|
46
|
+
notificationIds: string[]
|
|
47
|
+
tenantId: string
|
|
48
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { NotificationTypeDefinition, NotificationTypeAction } from '@open-mercato/shared/modules/notifications/types'
|
|
2
|
+
import type { CreateNotificationInput, CreateBatchNotificationInput, CreateRoleNotificationInput, CreateFeatureNotificationInput } from '../data/validators'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Transform type definition actions to API input actions.
|
|
6
|
+
* Type definitions use labelKey (i18n-first), while API input uses label with optional labelKey.
|
|
7
|
+
*/
|
|
8
|
+
function mapActions(actions: NotificationTypeAction[] | undefined) {
|
|
9
|
+
if (!actions || actions.length === 0) return undefined
|
|
10
|
+
return actions.map((action) => ({
|
|
11
|
+
id: action.id,
|
|
12
|
+
label: action.labelKey,
|
|
13
|
+
labelKey: action.labelKey,
|
|
14
|
+
variant: action.variant,
|
|
15
|
+
icon: action.icon,
|
|
16
|
+
commandId: action.commandId,
|
|
17
|
+
href: action.href,
|
|
18
|
+
confirmRequired: action.confirmRequired,
|
|
19
|
+
confirmMessage: action.confirmMessageKey,
|
|
20
|
+
}))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Common options for building notifications from type definitions
|
|
25
|
+
*/
|
|
26
|
+
interface CommonBuildOptions {
|
|
27
|
+
titleVariables?: Record<string, string>
|
|
28
|
+
bodyVariables?: Record<string, string>
|
|
29
|
+
sourceEntityType?: string
|
|
30
|
+
sourceEntityId?: string
|
|
31
|
+
linkHref?: string
|
|
32
|
+
groupKey?: string
|
|
33
|
+
expiresAt?: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Build base notification fields from a type definition.
|
|
38
|
+
* Shared logic used by all notification builder functions.
|
|
39
|
+
*/
|
|
40
|
+
function buildBaseNotificationFields(
|
|
41
|
+
typeDef: NotificationTypeDefinition,
|
|
42
|
+
options: CommonBuildOptions
|
|
43
|
+
) {
|
|
44
|
+
return {
|
|
45
|
+
type: typeDef.type,
|
|
46
|
+
titleKey: typeDef.titleKey,
|
|
47
|
+
bodyKey: typeDef.bodyKey,
|
|
48
|
+
titleVariables: options.titleVariables,
|
|
49
|
+
bodyVariables: options.bodyVariables,
|
|
50
|
+
title: typeDef.titleKey,
|
|
51
|
+
body: typeDef.bodyKey,
|
|
52
|
+
icon: typeDef.icon,
|
|
53
|
+
severity: typeDef.severity,
|
|
54
|
+
actions: mapActions(typeDef.actions),
|
|
55
|
+
primaryActionId: typeDef.primaryActionId,
|
|
56
|
+
sourceModule: typeDef.module,
|
|
57
|
+
sourceEntityType: options.sourceEntityType,
|
|
58
|
+
sourceEntityId: options.sourceEntityId,
|
|
59
|
+
linkHref: options.linkHref ?? typeDef.linkHref,
|
|
60
|
+
groupKey: options.groupKey,
|
|
61
|
+
expiresAt: options.expiresAt ?? (
|
|
62
|
+
typeDef.expiresAfterHours
|
|
63
|
+
? new Date(Date.now() + typeDef.expiresAfterHours * 60 * 60 * 1000).toISOString()
|
|
64
|
+
: undefined
|
|
65
|
+
),
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Build a notification input from a type definition with i18n support.
|
|
71
|
+
* This is the recommended way to create notifications - use type definitions from notifications.ts
|
|
72
|
+
* to ensure i18n-first approach.
|
|
73
|
+
*/
|
|
74
|
+
export function buildNotificationFromType(
|
|
75
|
+
typeDef: NotificationTypeDefinition,
|
|
76
|
+
options: CommonBuildOptions & { recipientUserId: string }
|
|
77
|
+
): CreateNotificationInput {
|
|
78
|
+
return {
|
|
79
|
+
recipientUserId: options.recipientUserId,
|
|
80
|
+
...buildBaseNotificationFields(typeDef, options),
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Build a batch notification input from a type definition
|
|
86
|
+
*/
|
|
87
|
+
export function buildBatchNotificationFromType(
|
|
88
|
+
typeDef: NotificationTypeDefinition,
|
|
89
|
+
options: CommonBuildOptions & { recipientUserIds: string[] }
|
|
90
|
+
): CreateBatchNotificationInput {
|
|
91
|
+
return {
|
|
92
|
+
recipientUserIds: options.recipientUserIds,
|
|
93
|
+
...buildBaseNotificationFields(typeDef, options),
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Build a role notification input from a type definition
|
|
99
|
+
*/
|
|
100
|
+
export function buildRoleNotificationFromType(
|
|
101
|
+
typeDef: NotificationTypeDefinition,
|
|
102
|
+
options: CommonBuildOptions & { roleId: string }
|
|
103
|
+
): CreateRoleNotificationInput {
|
|
104
|
+
return {
|
|
105
|
+
roleId: options.roleId,
|
|
106
|
+
...buildBaseNotificationFields(typeDef, options),
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Build a feature-based notification input from a type definition
|
|
112
|
+
*/
|
|
113
|
+
export function buildFeatureNotificationFromType(
|
|
114
|
+
typeDef: NotificationTypeDefinition,
|
|
115
|
+
options: CommonBuildOptions & { requiredFeature: string }
|
|
116
|
+
): CreateFeatureNotificationInput {
|
|
117
|
+
return {
|
|
118
|
+
requiredFeature: options.requiredFeature,
|
|
119
|
+
...buildBaseNotificationFields(typeDef, options),
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/core'
|
|
2
|
+
import { Notification } from '../data/entities'
|
|
3
|
+
import type { CreateNotificationInput } from '../data/validators'
|
|
4
|
+
import { NOTIFICATION_EVENTS } from './events'
|
|
5
|
+
import { assertSafeNotificationHref, sanitizeNotificationActions } from './safeHref'
|
|
6
|
+
|
|
7
|
+
export type NotificationContentInput = Omit<CreateNotificationInput, 'recipientUserId'>
|
|
8
|
+
|
|
9
|
+
export type NotificationTenantContext = {
|
|
10
|
+
tenantId: string
|
|
11
|
+
organizationId?: string | null
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function buildNotificationEntity(
|
|
15
|
+
em: EntityManager,
|
|
16
|
+
input: NotificationContentInput,
|
|
17
|
+
recipientUserId: string,
|
|
18
|
+
ctx: NotificationTenantContext
|
|
19
|
+
): Notification {
|
|
20
|
+
const actions = sanitizeNotificationActions(input.actions)
|
|
21
|
+
const linkHref = assertSafeNotificationHref(input.linkHref)
|
|
22
|
+
|
|
23
|
+
return em.create(Notification, {
|
|
24
|
+
recipientUserId,
|
|
25
|
+
type: input.type,
|
|
26
|
+
// i18n-first: store keys and variables for translation at display time
|
|
27
|
+
titleKey: input.titleKey,
|
|
28
|
+
bodyKey: input.bodyKey,
|
|
29
|
+
titleVariables: input.titleVariables,
|
|
30
|
+
bodyVariables: input.bodyVariables,
|
|
31
|
+
// Fallback text (required for backward compatibility)
|
|
32
|
+
title: input.title || input.titleKey || '',
|
|
33
|
+
body: input.body,
|
|
34
|
+
icon: input.icon,
|
|
35
|
+
severity: input.severity ?? 'info',
|
|
36
|
+
actionData: actions
|
|
37
|
+
? {
|
|
38
|
+
actions,
|
|
39
|
+
primaryActionId: input.primaryActionId,
|
|
40
|
+
}
|
|
41
|
+
: null,
|
|
42
|
+
sourceModule: input.sourceModule,
|
|
43
|
+
sourceEntityType: input.sourceEntityType,
|
|
44
|
+
sourceEntityId: input.sourceEntityId,
|
|
45
|
+
linkHref,
|
|
46
|
+
groupKey: input.groupKey,
|
|
47
|
+
expiresAt: input.expiresAt ? new Date(input.expiresAt) : null,
|
|
48
|
+
tenantId: ctx.tenantId,
|
|
49
|
+
organizationId: ctx.organizationId,
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function emitNotificationCreated(
|
|
54
|
+
eventBus: { emit: (event: string, payload: unknown) => Promise<void> },
|
|
55
|
+
notification: Notification,
|
|
56
|
+
ctx: NotificationTenantContext
|
|
57
|
+
): Promise<void> {
|
|
58
|
+
await eventBus.emit(NOTIFICATION_EVENTS.CREATED, {
|
|
59
|
+
notificationId: notification.id,
|
|
60
|
+
recipientUserId: notification.recipientUserId,
|
|
61
|
+
type: notification.type,
|
|
62
|
+
title: notification.title,
|
|
63
|
+
tenantId: ctx.tenantId,
|
|
64
|
+
organizationId: ctx.organizationId,
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function emitNotificationCreatedBatch(
|
|
69
|
+
eventBus: { emit: (event: string, payload: unknown) => Promise<void> },
|
|
70
|
+
notifications: Notification[],
|
|
71
|
+
ctx: NotificationTenantContext
|
|
72
|
+
): Promise<void> {
|
|
73
|
+
for (const notification of notifications) {
|
|
74
|
+
await emitNotificationCreated(eventBus, notification, ctx)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { NotificationDto } from '@open-mercato/shared/modules/notifications/types'
|
|
2
|
+
import { Notification } from '../data/entities'
|
|
3
|
+
|
|
4
|
+
export function toNotificationDto(notification: Notification): NotificationDto {
|
|
5
|
+
return {
|
|
6
|
+
id: notification.id,
|
|
7
|
+
type: notification.type,
|
|
8
|
+
title: notification.title,
|
|
9
|
+
body: notification.body,
|
|
10
|
+
titleKey: notification.titleKey,
|
|
11
|
+
bodyKey: notification.bodyKey,
|
|
12
|
+
titleVariables: notification.titleVariables,
|
|
13
|
+
bodyVariables: notification.bodyVariables,
|
|
14
|
+
icon: notification.icon,
|
|
15
|
+
severity: notification.severity,
|
|
16
|
+
status: notification.status,
|
|
17
|
+
actions: notification.actionData?.actions?.map((action) => ({
|
|
18
|
+
id: action.id,
|
|
19
|
+
label: action.label,
|
|
20
|
+
labelKey: action.labelKey,
|
|
21
|
+
variant: action.variant,
|
|
22
|
+
icon: action.icon,
|
|
23
|
+
})) ?? [],
|
|
24
|
+
primaryActionId: notification.actionData?.primaryActionId,
|
|
25
|
+
sourceModule: notification.sourceModule,
|
|
26
|
+
sourceEntityType: notification.sourceEntityType,
|
|
27
|
+
sourceEntityId: notification.sourceEntityId,
|
|
28
|
+
linkHref: notification.linkHref,
|
|
29
|
+
createdAt: notification.createdAt.toISOString(),
|
|
30
|
+
readAt: notification.readAt?.toISOString() ?? null,
|
|
31
|
+
actionTaken: notification.actionTaken,
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { Knex } from 'knex'
|
|
2
|
+
import { hasFeature } from '@open-mercato/shared/security/features'
|
|
3
|
+
|
|
4
|
+
interface AclRow {
|
|
5
|
+
user_id: string
|
|
6
|
+
features_json: unknown
|
|
7
|
+
is_super_admin: boolean
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function normalizeFeatures(features: unknown): string[] | undefined {
|
|
11
|
+
if (!Array.isArray(features)) return undefined
|
|
12
|
+
const normalized = features.filter((feature): feature is string => typeof feature === 'string')
|
|
13
|
+
return normalized.length ? normalized : undefined
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Extract user IDs from ACL rows that have the required feature or are super admins.
|
|
18
|
+
*/
|
|
19
|
+
function collectUsersWithFeature(
|
|
20
|
+
userIdsSet: Set<string>,
|
|
21
|
+
rows: AclRow[],
|
|
22
|
+
requiredFeature: string
|
|
23
|
+
): void {
|
|
24
|
+
for (const row of rows) {
|
|
25
|
+
if (row.is_super_admin) {
|
|
26
|
+
userIdsSet.add(row.user_id)
|
|
27
|
+
continue
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const features = normalizeFeatures(row.features_json)
|
|
31
|
+
if (features && hasFeature(features, requiredFeature)) {
|
|
32
|
+
userIdsSet.add(row.user_id)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function getRecipientUserIdsForRole(
|
|
38
|
+
knex: Knex,
|
|
39
|
+
tenantId: string,
|
|
40
|
+
roleId: string
|
|
41
|
+
): Promise<string[]> {
|
|
42
|
+
const userRoles = await knex('user_roles')
|
|
43
|
+
.join('users', 'user_roles.user_id', 'users.id')
|
|
44
|
+
.where('user_roles.role_id', roleId)
|
|
45
|
+
.whereNull('user_roles.deleted_at')
|
|
46
|
+
.whereNull('users.deleted_at')
|
|
47
|
+
.where('users.tenant_id', tenantId)
|
|
48
|
+
.select('users.id as user_id')
|
|
49
|
+
|
|
50
|
+
return userRoles.map((row: { user_id: string }) => row.user_id)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function getRecipientUserIdsForFeature(
|
|
54
|
+
knex: Knex,
|
|
55
|
+
tenantId: string,
|
|
56
|
+
requiredFeature: string
|
|
57
|
+
): Promise<string[]> {
|
|
58
|
+
const userIdsSet = new Set<string>()
|
|
59
|
+
|
|
60
|
+
const userAcls = await knex('user_acls')
|
|
61
|
+
.join('users', 'user_acls.user_id', 'users.id')
|
|
62
|
+
.where('user_acls.tenant_id', tenantId)
|
|
63
|
+
.whereNull('user_acls.deleted_at')
|
|
64
|
+
.whereNull('users.deleted_at')
|
|
65
|
+
.where('users.tenant_id', tenantId)
|
|
66
|
+
.select('users.id as user_id', 'user_acls.features_json', 'user_acls.is_super_admin')
|
|
67
|
+
|
|
68
|
+
collectUsersWithFeature(userIdsSet, userAcls, requiredFeature)
|
|
69
|
+
|
|
70
|
+
const roleAcls = await knex('role_acls')
|
|
71
|
+
.join('user_roles', 'role_acls.role_id', 'user_roles.role_id')
|
|
72
|
+
.join('users', 'user_roles.user_id', 'users.id')
|
|
73
|
+
.where('role_acls.tenant_id', tenantId)
|
|
74
|
+
.whereNull('role_acls.deleted_at')
|
|
75
|
+
.whereNull('user_roles.deleted_at')
|
|
76
|
+
.whereNull('users.deleted_at')
|
|
77
|
+
.where('users.tenant_id', tenantId)
|
|
78
|
+
.select('users.id as user_id', 'role_acls.features_json', 'role_acls.is_super_admin')
|
|
79
|
+
|
|
80
|
+
collectUsersWithFeature(userIdsSet, roleAcls, requiredFeature)
|
|
81
|
+
|
|
82
|
+
return Array.from(userIdsSet)
|
|
83
|
+
}
|