@open-mercato/core 0.4.2-canary-10c7a8bf2a → 0.4.2-canary-c84cff7ed5
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 +3 -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/analytics.js +27 -0
- package/dist/modules/catalog/analytics.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/analytics.js +50 -0
- package/dist/modules/customers/analytics.js.map +7 -0
- 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/acl.js +2 -1
- package/dist/modules/dashboards/acl.js.map +2 -2
- package/dist/modules/dashboards/api/widgets/data/route.js +187 -0
- package/dist/modules/dashboards/api/widgets/data/route.js.map +7 -0
- package/dist/modules/dashboards/cli.js +173 -1
- package/dist/modules/dashboards/cli.js.map +2 -2
- package/dist/modules/dashboards/di.js +11 -0
- package/dist/modules/dashboards/di.js.map +7 -0
- package/dist/modules/dashboards/lib/aggregations.js +162 -0
- package/dist/modules/dashboards/lib/aggregations.js.map +7 -0
- package/dist/modules/dashboards/lib/formatters.js +34 -0
- package/dist/modules/dashboards/lib/formatters.js.map +7 -0
- package/dist/modules/dashboards/lib/role-widgets.js +58 -0
- package/dist/modules/dashboards/lib/role-widgets.js.map +7 -0
- package/dist/modules/dashboards/seed/analytics.js +383 -0
- package/dist/modules/dashboards/seed/analytics.js.map +7 -0
- package/dist/modules/dashboards/services/analyticsRegistry.js +52 -0
- package/dist/modules/dashboards/services/analyticsRegistry.js.map +7 -0
- package/dist/modules/dashboards/services/widgetDataService.js +207 -0
- package/dist/modules/dashboards/services/widgetDataService.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/aov-kpi/config.js +18 -0
- package/dist/modules/dashboards/widgets/dashboard/aov-kpi/config.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.client.js +128 -0
- package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.client.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.js +25 -0
- package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/config.js +18 -0
- package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/config.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.client.js +126 -0
- package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.client.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.js +25 -0
- package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-by-status/config.js +18 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-by-status/config.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.js +151 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.js +25 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-kpi/config.js +18 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-kpi/config.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.client.js +126 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.client.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.js +25 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/config.js +16 -0
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/config.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.js +123 -0
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.js +25 -0
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/config.js +18 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/config.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.client.js +128 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.client.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.js +25 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-trend/config.js +21 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-trend/config.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.js +211 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.js +25 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/sales-by-region/config.js +19 -0
- package/dist/modules/dashboards/widgets/dashboard/sales-by-region/config.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.js +131 -0
- package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.js +25 -0
- package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/top-customers/config.js +19 -0
- package/dist/modules/dashboards/widgets/dashboard/top-customers/config.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.client.js +153 -0
- package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.client.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.js +25 -0
- package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/top-products/config.js +22 -0
- package/dist/modules/dashboards/widgets/dashboard/top-products/config.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/top-products/widget.client.js +180 -0
- package/dist/modules/dashboards/widgets/dashboard/top-products/widget.client.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/top-products/widget.js +25 -0
- package/dist/modules/dashboards/widgets/dashboard/top-products/widget.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/analytics.js +67 -0
- package/dist/modules/sales/analytics.js.map +7 -0
- 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 +3 -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/analytics.ts +24 -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/analytics.ts +47 -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/acl.ts +1 -0
- package/src/modules/dashboards/api/widgets/data/route.ts +221 -0
- package/src/modules/dashboards/cli.ts +204 -1
- package/src/modules/dashboards/di.ts +9 -0
- package/src/modules/dashboards/i18n/de.json +115 -1
- package/src/modules/dashboards/i18n/en.json +115 -1
- package/src/modules/dashboards/i18n/es.json +115 -1
- package/src/modules/dashboards/i18n/pl.json +115 -1
- package/src/modules/dashboards/lib/__tests__/aggregations.test.ts +327 -0
- package/src/modules/dashboards/lib/__tests__/formatters.test.ts +128 -0
- package/src/modules/dashboards/lib/aggregations.ts +225 -0
- package/src/modules/dashboards/lib/formatters.ts +36 -0
- package/src/modules/dashboards/lib/role-widgets.ts +80 -0
- package/src/modules/dashboards/seed/analytics.ts +405 -0
- package/src/modules/dashboards/services/analyticsRegistry.ts +79 -0
- package/src/modules/dashboards/services/widgetDataService.ts +329 -0
- package/src/modules/dashboards/widgets/dashboard/aov-kpi/config.ts +20 -0
- package/src/modules/dashboards/widgets/dashboard/aov-kpi/widget.client.tsx +135 -0
- package/src/modules/dashboards/widgets/dashboard/aov-kpi/widget.ts +24 -0
- package/src/modules/dashboards/widgets/dashboard/new-customers-kpi/config.ts +20 -0
- package/src/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.client.tsx +133 -0
- package/src/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.ts +24 -0
- package/src/modules/dashboards/widgets/dashboard/orders-by-status/config.ts +20 -0
- package/src/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.tsx +154 -0
- package/src/modules/dashboards/widgets/dashboard/orders-by-status/widget.ts +24 -0
- package/src/modules/dashboards/widgets/dashboard/orders-kpi/config.ts +20 -0
- package/src/modules/dashboards/widgets/dashboard/orders-kpi/widget.client.tsx +133 -0
- package/src/modules/dashboards/widgets/dashboard/orders-kpi/widget.ts +24 -0
- package/src/modules/dashboards/widgets/dashboard/pipeline-summary/config.ts +17 -0
- package/src/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.tsx +137 -0
- package/src/modules/dashboards/widgets/dashboard/pipeline-summary/widget.ts +24 -0
- package/src/modules/dashboards/widgets/dashboard/revenue-kpi/config.ts +20 -0
- package/src/modules/dashboards/widgets/dashboard/revenue-kpi/widget.client.tsx +135 -0
- package/src/modules/dashboards/widgets/dashboard/revenue-kpi/widget.ts +24 -0
- package/src/modules/dashboards/widgets/dashboard/revenue-trend/config.ts +24 -0
- package/src/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.tsx +220 -0
- package/src/modules/dashboards/widgets/dashboard/revenue-trend/widget.ts +24 -0
- package/src/modules/dashboards/widgets/dashboard/sales-by-region/config.ts +21 -0
- package/src/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.tsx +131 -0
- package/src/modules/dashboards/widgets/dashboard/sales-by-region/widget.ts +24 -0
- package/src/modules/dashboards/widgets/dashboard/top-customers/config.ts +21 -0
- package/src/modules/dashboards/widgets/dashboard/top-customers/widget.client.tsx +161 -0
- package/src/modules/dashboards/widgets/dashboard/top-customers/widget.ts +24 -0
- package/src/modules/dashboards/widgets/dashboard/top-products/config.ts +27 -0
- package/src/modules/dashboards/widgets/dashboard/top-products/widget.client.tsx +181 -0
- package/src/modules/dashboards/widgets/dashboard/top-products/widget.ts +24 -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/analytics.ts +64 -0
- 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,101 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { resolveRequestContext } from "@open-mercato/shared/lib/api/context";
|
|
3
|
+
import { resolveNotificationService } from "./notificationService.js";
|
|
4
|
+
async function resolveNotificationContext(req) {
|
|
5
|
+
const { ctx } = await resolveRequestContext(req);
|
|
6
|
+
return {
|
|
7
|
+
service: resolveNotificationService(ctx.container),
|
|
8
|
+
scope: {
|
|
9
|
+
tenantId: ctx.auth?.tenantId ?? "",
|
|
10
|
+
organizationId: ctx.selectedOrganizationId ?? null,
|
|
11
|
+
userId: ctx.auth?.sub ?? null
|
|
12
|
+
},
|
|
13
|
+
ctx
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function createBulkNotificationRoute(schema, serviceMethod) {
|
|
17
|
+
return async function POST(req) {
|
|
18
|
+
const { service, scope } = await resolveNotificationContext(req);
|
|
19
|
+
const body = await req.json().catch(() => ({}));
|
|
20
|
+
const input = schema.parse(body);
|
|
21
|
+
const notifications = await service[serviceMethod](input, scope);
|
|
22
|
+
return Response.json({
|
|
23
|
+
ok: true,
|
|
24
|
+
count: notifications.length,
|
|
25
|
+
ids: notifications.map((n) => n.id)
|
|
26
|
+
}, { status: 201 });
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function createBulkNotificationOpenApi(schema, summary, description) {
|
|
30
|
+
return {
|
|
31
|
+
POST: {
|
|
32
|
+
summary,
|
|
33
|
+
description,
|
|
34
|
+
tags: ["Notifications"],
|
|
35
|
+
requestBody: {
|
|
36
|
+
required: true,
|
|
37
|
+
content: {
|
|
38
|
+
"application/json": {
|
|
39
|
+
schema
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
responses: {
|
|
44
|
+
201: {
|
|
45
|
+
description: "Notifications created",
|
|
46
|
+
content: {
|
|
47
|
+
"application/json": {
|
|
48
|
+
schema: z.object({
|
|
49
|
+
ok: z.boolean(),
|
|
50
|
+
count: z.number(),
|
|
51
|
+
ids: z.array(z.string().uuid())
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function createSingleNotificationActionRoute(serviceMethod) {
|
|
61
|
+
return async function PUT(req, { params }) {
|
|
62
|
+
const { id } = await params;
|
|
63
|
+
const { service, scope } = await resolveNotificationContext(req);
|
|
64
|
+
await service[serviceMethod](id, scope);
|
|
65
|
+
return Response.json({ ok: true });
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function createSingleNotificationActionOpenApi(summary, description) {
|
|
69
|
+
return {
|
|
70
|
+
PUT: {
|
|
71
|
+
summary,
|
|
72
|
+
tags: ["Notifications"],
|
|
73
|
+
parameters: [
|
|
74
|
+
{
|
|
75
|
+
name: "id",
|
|
76
|
+
in: "path",
|
|
77
|
+
required: true,
|
|
78
|
+
schema: { type: "string", format: "uuid" }
|
|
79
|
+
}
|
|
80
|
+
],
|
|
81
|
+
responses: {
|
|
82
|
+
200: {
|
|
83
|
+
description,
|
|
84
|
+
content: {
|
|
85
|
+
"application/json": {
|
|
86
|
+
schema: z.object({ ok: z.boolean() })
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
export {
|
|
95
|
+
createBulkNotificationOpenApi,
|
|
96
|
+
createBulkNotificationRoute,
|
|
97
|
+
createSingleNotificationActionOpenApi,
|
|
98
|
+
createSingleNotificationActionRoute,
|
|
99
|
+
resolveNotificationContext
|
|
100
|
+
};
|
|
101
|
+
//# sourceMappingURL=routeHelpers.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/notifications/lib/routeHelpers.ts"],
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod'\nimport { resolveRequestContext } from '@open-mercato/shared/lib/api/context'\nimport { resolveNotificationService, type NotificationService } from './notificationService'\n\n/**\n * Notification scope context for service calls\n */\nexport interface NotificationScope {\n tenantId: string\n organizationId: string | null\n userId: string | null\n}\n\n/**\n * Resolved notification context from a request\n */\nexport interface NotificationRequestContext {\n service: NotificationService\n scope: NotificationScope\n ctx: Awaited<ReturnType<typeof resolveRequestContext>>['ctx']\n}\n\n/**\n * Resolve notification service and scope from a request.\n * Centralizes the common pattern used across all notification API routes.\n */\nexport async function resolveNotificationContext(req: Request): Promise<NotificationRequestContext> {\n const { ctx } = await resolveRequestContext(req)\n return {\n service: resolveNotificationService(ctx.container),\n scope: {\n tenantId: ctx.auth?.tenantId ?? '',\n organizationId: ctx.selectedOrganizationId ?? null,\n userId: ctx.auth?.sub ?? null,\n },\n ctx,\n }\n}\n\n/**\n * Create a POST handler for bulk notification creation routes.\n * Used by batch, role, and feature notification endpoints.\n */\nexport function createBulkNotificationRoute<TSchema extends z.ZodTypeAny>(\n schema: TSchema,\n serviceMethod: 'createBatch' | 'createForRole' | 'createForFeature'\n) {\n return async function POST(req: Request) {\n const { service, scope } = await resolveNotificationContext(req)\n\n const body = await req.json().catch(() => ({}))\n const input = schema.parse(body)\n\n const notifications = await service[serviceMethod](input as never, scope)\n\n return Response.json({\n ok: true,\n count: notifications.length,\n ids: notifications.map((n) => n.id),\n }, { status: 201 })\n }\n}\n\n/**\n * Create OpenAPI spec for bulk notification creation routes.\n */\nexport function createBulkNotificationOpenApi<TSchema extends z.ZodTypeAny>(\n schema: TSchema,\n summary: string,\n description?: string\n) {\n return {\n POST: {\n summary,\n description,\n tags: ['Notifications'],\n requestBody: {\n required: true,\n content: {\n 'application/json': {\n schema,\n },\n },\n },\n responses: {\n 201: {\n description: 'Notifications created',\n content: {\n 'application/json': {\n schema: z.object({\n ok: z.boolean(),\n count: z.number(),\n ids: z.array(z.string().uuid()),\n }),\n },\n },\n },\n },\n },\n }\n}\n\n/**\n * Create a PUT handler for single notification action routes.\n * Used by read and dismiss endpoints.\n */\nexport function createSingleNotificationActionRoute(\n serviceMethod: 'markAsRead' | 'dismiss'\n) {\n return async function PUT(req: Request, { params }: { params: Promise<{ id: string }> }) {\n const { id } = await params\n const { service, scope } = await resolveNotificationContext(req)\n\n await service[serviceMethod](id, scope)\n\n return Response.json({ ok: true })\n }\n}\n\n/**\n * Create OpenAPI spec for single notification action routes.\n */\nexport function createSingleNotificationActionOpenApi(\n summary: string,\n description: string\n) {\n return {\n PUT: {\n summary,\n tags: ['Notifications'],\n parameters: [\n {\n name: 'id',\n in: 'path',\n required: true,\n schema: { type: 'string', format: 'uuid' },\n },\n ],\n responses: {\n 200: {\n description,\n content: {\n 'application/json': {\n schema: z.object({ ok: z.boolean() }),\n },\n },\n },\n },\n },\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,6BAA6B;AACtC,SAAS,kCAA4D;AAwBrE,eAAsB,2BAA2B,KAAmD;AAClG,QAAM,EAAE,IAAI,IAAI,MAAM,sBAAsB,GAAG;AAC/C,SAAO;AAAA,IACL,SAAS,2BAA2B,IAAI,SAAS;AAAA,IACjD,OAAO;AAAA,MACL,UAAU,IAAI,MAAM,YAAY;AAAA,MAChC,gBAAgB,IAAI,0BAA0B;AAAA,MAC9C,QAAQ,IAAI,MAAM,OAAO;AAAA,IAC3B;AAAA,IACA;AAAA,EACF;AACF;AAMO,SAAS,4BACd,QACA,eACA;AACA,SAAO,eAAe,KAAK,KAAc;AACvC,UAAM,EAAE,SAAS,MAAM,IAAI,MAAM,2BAA2B,GAAG;AAE/D,UAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9C,UAAM,QAAQ,OAAO,MAAM,IAAI;AAE/B,UAAM,gBAAgB,MAAM,QAAQ,aAAa,EAAE,OAAgB,KAAK;AAExE,WAAO,SAAS,KAAK;AAAA,MACnB,IAAI;AAAA,MACJ,OAAO,cAAc;AAAA,MACrB,KAAK,cAAc,IAAI,CAAC,MAAM,EAAE,EAAE;AAAA,IACpC,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACpB;AACF;AAKO,SAAS,8BACd,QACA,SACA,aACA;AACA,SAAO;AAAA,IACL,MAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA,MAAM,CAAC,eAAe;AAAA,MACtB,aAAa;AAAA,QACX,UAAU;AAAA,QACV,SAAS;AAAA,UACP,oBAAoB;AAAA,YAClB;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA,WAAW;AAAA,QACT,KAAK;AAAA,UACH,aAAa;AAAA,UACb,SAAS;AAAA,YACP,oBAAoB;AAAA,cAClB,QAAQ,EAAE,OAAO;AAAA,gBACf,IAAI,EAAE,QAAQ;AAAA,gBACd,OAAO,EAAE,OAAO;AAAA,gBAChB,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;AAAA,cAChC,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAMO,SAAS,oCACd,eACA;AACA,SAAO,eAAe,IAAI,KAAc,EAAE,OAAO,GAAwC;AACvF,UAAM,EAAE,GAAG,IAAI,MAAM;AACrB,UAAM,EAAE,SAAS,MAAM,IAAI,MAAM,2BAA2B,GAAG;AAE/D,UAAM,QAAQ,aAAa,EAAE,IAAI,KAAK;AAEtC,WAAO,SAAS,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,EACnC;AACF;AAKO,SAAS,sCACd,SACA,aACA;AACA,SAAO;AAAA,IACL,KAAK;AAAA,MACH;AAAA,MACA,MAAM,CAAC,eAAe;AAAA,MACtB,YAAY;AAAA,QACV;AAAA,UACE,MAAM;AAAA,UACN,IAAI;AAAA,UACJ,UAAU;AAAA,UACV,QAAQ,EAAE,MAAM,UAAU,QAAQ,OAAO;AAAA,QAC3C;AAAA,MACF;AAAA,MACA,WAAW;AAAA,QACT,KAAK;AAAA,UACH;AAAA,UACA,SAAS;AAAA,YACP,oBAAoB;AAAA,cAClB,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAAA,YACtC;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
function isSafeNotificationHref(href) {
|
|
2
|
+
return href.startsWith("/") && !href.startsWith("//");
|
|
3
|
+
}
|
|
4
|
+
function assertSafeNotificationHref(href) {
|
|
5
|
+
if (href == null) {
|
|
6
|
+
return void 0;
|
|
7
|
+
}
|
|
8
|
+
if (!isSafeNotificationHref(href)) {
|
|
9
|
+
throw new Error("Notification href must be a same-origin relative path starting with /");
|
|
10
|
+
}
|
|
11
|
+
return href;
|
|
12
|
+
}
|
|
13
|
+
function sanitizeNotificationActions(actions) {
|
|
14
|
+
if (!actions) {
|
|
15
|
+
return void 0;
|
|
16
|
+
}
|
|
17
|
+
return actions.map((action) => action.href ? { ...action, href: assertSafeNotificationHref(action.href) } : action);
|
|
18
|
+
}
|
|
19
|
+
export {
|
|
20
|
+
assertSafeNotificationHref,
|
|
21
|
+
isSafeNotificationHref,
|
|
22
|
+
sanitizeNotificationActions
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=safeHref.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/notifications/lib/safeHref.ts"],
|
|
4
|
+
"sourcesContent": ["import type { NotificationAction } from '@open-mercato/shared/modules/notifications/types'\n\nexport function isSafeNotificationHref(href: string): boolean {\n return href.startsWith('/') && !href.startsWith('//')\n}\n\nexport function assertSafeNotificationHref(href: string | undefined | null): string | undefined {\n if (href == null) {\n return undefined\n }\n\n if (!isSafeNotificationHref(href)) {\n throw new Error('Notification href must be a same-origin relative path starting with /')\n }\n\n return href\n}\n\nexport function sanitizeNotificationActions(\n actions: NotificationAction[] | undefined\n): NotificationAction[] | undefined {\n if (!actions) {\n return undefined\n }\n\n return actions.map((action) => (\n action.href ? { ...action, href: assertSafeNotificationHref(action.href) } : action\n ))\n}\n"],
|
|
5
|
+
"mappings": "AAEO,SAAS,uBAAuB,MAAuB;AAC5D,SAAO,KAAK,WAAW,GAAG,KAAK,CAAC,KAAK,WAAW,IAAI;AACtD;AAEO,SAAS,2BAA2B,MAAqD;AAC9F,MAAI,QAAQ,MAAM;AAChB,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,uBAAuB,IAAI,GAAG;AACjC,UAAM,IAAI,MAAM,uEAAuE;AAAA,EACzF;AAEA,SAAO;AACT;AAEO,SAAS,4BACd,SACkC;AAClC,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,EACT;AAEA,SAAO,QAAQ,IAAI,CAAC,WAClB,OAAO,OAAO,EAAE,GAAG,QAAQ,MAAM,2BAA2B,OAAO,IAAI,EAAE,IAAI,MAC9E;AACH;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Migration } from "@mikro-orm/migrations";
|
|
2
|
+
class Migration20260123000001 extends Migration {
|
|
3
|
+
async up() {
|
|
4
|
+
this.addSql(`
|
|
5
|
+
CREATE TABLE notifications (
|
|
6
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
7
|
+
|
|
8
|
+
recipient_user_id UUID NOT NULL,
|
|
9
|
+
|
|
10
|
+
type TEXT NOT NULL,
|
|
11
|
+
title TEXT NOT NULL,
|
|
12
|
+
body TEXT,
|
|
13
|
+
icon TEXT,
|
|
14
|
+
severity TEXT NOT NULL DEFAULT 'info',
|
|
15
|
+
|
|
16
|
+
status TEXT NOT NULL DEFAULT 'unread',
|
|
17
|
+
|
|
18
|
+
action_data JSONB,
|
|
19
|
+
action_result JSONB,
|
|
20
|
+
action_taken TEXT,
|
|
21
|
+
|
|
22
|
+
source_module TEXT,
|
|
23
|
+
source_entity_type TEXT,
|
|
24
|
+
source_entity_id UUID,
|
|
25
|
+
link_href TEXT,
|
|
26
|
+
|
|
27
|
+
group_key TEXT,
|
|
28
|
+
|
|
29
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
30
|
+
read_at TIMESTAMPTZ,
|
|
31
|
+
actioned_at TIMESTAMPTZ,
|
|
32
|
+
dismissed_at TIMESTAMPTZ,
|
|
33
|
+
expires_at TIMESTAMPTZ,
|
|
34
|
+
|
|
35
|
+
tenant_id UUID NOT NULL,
|
|
36
|
+
organization_id UUID
|
|
37
|
+
);
|
|
38
|
+
`);
|
|
39
|
+
this.addSql(`
|
|
40
|
+
CREATE INDEX notifications_recipient_status_idx
|
|
41
|
+
ON notifications(recipient_user_id, status, created_at DESC);
|
|
42
|
+
`);
|
|
43
|
+
this.addSql(`
|
|
44
|
+
CREATE INDEX notifications_source_idx
|
|
45
|
+
ON notifications(source_entity_type, source_entity_id)
|
|
46
|
+
WHERE source_entity_id IS NOT NULL;
|
|
47
|
+
`);
|
|
48
|
+
this.addSql(`
|
|
49
|
+
CREATE INDEX notifications_tenant_idx
|
|
50
|
+
ON notifications(tenant_id, organization_id);
|
|
51
|
+
`);
|
|
52
|
+
this.addSql(`
|
|
53
|
+
CREATE INDEX notifications_expires_idx
|
|
54
|
+
ON notifications(expires_at)
|
|
55
|
+
WHERE expires_at IS NOT NULL AND status NOT IN ('actioned', 'dismissed');
|
|
56
|
+
`);
|
|
57
|
+
this.addSql(`
|
|
58
|
+
CREATE INDEX notifications_group_idx
|
|
59
|
+
ON notifications(group_key, recipient_user_id)
|
|
60
|
+
WHERE group_key IS NOT NULL;
|
|
61
|
+
`);
|
|
62
|
+
}
|
|
63
|
+
async down() {
|
|
64
|
+
this.addSql("DROP TABLE IF EXISTS notifications CASCADE;");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export {
|
|
68
|
+
Migration20260123000001
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=Migration20260123000001.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/notifications/migrations/Migration20260123000001.ts"],
|
|
4
|
+
"sourcesContent": ["import { Migration } from '@mikro-orm/migrations'\n\nexport class Migration20260123000001 extends Migration {\n async up(): Promise<void> {\n this.addSql(`\n CREATE TABLE notifications (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n \n recipient_user_id UUID NOT NULL,\n \n type TEXT NOT NULL,\n title TEXT NOT NULL,\n body TEXT,\n icon TEXT,\n severity TEXT NOT NULL DEFAULT 'info',\n \n status TEXT NOT NULL DEFAULT 'unread',\n \n action_data JSONB,\n action_result JSONB,\n action_taken TEXT,\n \n source_module TEXT,\n source_entity_type TEXT,\n source_entity_id UUID,\n link_href TEXT,\n \n group_key TEXT,\n \n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n read_at TIMESTAMPTZ,\n actioned_at TIMESTAMPTZ,\n dismissed_at TIMESTAMPTZ,\n expires_at TIMESTAMPTZ,\n \n tenant_id UUID NOT NULL,\n organization_id UUID\n );\n `)\n\n this.addSql(`\n CREATE INDEX notifications_recipient_status_idx \n ON notifications(recipient_user_id, status, created_at DESC);\n `)\n\n this.addSql(`\n CREATE INDEX notifications_source_idx \n ON notifications(source_entity_type, source_entity_id) \n WHERE source_entity_id IS NOT NULL;\n `)\n\n this.addSql(`\n CREATE INDEX notifications_tenant_idx \n ON notifications(tenant_id, organization_id);\n `)\n\n this.addSql(`\n CREATE INDEX notifications_expires_idx \n ON notifications(expires_at) \n WHERE expires_at IS NOT NULL AND status NOT IN ('actioned', 'dismissed');\n `)\n\n this.addSql(`\n CREATE INDEX notifications_group_idx \n ON notifications(group_key, recipient_user_id) \n WHERE group_key IS NOT NULL;\n `)\n }\n\n async down(): Promise<void> {\n this.addSql('DROP TABLE IF EXISTS notifications CASCADE;')\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,gCAAgC,UAAU;AAAA,EACrD,MAAM,KAAoB;AACxB,SAAK,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAkCX;AAED,SAAK,OAAO;AAAA;AAAA;AAAA,KAGX;AAED,SAAK,OAAO;AAAA;AAAA;AAAA;AAAA,KAIX;AAED,SAAK,OAAO;AAAA;AAAA;AAAA,KAGX;AAED,SAAK,OAAO;AAAA;AAAA;AAAA;AAAA,KAIX;AAED,SAAK,OAAO;AAAA;AAAA;AAAA;AAAA,KAIX;AAAA,EACH;AAAA,EAEA,MAAM,OAAsB;AAC1B,SAAK,OAAO,6CAA6C;AAAA,EAC3D;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Migration } from "@mikro-orm/migrations";
|
|
2
|
+
class Migration20260126150000 extends Migration {
|
|
3
|
+
async up() {
|
|
4
|
+
this.addSql(`
|
|
5
|
+
alter table "notifications"
|
|
6
|
+
add column if not exists "title_key" text,
|
|
7
|
+
add column if not exists "body_key" text,
|
|
8
|
+
add column if not exists "title_variables" jsonb,
|
|
9
|
+
add column if not exists "body_variables" jsonb;
|
|
10
|
+
`);
|
|
11
|
+
this.addSql(`
|
|
12
|
+
comment on column "notifications"."title_key" is 'i18n key for notification title';
|
|
13
|
+
`);
|
|
14
|
+
this.addSql(`
|
|
15
|
+
comment on column "notifications"."body_key" is 'i18n key for notification body';
|
|
16
|
+
`);
|
|
17
|
+
this.addSql(`
|
|
18
|
+
comment on column "notifications"."title_variables" is 'Variables for i18n interpolation in title';
|
|
19
|
+
`);
|
|
20
|
+
this.addSql(`
|
|
21
|
+
comment on column "notifications"."body_variables" is 'Variables for i18n interpolation in body';
|
|
22
|
+
`);
|
|
23
|
+
}
|
|
24
|
+
async down() {
|
|
25
|
+
this.addSql(`
|
|
26
|
+
alter table "notifications"
|
|
27
|
+
drop column if exists "title_key",
|
|
28
|
+
drop column if exists "body_key",
|
|
29
|
+
drop column if exists "title_variables",
|
|
30
|
+
drop column if exists "body_variables";
|
|
31
|
+
`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export {
|
|
35
|
+
Migration20260126150000
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=Migration20260126150000.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/notifications/migrations/Migration20260126150000.ts"],
|
|
4
|
+
"sourcesContent": ["import { Migration } from '@mikro-orm/migrations'\n\nexport class Migration20260126150000 extends Migration {\n async up(): Promise<void> {\n // Add i18n support fields to notifications table\n this.addSql(`\n alter table \"notifications\"\n add column if not exists \"title_key\" text,\n add column if not exists \"body_key\" text,\n add column if not exists \"title_variables\" jsonb,\n add column if not exists \"body_variables\" jsonb;\n `)\n\n // Add comments for clarity\n this.addSql(`\n comment on column \"notifications\".\"title_key\" is 'i18n key for notification title';\n `)\n this.addSql(`\n comment on column \"notifications\".\"body_key\" is 'i18n key for notification body';\n `)\n this.addSql(`\n comment on column \"notifications\".\"title_variables\" is 'Variables for i18n interpolation in title';\n `)\n this.addSql(`\n comment on column \"notifications\".\"body_variables\" is 'Variables for i18n interpolation in body';\n `)\n }\n\n async down(): Promise<void> {\n // Remove i18n support fields\n this.addSql(`\n alter table \"notifications\"\n drop column if exists \"title_key\",\n drop column if exists \"body_key\",\n drop column if exists \"title_variables\",\n drop column if exists \"body_variables\";\n `)\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,gCAAgC,UAAU;AAAA,EACrD,MAAM,KAAoB;AAExB,SAAK,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAMX;AAGD,SAAK,OAAO;AAAA;AAAA,KAEX;AACD,SAAK,OAAO;AAAA;AAAA,KAEX;AACD,SAAK,OAAO;AAAA;AAAA,KAEX;AACD,SAAK,OAAO;AAAA;AAAA,KAEX;AAAA,EACH;AAAA,EAEA,MAAM,OAAsB;AAE1B,SAAK,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAMX;AAAA,EACH;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { Notification } from "../data/entities.js";
|
|
2
|
+
import { NOTIFICATION_EVENTS } from "../lib/events.js";
|
|
3
|
+
import { DEFAULT_NOTIFICATION_DELIVERY_CONFIG, resolveNotificationDeliveryConfig, resolveNotificationPanelUrl } from "../lib/deliveryConfig.js";
|
|
4
|
+
import { sendEmail } from "@open-mercato/shared/lib/email/send";
|
|
5
|
+
import NotificationEmail from "../emails/NotificationEmail.js";
|
|
6
|
+
import { loadDictionary } from "@open-mercato/shared/lib/i18n/server";
|
|
7
|
+
import { createFallbackTranslator } from "@open-mercato/shared/lib/i18n/translate";
|
|
8
|
+
import { defaultLocale } from "@open-mercato/shared/lib/i18n/config";
|
|
9
|
+
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
10
|
+
import { User } from "../../auth/data/entities.js";
|
|
11
|
+
const metadata = {
|
|
12
|
+
event: NOTIFICATION_EVENTS.CREATED,
|
|
13
|
+
persistent: true,
|
|
14
|
+
id: "notifications:deliver"
|
|
15
|
+
};
|
|
16
|
+
const DEBUG = process.env.NOTIFICATIONS_DEBUG === "true";
|
|
17
|
+
function debug(...args) {
|
|
18
|
+
if (DEBUG) {
|
|
19
|
+
console.log("[notifications]", ...args);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const buildPanelLink = (panelUrl, notificationId) => {
|
|
23
|
+
if (panelUrl.startsWith("http://") || panelUrl.startsWith("https://")) {
|
|
24
|
+
const url = new URL(panelUrl);
|
|
25
|
+
url.searchParams.set("notificationId", notificationId);
|
|
26
|
+
return url.toString();
|
|
27
|
+
}
|
|
28
|
+
const separator = panelUrl.includes("?") ? "&" : "?";
|
|
29
|
+
return `${panelUrl}${separator}notificationId=${encodeURIComponent(notificationId)}`;
|
|
30
|
+
};
|
|
31
|
+
const resolveNotificationCopy = async (notification) => {
|
|
32
|
+
const dict = await loadDictionary(defaultLocale);
|
|
33
|
+
const t = createFallbackTranslator(dict);
|
|
34
|
+
const title = notification.titleKey ? t(notification.titleKey, notification.title ?? notification.titleKey, notification.titleVariables ?? void 0) : notification.title;
|
|
35
|
+
const body = notification.bodyKey ? t(notification.bodyKey, notification.body ?? notification.bodyKey ?? "", notification.bodyVariables ?? void 0) : notification.body ?? null;
|
|
36
|
+
return { title, body, t };
|
|
37
|
+
};
|
|
38
|
+
const resolveRecipient = async (em, notification, encryptionService) => {
|
|
39
|
+
const where = {
|
|
40
|
+
id: notification.recipientUserId,
|
|
41
|
+
tenantId: notification.tenantId,
|
|
42
|
+
deletedAt: null
|
|
43
|
+
};
|
|
44
|
+
if (notification.organizationId) {
|
|
45
|
+
where.organizationId = notification.organizationId;
|
|
46
|
+
}
|
|
47
|
+
const record = await findOneWithDecryption(
|
|
48
|
+
em,
|
|
49
|
+
User,
|
|
50
|
+
where,
|
|
51
|
+
void 0,
|
|
52
|
+
{
|
|
53
|
+
tenantId: notification.tenantId,
|
|
54
|
+
organizationId: notification.organizationId ?? null,
|
|
55
|
+
encryptionService: encryptionService ?? null
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
if (!record) return null;
|
|
59
|
+
return {
|
|
60
|
+
email: typeof record.email === "string" ? record.email : null,
|
|
61
|
+
name: typeof record.name === "string" ? record.name : null
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
async function handle(payload, ctx) {
|
|
65
|
+
debug("deliver notification event", payload);
|
|
66
|
+
const deliveryConfig = await resolveNotificationDeliveryConfig(ctx, { defaultValue: DEFAULT_NOTIFICATION_DELIVERY_CONFIG });
|
|
67
|
+
if (!deliveryConfig.strategies.email.enabled) {
|
|
68
|
+
debug("email delivery disabled");
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const em = ctx.resolve("em");
|
|
72
|
+
const notification = await em.findOne(Notification, {
|
|
73
|
+
id: payload.notificationId,
|
|
74
|
+
tenantId: payload.tenantId,
|
|
75
|
+
organizationId: payload.organizationId ?? null
|
|
76
|
+
});
|
|
77
|
+
if (!notification) {
|
|
78
|
+
debug("notification not found", payload.notificationId);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
let encryptionService = null;
|
|
82
|
+
try {
|
|
83
|
+
encryptionService = ctx.resolve("tenantEncryptionService");
|
|
84
|
+
} catch {
|
|
85
|
+
encryptionService = null;
|
|
86
|
+
}
|
|
87
|
+
const recipient = await resolveRecipient(em, notification, encryptionService);
|
|
88
|
+
if (!recipient?.email) {
|
|
89
|
+
debug("recipient has no email", notification.recipientUserId);
|
|
90
|
+
}
|
|
91
|
+
const { title, body, t } = await resolveNotificationCopy(notification);
|
|
92
|
+
const panelUrl = resolveNotificationPanelUrl(deliveryConfig);
|
|
93
|
+
if (!panelUrl) {
|
|
94
|
+
debug("missing panelUrl; check appUrl/panelPath settings");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const panelLink = buildPanelLink(panelUrl, notification.id);
|
|
98
|
+
const actionLinks = (notification.actionData?.actions ?? []).map((action) => ({
|
|
99
|
+
id: action.id,
|
|
100
|
+
label: action.labelKey ? t(action.labelKey, action.label) : action.label,
|
|
101
|
+
href: panelLink
|
|
102
|
+
}));
|
|
103
|
+
if (deliveryConfig.strategies.email.enabled && recipient?.email) {
|
|
104
|
+
const subjectPrefix = deliveryConfig.strategies.email.subjectPrefix?.trim();
|
|
105
|
+
const subject = subjectPrefix ? `${subjectPrefix} ${title}` : title;
|
|
106
|
+
const copy = {
|
|
107
|
+
preview: t("notifications.delivery.email.preview", "New notification"),
|
|
108
|
+
heading: t("notifications.delivery.email.heading", "You have a new notification"),
|
|
109
|
+
bodyIntro: t("notifications.delivery.email.bodyIntro", "Review the notification details and take any required actions."),
|
|
110
|
+
actionNotice: t("notifications.delivery.email.actionNotice", "Actions are available in Open Mercato and are read-only in this email."),
|
|
111
|
+
openCta: t("notifications.delivery.email.openCta", "Open notification center"),
|
|
112
|
+
footer: t("notifications.delivery.email.footer", "Open Mercato notifications")
|
|
113
|
+
};
|
|
114
|
+
try {
|
|
115
|
+
debug("sending email", { to: recipient.email, from: deliveryConfig.strategies.email.from, subject });
|
|
116
|
+
await sendEmail({
|
|
117
|
+
to: recipient.email,
|
|
118
|
+
subject,
|
|
119
|
+
from: deliveryConfig.strategies.email.from,
|
|
120
|
+
replyTo: deliveryConfig.strategies.email.replyTo,
|
|
121
|
+
react: NotificationEmail({
|
|
122
|
+
title,
|
|
123
|
+
body,
|
|
124
|
+
actions: actionLinks,
|
|
125
|
+
panelUrl: panelLink,
|
|
126
|
+
copy
|
|
127
|
+
})
|
|
128
|
+
});
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error("[notifications] email delivery failed", error);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
export {
|
|
136
|
+
handle as default,
|
|
137
|
+
metadata
|
|
138
|
+
};
|
|
139
|
+
//# sourceMappingURL=deliver-notification.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/notifications/subscribers/deliver-notification.ts"],
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { Notification } from '../data/entities'\nimport { NOTIFICATION_EVENTS } from '../lib/events'\nimport { DEFAULT_NOTIFICATION_DELIVERY_CONFIG, resolveNotificationDeliveryConfig, resolveNotificationPanelUrl } from '../lib/deliveryConfig'\nimport { sendEmail } from '@open-mercato/shared/lib/email/send'\nimport NotificationEmail from '../emails/NotificationEmail'\nimport { loadDictionary } from '@open-mercato/shared/lib/i18n/server'\nimport { createFallbackTranslator } from '@open-mercato/shared/lib/i18n/translate'\nimport { defaultLocale } from '@open-mercato/shared/lib/i18n/config'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { User } from '../../auth/data/entities'\nimport type { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\n\nexport const metadata = {\n event: NOTIFICATION_EVENTS.CREATED,\n persistent: true,\n id: 'notifications:deliver',\n}\n\nconst DEBUG = process.env.NOTIFICATIONS_DEBUG === 'true'\n\nfunction debug(...args: unknown[]): void {\n if (DEBUG) {\n console.log('[notifications]', ...args)\n }\n}\n\ntype NotificationCreatedPayload = {\n notificationId: string\n recipientUserId: string\n tenantId: string\n organizationId?: string | null\n}\n\ntype ResolverContext = {\n resolve: <T = unknown>(name: string) => T\n}\n\nconst buildPanelLink = (panelUrl: string, notificationId: string) => {\n if (panelUrl.startsWith('http://') || panelUrl.startsWith('https://')) {\n const url = new URL(panelUrl)\n url.searchParams.set('notificationId', notificationId)\n return url.toString()\n }\n const separator = panelUrl.includes('?') ? '&' : '?'\n return `${panelUrl}${separator}notificationId=${encodeURIComponent(notificationId)}`\n}\n\nconst resolveNotificationCopy = async (\n notification: Notification\n) => {\n const dict = await loadDictionary(defaultLocale)\n const t = createFallbackTranslator(dict)\n\n const title = notification.titleKey\n ? t(notification.titleKey, notification.title ?? notification.titleKey, notification.titleVariables ?? undefined)\n : notification.title\n\n const body = notification.bodyKey\n ? t(notification.bodyKey, notification.body ?? notification.bodyKey ?? '', notification.bodyVariables ?? undefined)\n : notification.body ?? null\n\n return { title, body, t }\n}\n\nconst resolveRecipient = async (\n em: EntityManager,\n notification: Notification,\n encryptionService?: TenantDataEncryptionService | null,\n) => {\n const where: Partial<User> & { deletedAt?: null } = {\n id: notification.recipientUserId,\n tenantId: notification.tenantId,\n deletedAt: null,\n }\n if (notification.organizationId) {\n where.organizationId = notification.organizationId\n }\n const record = await findOneWithDecryption(\n em,\n User,\n where,\n undefined,\n {\n tenantId: notification.tenantId,\n organizationId: notification.organizationId ?? null,\n encryptionService: encryptionService ?? null,\n },\n )\n if (!record) return null\n return {\n email: typeof record.email === 'string' ? record.email : null,\n name: typeof record.name === 'string' ? record.name : null,\n }\n}\n\n\nexport default async function handle(payload: NotificationCreatedPayload, ctx: ResolverContext) {\n debug('deliver notification event', payload)\n const deliveryConfig = await resolveNotificationDeliveryConfig(ctx, { defaultValue: DEFAULT_NOTIFICATION_DELIVERY_CONFIG })\n if (!deliveryConfig.strategies.email.enabled) {\n debug('email delivery disabled')\n return\n }\n\n const em = ctx.resolve('em') as EntityManager\n const notification = await em.findOne(Notification, {\n id: payload.notificationId,\n tenantId: payload.tenantId,\n organizationId: payload.organizationId ?? null,\n })\n if (!notification) {\n debug('notification not found', payload.notificationId)\n return\n }\n\n let encryptionService: TenantDataEncryptionService | null = null\n try {\n encryptionService = ctx.resolve<TenantDataEncryptionService>('tenantEncryptionService')\n } catch {\n encryptionService = null\n }\n\n const recipient = await resolveRecipient(em, notification, encryptionService)\n if (!recipient?.email) {\n debug('recipient has no email', notification.recipientUserId)\n }\n const { title, body, t } = await resolveNotificationCopy(notification)\n const panelUrl = resolveNotificationPanelUrl(deliveryConfig)\n if (!panelUrl) {\n debug('missing panelUrl; check appUrl/panelPath settings')\n return\n }\n\n const panelLink = buildPanelLink(panelUrl, notification.id)\n const actionLinks = (notification.actionData?.actions ?? []).map((action) => ({\n id: action.id,\n label: action.labelKey ? t(action.labelKey, action.label) : action.label,\n href: panelLink,\n }))\n\n if (deliveryConfig.strategies.email.enabled && recipient?.email) {\n const subjectPrefix = deliveryConfig.strategies.email.subjectPrefix?.trim()\n const subject = subjectPrefix ? `${subjectPrefix} ${title}` : title\n const copy = {\n preview: t('notifications.delivery.email.preview', 'New notification'),\n heading: t('notifications.delivery.email.heading', 'You have a new notification'),\n bodyIntro: t('notifications.delivery.email.bodyIntro', 'Review the notification details and take any required actions.'),\n actionNotice: t('notifications.delivery.email.actionNotice', 'Actions are available in Open Mercato and are read-only in this email.'),\n openCta: t('notifications.delivery.email.openCta', 'Open notification center'),\n footer: t('notifications.delivery.email.footer', 'Open Mercato notifications'),\n }\n\n try {\n debug('sending email', { to: recipient.email, from: deliveryConfig.strategies.email.from, subject })\n await sendEmail({\n to: recipient.email,\n subject,\n from: deliveryConfig.strategies.email.from,\n replyTo: deliveryConfig.strategies.email.replyTo,\n react: NotificationEmail({\n title,\n body,\n actions: actionLinks,\n panelUrl: panelLink,\n copy,\n }),\n })\n } catch (error) {\n console.error('[notifications] email delivery failed', error)\n }\n }\n\n return\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,oBAAoB;AAC7B,SAAS,2BAA2B;AACpC,SAAS,sCAAsC,mCAAmC,mCAAmC;AACrH,SAAS,iBAAiB;AAC1B,OAAO,uBAAuB;AAC9B,SAAS,sBAAsB;AAC/B,SAAS,gCAAgC;AACzC,SAAS,qBAAqB;AAC9B,SAAS,6BAA6B;AACtC,SAAS,YAAY;AAGd,MAAM,WAAW;AAAA,EACtB,OAAO,oBAAoB;AAAA,EAC3B,YAAY;AAAA,EACZ,IAAI;AACN;AAEA,MAAM,QAAQ,QAAQ,IAAI,wBAAwB;AAElD,SAAS,SAAS,MAAuB;AACvC,MAAI,OAAO;AACT,YAAQ,IAAI,mBAAmB,GAAG,IAAI;AAAA,EACxC;AACF;AAaA,MAAM,iBAAiB,CAAC,UAAkB,mBAA2B;AACnE,MAAI,SAAS,WAAW,SAAS,KAAK,SAAS,WAAW,UAAU,GAAG;AACrE,UAAM,MAAM,IAAI,IAAI,QAAQ;AAC5B,QAAI,aAAa,IAAI,kBAAkB,cAAc;AACrD,WAAO,IAAI,SAAS;AAAA,EACtB;AACA,QAAM,YAAY,SAAS,SAAS,GAAG,IAAI,MAAM;AACjD,SAAO,GAAG,QAAQ,GAAG,SAAS,kBAAkB,mBAAmB,cAAc,CAAC;AACpF;AAEA,MAAM,0BAA0B,OAC9B,iBACG;AACH,QAAM,OAAO,MAAM,eAAe,aAAa;AAC/C,QAAM,IAAI,yBAAyB,IAAI;AAEvC,QAAM,QAAQ,aAAa,WACvB,EAAE,aAAa,UAAU,aAAa,SAAS,aAAa,UAAU,aAAa,kBAAkB,MAAS,IAC9G,aAAa;AAEjB,QAAM,OAAO,aAAa,UACtB,EAAE,aAAa,SAAS,aAAa,QAAQ,aAAa,WAAW,IAAI,aAAa,iBAAiB,MAAS,IAChH,aAAa,QAAQ;AAEzB,SAAO,EAAE,OAAO,MAAM,EAAE;AAC1B;AAEA,MAAM,mBAAmB,OACvB,IACA,cACA,sBACG;AACH,QAAM,QAA8C;AAAA,IAClD,IAAI,aAAa;AAAA,IACjB,UAAU,aAAa;AAAA,IACvB,WAAW;AAAA,EACb;AACA,MAAI,aAAa,gBAAgB;AAC/B,UAAM,iBAAiB,aAAa;AAAA,EACtC;AACA,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,MACE,UAAU,aAAa;AAAA,MACvB,gBAAgB,aAAa,kBAAkB;AAAA,MAC/C,mBAAmB,qBAAqB;AAAA,IAC1C;AAAA,EACF;AACA,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO;AAAA,IACL,OAAO,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;AAAA,IACzD,MAAM,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;AAAA,EACxD;AACF;AAGA,eAAO,OAA8B,SAAqC,KAAsB;AAC9F,QAAM,8BAA8B,OAAO;AAC3C,QAAM,iBAAiB,MAAM,kCAAkC,KAAK,EAAE,cAAc,qCAAqC,CAAC;AAC1H,MAAI,CAAC,eAAe,WAAW,MAAM,SAAS;AAC5C,UAAM,yBAAyB;AAC/B;AAAA,EACF;AAEA,QAAM,KAAK,IAAI,QAAQ,IAAI;AAC3B,QAAM,eAAe,MAAM,GAAG,QAAQ,cAAc;AAAA,IAClD,IAAI,QAAQ;AAAA,IACZ,UAAU,QAAQ;AAAA,IAClB,gBAAgB,QAAQ,kBAAkB;AAAA,EAC5C,CAAC;AACD,MAAI,CAAC,cAAc;AACjB,UAAM,0BAA0B,QAAQ,cAAc;AACtD;AAAA,EACF;AAEA,MAAI,oBAAwD;AAC5D,MAAI;AACF,wBAAoB,IAAI,QAAqC,yBAAyB;AAAA,EACxF,QAAQ;AACN,wBAAoB;AAAA,EACtB;AAEA,QAAM,YAAY,MAAM,iBAAiB,IAAI,cAAc,iBAAiB;AAC5E,MAAI,CAAC,WAAW,OAAO;AACrB,UAAM,0BAA0B,aAAa,eAAe;AAAA,EAC9D;AACA,QAAM,EAAE,OAAO,MAAM,EAAE,IAAI,MAAM,wBAAwB,YAAY;AACrE,QAAM,WAAW,4BAA4B,cAAc;AAC3D,MAAI,CAAC,UAAU;AACb,UAAM,mDAAmD;AACzD;AAAA,EACF;AAEA,QAAM,YAAY,eAAe,UAAU,aAAa,EAAE;AAC1D,QAAM,eAAe,aAAa,YAAY,WAAW,CAAC,GAAG,IAAI,CAAC,YAAY;AAAA,IAC5E,IAAI,OAAO;AAAA,IACX,OAAO,OAAO,WAAW,EAAE,OAAO,UAAU,OAAO,KAAK,IAAI,OAAO;AAAA,IACnE,MAAM;AAAA,EACR,EAAE;AAEF,MAAI,eAAe,WAAW,MAAM,WAAW,WAAW,OAAO;AAC/D,UAAM,gBAAgB,eAAe,WAAW,MAAM,eAAe,KAAK;AAC1E,UAAM,UAAU,gBAAgB,GAAG,aAAa,IAAI,KAAK,KAAK;AAC9D,UAAM,OAAO;AAAA,MACX,SAAS,EAAE,wCAAwC,kBAAkB;AAAA,MACrE,SAAS,EAAE,wCAAwC,6BAA6B;AAAA,MAChF,WAAW,EAAE,0CAA0C,gEAAgE;AAAA,MACvH,cAAc,EAAE,6CAA6C,wEAAwE;AAAA,MACrI,SAAS,EAAE,wCAAwC,0BAA0B;AAAA,MAC7E,QAAQ,EAAE,uCAAuC,4BAA4B;AAAA,IAC/E;AAEA,QAAI;AACF,YAAM,iBAAiB,EAAE,IAAI,UAAU,OAAO,MAAM,eAAe,WAAW,MAAM,MAAM,QAAQ,CAAC;AACnG,YAAM,UAAU;AAAA,QACd,IAAI,UAAU;AAAA,QACd;AAAA,QACA,MAAM,eAAe,WAAW,MAAM;AAAA,QACtC,SAAS,eAAe,WAAW,MAAM;AAAA,QACzC,OAAO,kBAAkB;AAAA,UACvB;AAAA,UACA;AAAA,UACA,SAAS;AAAA,UACT,UAAU;AAAA,UACV;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAAA,IACH,SAAS,OAAO;AACd,cAAQ,MAAM,yCAAyC,KAAK;AAAA,IAC9D;AAAA,EACF;AAEA;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { buildNotificationEntity, emitNotificationCreated, emitNotificationCreatedBatch } from "../lib/notificationFactory.js";
|
|
2
|
+
import { getRecipientUserIdsForFeature, getRecipientUserIdsForRole } from "../lib/notificationRecipients.js";
|
|
3
|
+
function getKnex(em) {
|
|
4
|
+
return em.getConnection().getKnex();
|
|
5
|
+
}
|
|
6
|
+
const NOTIFICATIONS_QUEUE_NAME = "notifications";
|
|
7
|
+
const metadata = {
|
|
8
|
+
queue: NOTIFICATIONS_QUEUE_NAME,
|
|
9
|
+
id: "notifications:create",
|
|
10
|
+
concurrency: 5
|
|
11
|
+
};
|
|
12
|
+
async function handle(job, ctx) {
|
|
13
|
+
const { payload } = job;
|
|
14
|
+
if (payload.type === "create") {
|
|
15
|
+
const em = ctx.resolve("em").fork();
|
|
16
|
+
const eventBus = ctx.resolve("eventBus");
|
|
17
|
+
const { input, tenantId, organizationId } = payload;
|
|
18
|
+
const { recipientUserId, ...content } = input;
|
|
19
|
+
const notification = buildNotificationEntity(em, content, recipientUserId, { tenantId, organizationId });
|
|
20
|
+
await em.persistAndFlush(notification);
|
|
21
|
+
await emitNotificationCreated(eventBus, notification, { tenantId, organizationId });
|
|
22
|
+
} else if (payload.type === "create-role") {
|
|
23
|
+
const em = ctx.resolve("em").fork();
|
|
24
|
+
const eventBus = ctx.resolve("eventBus");
|
|
25
|
+
const { input, tenantId, organizationId } = payload;
|
|
26
|
+
const knex = getKnex(em);
|
|
27
|
+
const recipientUserIds = await getRecipientUserIdsForRole(knex, tenantId, input.roleId);
|
|
28
|
+
if (recipientUserIds.length === 0) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const { roleId: _roleId, ...content } = input;
|
|
32
|
+
const notifications = [];
|
|
33
|
+
for (const recipientUserId of recipientUserIds) {
|
|
34
|
+
const notification = buildNotificationEntity(em, content, recipientUserId, { tenantId, organizationId });
|
|
35
|
+
notifications.push(notification);
|
|
36
|
+
}
|
|
37
|
+
await em.persistAndFlush(notifications);
|
|
38
|
+
await emitNotificationCreatedBatch(eventBus, notifications, { tenantId, organizationId });
|
|
39
|
+
} else if (payload.type === "create-feature") {
|
|
40
|
+
const em = ctx.resolve("em").fork();
|
|
41
|
+
const eventBus = ctx.resolve("eventBus");
|
|
42
|
+
const { input, tenantId, organizationId } = payload;
|
|
43
|
+
const knex = getKnex(em);
|
|
44
|
+
const recipientUserIds = await getRecipientUserIdsForFeature(knex, tenantId, input.requiredFeature);
|
|
45
|
+
if (recipientUserIds.length === 0) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const notifications = [];
|
|
49
|
+
const { requiredFeature: _requiredFeature, ...content } = input;
|
|
50
|
+
for (const recipientUserId of recipientUserIds) {
|
|
51
|
+
const notification = buildNotificationEntity(em, content, recipientUserId, { tenantId, organizationId });
|
|
52
|
+
notifications.push(notification);
|
|
53
|
+
}
|
|
54
|
+
await em.persistAndFlush(notifications);
|
|
55
|
+
await emitNotificationCreatedBatch(eventBus, notifications, { tenantId, organizationId });
|
|
56
|
+
} else if (payload.type === "cleanup-expired") {
|
|
57
|
+
const em = ctx.resolve("em").fork();
|
|
58
|
+
const knex = getKnex(em);
|
|
59
|
+
await knex("notifications").where("expires_at", "<", knex.fn.now()).whereNotIn("status", ["actioned", "dismissed"]).update({
|
|
60
|
+
status: "dismissed",
|
|
61
|
+
dismissed_at: knex.fn.now()
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export {
|
|
66
|
+
NOTIFICATIONS_QUEUE_NAME,
|
|
67
|
+
handle as default,
|
|
68
|
+
metadata
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=create-notification.worker.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/notifications/workers/create-notification.worker.ts"],
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/core'\nimport type { Knex } from 'knex'\nimport { Notification } from '../data/entities'\nimport type { CreateNotificationInput, CreateRoleNotificationInput, CreateFeatureNotificationInput } from '../data/validators'\nimport { buildNotificationEntity, emitNotificationCreated, emitNotificationCreatedBatch } from '../lib/notificationFactory'\nimport { getRecipientUserIdsForFeature, getRecipientUserIdsForRole } from '../lib/notificationRecipients'\n\nfunction getKnex(em: EntityManager): Knex {\n return (em.getConnection() as unknown as { getKnex: () => Knex }).getKnex()\n}\n\nexport const NOTIFICATIONS_QUEUE_NAME = 'notifications'\n\nexport type CreateNotificationJob = {\n type: 'create'\n input: CreateNotificationInput\n tenantId: string\n organizationId?: string | null\n}\n\nexport type CreateRoleNotificationJob = {\n type: 'create-role'\n input: CreateRoleNotificationInput\n tenantId: string\n organizationId?: string | null\n}\n\nexport type CreateFeatureNotificationJob = {\n type: 'create-feature'\n input: CreateFeatureNotificationInput\n tenantId: string\n organizationId?: string | null\n}\n\nexport type CleanupExpiredJob = {\n type: 'cleanup-expired'\n}\n\nexport type NotificationJob = CreateNotificationJob | CreateRoleNotificationJob | CreateFeatureNotificationJob | CleanupExpiredJob\n\nexport const metadata = {\n queue: NOTIFICATIONS_QUEUE_NAME,\n id: 'notifications:create',\n concurrency: 5,\n}\n\ntype HandlerContext = {\n resolve: <T = unknown>(name: string) => T\n}\n\nexport default async function handle(\n job: { payload: NotificationJob },\n ctx: HandlerContext\n): Promise<void> {\n const { payload } = job\n\n if (payload.type === 'create') {\n const em = (ctx.resolve('em') as EntityManager).fork()\n const eventBus = ctx.resolve('eventBus') as { emit: (event: string, payload: unknown) => Promise<void> }\n const { input, tenantId, organizationId } = payload\n const { recipientUserId, ...content } = input\n const notification = buildNotificationEntity(em, content, recipientUserId, { tenantId, organizationId })\n\n await em.persistAndFlush(notification)\n\n await emitNotificationCreated(eventBus, notification, { tenantId, organizationId })\n } else if (payload.type === 'create-role') {\n const em = (ctx.resolve('em') as EntityManager).fork()\n const eventBus = ctx.resolve('eventBus') as { emit: (event: string, payload: unknown) => Promise<void> }\n const { input, tenantId, organizationId } = payload\n\n const knex = getKnex(em)\n const recipientUserIds = await getRecipientUserIdsForRole(knex, tenantId, input.roleId)\n if (recipientUserIds.length === 0) {\n return\n }\n\n const { roleId: _roleId, ...content } = input\n const notifications: Notification[] = []\n for (const recipientUserId of recipientUserIds) {\n const notification = buildNotificationEntity(em, content, recipientUserId, { tenantId, organizationId })\n notifications.push(notification)\n }\n\n await em.persistAndFlush(notifications)\n\n await emitNotificationCreatedBatch(eventBus, notifications, { tenantId, organizationId })\n } else if (payload.type === 'create-feature') {\n const em = (ctx.resolve('em') as EntityManager).fork()\n const eventBus = ctx.resolve('eventBus') as { emit: (event: string, payload: unknown) => Promise<void> }\n const { input, tenantId, organizationId } = payload\n\n const knex = getKnex(em)\n const recipientUserIds = await getRecipientUserIdsForFeature(knex, tenantId, input.requiredFeature)\n\n if (recipientUserIds.length === 0) {\n return\n }\n\n const notifications: Notification[] = []\n const { requiredFeature: _requiredFeature, ...content } = input\n for (const recipientUserId of recipientUserIds) {\n const notification = buildNotificationEntity(em, content, recipientUserId, { tenantId, organizationId })\n notifications.push(notification)\n }\n\n await em.persistAndFlush(notifications)\n\n await emitNotificationCreatedBatch(eventBus, notifications, { tenantId, organizationId })\n } else if (payload.type === 'cleanup-expired') {\n const em = (ctx.resolve('em') as EntityManager).fork()\n const knex = getKnex(em)\n\n await knex('notifications')\n .where('expires_at', '<', knex.fn.now())\n .whereNotIn('status', ['actioned', 'dismissed'])\n .update({\n status: 'dismissed',\n dismissed_at: knex.fn.now(),\n })\n }\n}\n"],
|
|
5
|
+
"mappings": "AAIA,SAAS,yBAAyB,yBAAyB,oCAAoC;AAC/F,SAAS,+BAA+B,kCAAkC;AAE1E,SAAS,QAAQ,IAAyB;AACxC,SAAQ,GAAG,cAAc,EAAyC,QAAQ;AAC5E;AAEO,MAAM,2BAA2B;AA6BjC,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,IAAI;AAAA,EACJ,aAAa;AACf;AAMA,eAAO,OACL,KACA,KACe;AACf,QAAM,EAAE,QAAQ,IAAI;AAEpB,MAAI,QAAQ,SAAS,UAAU;AAC7B,UAAM,KAAM,IAAI,QAAQ,IAAI,EAAoB,KAAK;AACrD,UAAM,WAAW,IAAI,QAAQ,UAAU;AACvC,UAAM,EAAE,OAAO,UAAU,eAAe,IAAI;AAC5C,UAAM,EAAE,iBAAiB,GAAG,QAAQ,IAAI;AACxC,UAAM,eAAe,wBAAwB,IAAI,SAAS,iBAAiB,EAAE,UAAU,eAAe,CAAC;AAEvG,UAAM,GAAG,gBAAgB,YAAY;AAErC,UAAM,wBAAwB,UAAU,cAAc,EAAE,UAAU,eAAe,CAAC;AAAA,EACpF,WAAW,QAAQ,SAAS,eAAe;AACzC,UAAM,KAAM,IAAI,QAAQ,IAAI,EAAoB,KAAK;AACrD,UAAM,WAAW,IAAI,QAAQ,UAAU;AACvC,UAAM,EAAE,OAAO,UAAU,eAAe,IAAI;AAE5C,UAAM,OAAO,QAAQ,EAAE;AACvB,UAAM,mBAAmB,MAAM,2BAA2B,MAAM,UAAU,MAAM,MAAM;AACtF,QAAI,iBAAiB,WAAW,GAAG;AACjC;AAAA,IACF;AAEA,UAAM,EAAE,QAAQ,SAAS,GAAG,QAAQ,IAAI;AACxC,UAAM,gBAAgC,CAAC;AACvC,eAAW,mBAAmB,kBAAkB;AAC9C,YAAM,eAAe,wBAAwB,IAAI,SAAS,iBAAiB,EAAE,UAAU,eAAe,CAAC;AACvG,oBAAc,KAAK,YAAY;AAAA,IACjC;AAEA,UAAM,GAAG,gBAAgB,aAAa;AAEtC,UAAM,6BAA6B,UAAU,eAAe,EAAE,UAAU,eAAe,CAAC;AAAA,EAC1F,WAAW,QAAQ,SAAS,kBAAkB;AAC5C,UAAM,KAAM,IAAI,QAAQ,IAAI,EAAoB,KAAK;AACrD,UAAM,WAAW,IAAI,QAAQ,UAAU;AACvC,UAAM,EAAE,OAAO,UAAU,eAAe,IAAI;AAE5C,UAAM,OAAO,QAAQ,EAAE;AACvB,UAAM,mBAAmB,MAAM,8BAA8B,MAAM,UAAU,MAAM,eAAe;AAElG,QAAI,iBAAiB,WAAW,GAAG;AACjC;AAAA,IACF;AAEA,UAAM,gBAAgC,CAAC;AACvC,UAAM,EAAE,iBAAiB,kBAAkB,GAAG,QAAQ,IAAI;AAC1D,eAAW,mBAAmB,kBAAkB;AAC9C,YAAM,eAAe,wBAAwB,IAAI,SAAS,iBAAiB,EAAE,UAAU,eAAe,CAAC;AACvG,oBAAc,KAAK,YAAY;AAAA,IACjC;AAEA,UAAM,GAAG,gBAAgB,aAAa;AAEtC,UAAM,6BAA6B,UAAU,eAAe,EAAE,UAAU,eAAe,CAAC;AAAA,EAC1F,WAAW,QAAQ,SAAS,mBAAmB;AAC7C,UAAM,KAAM,IAAI,QAAQ,IAAI,EAAoB,KAAK;AACrD,UAAM,OAAO,QAAQ,EAAE;AAEvB,UAAM,KAAK,eAAe,EACvB,MAAM,cAAc,KAAK,KAAK,GAAG,IAAI,CAAC,EACtC,WAAW,UAAU,CAAC,YAAY,WAAW,CAAC,EAC9C,OAAO;AAAA,MACN,QAAQ;AAAA,MACR,cAAc,KAAK,GAAG,IAAI;AAAA,IAC5B,CAAC;AAAA,EACL;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -161,8 +161,8 @@ function PlannerAvailabilityRuleSetsPage() {
|
|
|
161
161
|
RowActions,
|
|
162
162
|
{
|
|
163
163
|
items: [
|
|
164
|
-
{ label: labels.actions.edit, href: `/backend/planner/availability-rulesets/${row.id}` },
|
|
165
|
-
{ label: labels.actions.delete, destructive: true, onSelect: () => {
|
|
164
|
+
{ id: "edit", label: labels.actions.edit, href: `/backend/planner/availability-rulesets/${row.id}` },
|
|
165
|
+
{ id: "delete", label: labels.actions.delete, destructive: true, onSelect: () => {
|
|
166
166
|
void handleDelete(row);
|
|
167
167
|
} }
|
|
168
168
|
]
|