@open-mercato/core 0.4.2-canary-f075c3eb92 → 0.4.2-canary-8a04af8836
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/login.js +25 -6
- package/dist/modules/auth/api/login.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/[id]/edit/page.js +4 -1
- package/dist/modules/auth/backend/roles/[id]/edit/page.js.map +2 -2
- 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 +18 -3
- 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 +25 -11
- 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 +6 -3
- package/dist/modules/auth/data/validators.js.map +2 -2
- package/dist/modules/auth/frontend/login.js +112 -3
- package/dist/modules/auth/frontend/login.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 +46 -8
- 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 +24 -3
- package/dist/modules/auth/services/authService.js.map +2 -2
- package/dist/modules/business_rules/api/execute/route.js +7 -1
- package/dist/modules/business_rules/api/execute/route.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/cli.js +2 -1
- package/dist/modules/business_rules/cli.js.map +2 -2
- package/dist/modules/business_rules/lib/rule-engine.js +33 -3
- package/dist/modules/business_rules/lib/rule-engine.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/components/CachePanel.js +4 -4
- package/dist/modules/configs/components/CachePanel.js.map +2 -2
- package/dist/modules/configs/lib/system-status.js +48 -1
- package/dist/modules/configs/lib/system-status.js.map +2 -2
- package/dist/modules/configs/lib/upgrade-actions.js +138 -306
- 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/customers/widgets/dashboard/customer-todos/widget.js +2 -1
- package/dist/modules/customers/widgets/dashboard/customer-todos/widget.js.map +2 -2
- package/dist/modules/customers/widgets/dashboard/new-customers/widget.js +2 -1
- package/dist/modules/customers/widgets/dashboard/new-customers/widget.js.map +2 -2
- package/dist/modules/customers/widgets/dashboard/new-deals/widget.js +2 -1
- package/dist/modules/customers/widgets/dashboard/new-deals/widget.js.map +2 -2
- package/dist/modules/customers/widgets/dashboard/next-interactions/widget.js +2 -1
- package/dist/modules/customers/widgets/dashboard/next-interactions/widget.js.map +2 -2
- package/dist/modules/dashboards/cli.js +44 -5
- package/dist/modules/dashboards/cli.js.map +2 -2
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +16 -11
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +3 -3
- 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/services/widgetDataService.js +139 -3
- package/dist/modules/dashboards/services/widgetDataService.js.map +2 -2
- package/dist/modules/dashboards/setup.js +15 -0
- package/dist/modules/dashboards/setup.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.js +2 -1
- package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.js +2 -1
- package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.js +2 -1
- package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.js +2 -1
- package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.js +2 -1
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.js +2 -1
- package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.js +2 -1
- package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.js +2 -1
- package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.js +2 -1
- package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/top-products/widget.js +2 -1
- package/dist/modules/dashboards/widgets/dashboard/top-products/widget.js.map +2 -2
- package/dist/modules/dictionaries/components/DictionaryTable.js +2 -0
- package/dist/modules/dictionaries/components/DictionaryTable.js.map +2 -2
- package/dist/modules/directory/api/get/tenants/lookup.js +70 -0
- package/dist/modules/directory/api/get/tenants/lookup.js.map +7 -0
- 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 +98 -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 +220 -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 +107 -0
- package/dist/modules/notifications/lib/deliveryConfig.js.map +7 -0
- package/dist/modules/notifications/lib/deliveryStrategies.js +14 -0
- package/dist/modules/notifications/lib/deliveryStrategies.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/migrations/Migration20260129082610.js +13 -0
- package/dist/modules/notifications/migrations/Migration20260129082610.js.map +7 -0
- package/dist/modules/notifications/subscribers/deliver-notification.js +165 -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/cli.js +63 -7
- package/dist/modules/query_index/cli.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/cli.js +2 -42
- package/dist/modules/sales/cli.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/lib/seeds.js +48 -0
- package/dist/modules/sales/lib/seeds.js.map +7 -0
- 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/cli.js +12 -12
- package/dist/modules/workflows/cli.js.map +2 -2
- package/dist/modules/workflows/lib/transition-handler.js +14 -6
- package/dist/modules/workflows/lib/transition-handler.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/README.md +1 -1
- package/src/modules/auth/__tests__/cli-setup-acl.test.ts +1 -1
- package/src/modules/auth/api/__tests__/login.test.ts +2 -0
- package/src/modules/auth/api/admin/nav.ts +10 -6
- package/src/modules/auth/api/login.ts +26 -7
- 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/[id]/edit/page.tsx +4 -1
- package/src/modules/auth/backend/roles/page.tsx +3 -3
- package/src/modules/auth/backend/users/[id]/edit/page.tsx +22 -3
- 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 +38 -11
- package/src/modules/auth/commands/users.ts +73 -2
- package/src/modules/auth/data/validators.ts +6 -2
- package/src/modules/auth/frontend/login.tsx +134 -5
- package/src/modules/auth/frontend/reset/[token]/page.tsx +24 -11
- package/src/modules/auth/i18n/de.json +48 -1
- package/src/modules/auth/i18n/en.json +48 -1
- package/src/modules/auth/i18n/es.json +48 -1
- package/src/modules/auth/i18n/pl.json +48 -1
- package/src/modules/auth/lib/setup-app.ts +63 -9
- package/src/modules/auth/notifications.ts +109 -0
- package/src/modules/auth/services/authService.ts +27 -4
- package/src/modules/business_rules/api/execute/route.ts +8 -1
- 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/cli.ts +2 -1
- package/src/modules/business_rules/i18n/en.json +3 -1
- package/src/modules/business_rules/lib/__tests__/rule-engine.test.ts +51 -0
- package/src/modules/business_rules/lib/rule-engine.ts +57 -3
- 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/components/CachePanel.tsx +4 -4
- package/src/modules/configs/i18n/en.json +12 -2
- package/src/modules/configs/i18n/pl.json +12 -2
- package/src/modules/configs/lib/system-status.ts +48 -1
- package/src/modules/configs/lib/system-status.types.ts +1 -0
- package/src/modules/configs/lib/upgrade-actions.ts +157 -351
- 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/customers/widgets/dashboard/customer-todos/widget.ts +2 -2
- package/src/modules/customers/widgets/dashboard/new-customers/widget.ts +2 -2
- package/src/modules/customers/widgets/dashboard/new-deals/widget.ts +2 -2
- package/src/modules/customers/widgets/dashboard/next-interactions/widget.ts +2 -2
- package/src/modules/dashboards/cli.ts +55 -5
- package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +22 -11
- package/src/modules/dashboards/lib/role-widgets.ts +80 -0
- package/src/modules/dashboards/services/widgetDataService.ts +164 -4
- package/src/modules/dashboards/setup.ts +16 -0
- package/src/modules/dashboards/widgets/dashboard/aov-kpi/widget.ts +2 -2
- package/src/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.ts +2 -2
- package/src/modules/dashboards/widgets/dashboard/orders-by-status/widget.ts +2 -2
- package/src/modules/dashboards/widgets/dashboard/orders-kpi/widget.ts +2 -2
- package/src/modules/dashboards/widgets/dashboard/pipeline-summary/widget.ts +2 -2
- package/src/modules/dashboards/widgets/dashboard/revenue-kpi/widget.ts +2 -2
- package/src/modules/dashboards/widgets/dashboard/revenue-trend/widget.ts +2 -2
- package/src/modules/dashboards/widgets/dashboard/sales-by-region/widget.ts +2 -2
- package/src/modules/dashboards/widgets/dashboard/top-customers/widget.ts +2 -2
- package/src/modules/dashboards/widgets/dashboard/top-products/widget.ts +2 -2
- package/src/modules/dictionaries/components/DictionaryTable.tsx +2 -0
- package/src/modules/directory/api/get/tenants/lookup.ts +75 -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/__tests__/deliver-notification.test.ts +195 -0
- package/src/modules/notifications/__tests__/deliveryStrategies.test.ts +19 -0
- package/src/modules/notifications/__tests__/notificationService.test.ts +208 -0
- 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 +115 -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 +233 -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 +153 -0
- package/src/modules/notifications/lib/deliveryStrategies.ts +50 -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 +336 -0
- package/src/modules/notifications/migrations/Migration20260123000001.ts +73 -0
- package/src/modules/notifications/migrations/Migration20260126150000.ts +39 -0
- package/src/modules/notifications/migrations/Migration20260129082610.ts +13 -0
- package/src/modules/notifications/subscribers/deliver-notification.ts +204 -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/cli.ts +82 -13
- 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/cli.ts +2 -43
- 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/lib/seeds.ts +53 -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 +6 -0
- package/src/modules/staff/i18n/en.json +9 -1
- package/src/modules/staff/i18n/es.json +6 -0
- package/src/modules/staff/i18n/pl.json +6 -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/cli.ts +12 -12
- package/src/modules/workflows/i18n/en.json +3 -1
- package/src/modules/workflows/lib/__tests__/transition-handler.test.ts +6 -3
- package/src/modules/workflows/lib/transition-handler.ts +18 -6
- package/src/modules/workflows/notifications.ts +25 -0
- package/src/modules/workflows/subscribers/task-assigned-notification.ts +53 -0
|
@@ -35,6 +35,9 @@ import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
|
35
35
|
import type { CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'
|
|
36
36
|
import { E } from '#generated/entities.ids.generated'
|
|
37
37
|
import { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
38
|
+
import { resolveNotificationService } from '../../notifications/lib/notificationService'
|
|
39
|
+
import { buildNotificationFromType } from '../../notifications/lib/notificationBuilder'
|
|
40
|
+
import { notificationTypes } from '../notifications'
|
|
38
41
|
|
|
39
42
|
const DEAL_ENTITY_ID = 'customers:customer_deal'
|
|
40
43
|
const dealCrudIndexer: CrudIndexerConfig<CustomerDeal> = {
|
|
@@ -281,6 +284,8 @@ const updateDealCommand: CommandHandler<DealUpdateInput, { dealId: string }> = {
|
|
|
281
284
|
ensureTenantScope(ctx, record.tenantId)
|
|
282
285
|
ensureOrganizationScope(ctx, record.organizationId)
|
|
283
286
|
|
|
287
|
+
const previousStatus = record.status
|
|
288
|
+
|
|
284
289
|
if (parsed.title !== undefined) record.title = parsed.title
|
|
285
290
|
if (parsed.description !== undefined) record.description = parsed.description ?? null
|
|
286
291
|
if (parsed.status !== undefined) record.status = parsed.status ?? record.status
|
|
@@ -319,6 +324,40 @@ const updateDealCommand: CommandHandler<DealUpdateInput, { dealId: string }> = {
|
|
|
319
324
|
indexer: dealCrudIndexer,
|
|
320
325
|
})
|
|
321
326
|
|
|
327
|
+
// Send notifications for deal won/lost status changes
|
|
328
|
+
const newStatus = record.status
|
|
329
|
+
const normalizedStatus = newStatus === 'win' ? 'won' : newStatus === 'loose' ? 'lost' : newStatus
|
|
330
|
+
if (previousStatus !== newStatus && (normalizedStatus === 'won' || normalizedStatus === 'lost') && record.ownerUserId) {
|
|
331
|
+
try {
|
|
332
|
+
const notificationService = resolveNotificationService(ctx.container)
|
|
333
|
+
const notificationType = normalizedStatus === 'won' ? 'customers.deal.won' : 'customers.deal.lost'
|
|
334
|
+
const typeDef = notificationTypes.find((type) => type.type === notificationType)
|
|
335
|
+
if (typeDef) {
|
|
336
|
+
const valueDisplay = record.valueAmount && record.valueCurrency
|
|
337
|
+
? `${record.valueCurrency} ${record.valueAmount}`
|
|
338
|
+
: ''
|
|
339
|
+
|
|
340
|
+
const notificationInput = buildNotificationFromType(typeDef, {
|
|
341
|
+
recipientUserId: record.ownerUserId,
|
|
342
|
+
bodyVariables: {
|
|
343
|
+
dealTitle: record.title,
|
|
344
|
+
dealValue: valueDisplay,
|
|
345
|
+
},
|
|
346
|
+
sourceEntityType: 'customers:customer_deal',
|
|
347
|
+
sourceEntityId: record.id,
|
|
348
|
+
linkHref: `/backend/customers/deals/${record.id}`,
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
await notificationService.create(notificationInput, {
|
|
352
|
+
tenantId: record.tenantId,
|
|
353
|
+
organizationId: record.organizationId,
|
|
354
|
+
})
|
|
355
|
+
}
|
|
356
|
+
} catch {
|
|
357
|
+
// Notification creation is non-critical, don't fail the command
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
322
361
|
return { dealId: record.id }
|
|
323
362
|
},
|
|
324
363
|
buildLog: async ({ snapshots, ctx }) => {
|
|
@@ -943,5 +943,9 @@
|
|
|
943
943
|
"customers.workPlan.customerTodos.table.state.empty": "No customer tasks yet.",
|
|
944
944
|
"customers.workPlan.customerTodos.table.error.load": "Failed to load customer tasks.",
|
|
945
945
|
"customers.workPlan.customerTodos.table.export.view": "Exports the current list with filters and visible columns.",
|
|
946
|
-
"customers.workPlan.customerTodos.table.export.full": "Exports every linked task field, including hidden attributes."
|
|
946
|
+
"customers.workPlan.customerTodos.table.export.full": "Exports every linked task field, including hidden attributes.",
|
|
947
|
+
"customers.notifications.deal.won.title": "Deal Won",
|
|
948
|
+
"customers.notifications.deal.won.body": "{dealTitle} has been marked as won{dealValue, select, other { ({dealValue})}}",
|
|
949
|
+
"customers.notifications.deal.lost.title": "Deal Lost",
|
|
950
|
+
"customers.notifications.deal.lost.body": "{dealTitle} has been marked as lost"
|
|
947
951
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'
|
|
2
|
+
|
|
3
|
+
export const notificationTypes: NotificationTypeDefinition[] = [
|
|
4
|
+
{
|
|
5
|
+
type: 'customers.deal.won',
|
|
6
|
+
module: 'customers',
|
|
7
|
+
titleKey: 'customers.notifications.deal.won.title',
|
|
8
|
+
bodyKey: 'customers.notifications.deal.won.body',
|
|
9
|
+
icon: 'trophy',
|
|
10
|
+
severity: 'success',
|
|
11
|
+
actions: [
|
|
12
|
+
{
|
|
13
|
+
id: 'view',
|
|
14
|
+
labelKey: 'common.view',
|
|
15
|
+
variant: 'outline',
|
|
16
|
+
href: '/backend/customers/deals/{sourceEntityId}',
|
|
17
|
+
icon: 'external-link',
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
linkHref: '/backend/customers/deals/{sourceEntityId}',
|
|
21
|
+
expiresAfterHours: 168, // 7 days
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
type: 'customers.deal.lost',
|
|
25
|
+
module: 'customers',
|
|
26
|
+
titleKey: 'customers.notifications.deal.lost.title',
|
|
27
|
+
bodyKey: 'customers.notifications.deal.lost.body',
|
|
28
|
+
icon: 'x-circle',
|
|
29
|
+
severity: 'warning',
|
|
30
|
+
actions: [
|
|
31
|
+
{
|
|
32
|
+
id: 'view',
|
|
33
|
+
labelKey: 'common.view',
|
|
34
|
+
variant: 'outline',
|
|
35
|
+
href: '/backend/customers/deals/{sourceEntityId}',
|
|
36
|
+
icon: 'external-link',
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
linkHref: '/backend/customers/deals/{sourceEntityId}',
|
|
40
|
+
expiresAfterHours: 168, // 7 days
|
|
41
|
+
},
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
export default notificationTypes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import CustomerTodosWidget from './widget.client'
|
|
1
|
+
import { lazyDashboardWidget, type DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'
|
|
3
2
|
import { DEFAULT_SETTINGS, hydrateCustomerTodoSettings, type CustomerTodoWidgetSettings } from './config'
|
|
3
|
+
const CustomerTodosWidget = lazyDashboardWidget(() => import('./widget.client'))
|
|
4
4
|
|
|
5
5
|
const widget: DashboardWidgetModule<CustomerTodoWidgetSettings> = {
|
|
6
6
|
metadata: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import CustomerNewCustomersWidget from './widget.client'
|
|
1
|
+
import { lazyDashboardWidget, type DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'
|
|
3
2
|
import { DEFAULT_SETTINGS, hydrateNewCustomersSettings, type CustomerNewCustomersSettings } from './config'
|
|
3
|
+
const CustomerNewCustomersWidget = lazyDashboardWidget(() => import('./widget.client'))
|
|
4
4
|
|
|
5
5
|
const widget: DashboardWidgetModule<CustomerNewCustomersSettings> = {
|
|
6
6
|
metadata: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import CustomerNewDealsWidget from './widget.client'
|
|
1
|
+
import { lazyDashboardWidget, type DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'
|
|
3
2
|
import { DEFAULT_SETTINGS, hydrateNewDealsSettings, type CustomerNewDealsSettings } from './config'
|
|
3
|
+
const CustomerNewDealsWidget = lazyDashboardWidget(() => import('./widget.client'))
|
|
4
4
|
|
|
5
5
|
const widget: DashboardWidgetModule<CustomerNewDealsSettings> = {
|
|
6
6
|
metadata: {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import CustomerNextInteractionsWidget from './widget.client'
|
|
1
|
+
import { lazyDashboardWidget, type DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'
|
|
3
2
|
import {
|
|
4
3
|
DEFAULT_SETTINGS,
|
|
5
4
|
hydrateNextInteractionsSettings,
|
|
6
5
|
type CustomerNextInteractionsSettings,
|
|
7
6
|
} from './config'
|
|
7
|
+
const CustomerNextInteractionsWidget = lazyDashboardWidget(() => import('./widget.client'))
|
|
8
8
|
|
|
9
9
|
const widget: DashboardWidgetModule<CustomerNextInteractionsSettings> = {
|
|
10
10
|
metadata: {
|
|
@@ -4,6 +4,7 @@ import type { EntityManager } from '@mikro-orm/postgresql'
|
|
|
4
4
|
import { DashboardRoleWidgets } from '@open-mercato/core/modules/dashboards/data/entities'
|
|
5
5
|
import { Role } from '@open-mercato/core/modules/auth/data/entities'
|
|
6
6
|
import { loadAllWidgets } from '@open-mercato/core/modules/dashboards/lib/widgets'
|
|
7
|
+
import { appendWidgetsToRoles, resolveAnalyticsWidgetIds } from '@open-mercato/core/modules/dashboards/lib/role-widgets'
|
|
7
8
|
import { seedAnalyticsData } from './seed/analytics'
|
|
8
9
|
|
|
9
10
|
type Args = Record<string, string>
|
|
@@ -41,15 +42,25 @@ export async function seedDashboardDefaultsForTenant(
|
|
|
41
42
|
const widgetMap = new Map(widgets.map((widget) => [widget.metadata.id, widget]))
|
|
42
43
|
const resolvedWidgetIds = widgetIds && widgetIds.length
|
|
43
44
|
? widgetIds.filter((id) => widgetMap.has(id))
|
|
44
|
-
:
|
|
45
|
+
: null
|
|
46
|
+
const defaultWidgetIds = widgets
|
|
47
|
+
.filter((widget) => widget.metadata.defaultEnabled)
|
|
48
|
+
.map((widget) => widget.metadata.id)
|
|
49
|
+
const allWidgetIds = widgets.map((widget) => widget.metadata.id)
|
|
45
50
|
|
|
46
|
-
if (
|
|
51
|
+
if (resolvedWidgetIds && resolvedWidgetIds.length === 0) {
|
|
47
52
|
log('No widgets resolved for dashboard seeding.')
|
|
48
53
|
return false
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
await em.transactional(async (tem) => {
|
|
52
57
|
for (const roleName of roleNames) {
|
|
58
|
+
const isAdminRole = roleName === 'admin' || roleName === 'superadmin'
|
|
59
|
+
const roleWidgetIds = resolvedWidgetIds ?? (isAdminRole ? allWidgetIds : defaultWidgetIds)
|
|
60
|
+
if (!roleWidgetIds.length) {
|
|
61
|
+
log(`No widgets resolved for role "${roleName}".`)
|
|
62
|
+
continue
|
|
63
|
+
}
|
|
53
64
|
const role = await tem.findOne(Role, { name: roleName })
|
|
54
65
|
if (!role) {
|
|
55
66
|
log(`Skipping role "${roleName}" (not found)`)
|
|
@@ -62,7 +73,7 @@ export async function seedDashboardDefaultsForTenant(
|
|
|
62
73
|
deletedAt: null,
|
|
63
74
|
})
|
|
64
75
|
if (existing) {
|
|
65
|
-
existing.widgetIdsJson =
|
|
76
|
+
existing.widgetIdsJson = roleWidgetIds
|
|
66
77
|
tem.persist(existing)
|
|
67
78
|
log(`Updated dashboard widgets for role "${roleName}"`)
|
|
68
79
|
} else {
|
|
@@ -70,7 +81,7 @@ export async function seedDashboardDefaultsForTenant(
|
|
|
70
81
|
roleId: String(role.id),
|
|
71
82
|
tenantId,
|
|
72
83
|
organizationId,
|
|
73
|
-
widgetIdsJson:
|
|
84
|
+
widgetIdsJson: roleWidgetIds,
|
|
74
85
|
createdAt: new Date(),
|
|
75
86
|
updatedAt: null,
|
|
76
87
|
deletedAt: null,
|
|
@@ -120,6 +131,45 @@ const seedDefaults: ModuleCli = {
|
|
|
120
131
|
},
|
|
121
132
|
}
|
|
122
133
|
|
|
134
|
+
const enableAnalyticsWidgets: ModuleCli = {
|
|
135
|
+
command: 'enable-analytics-widgets',
|
|
136
|
+
async run(rest) {
|
|
137
|
+
const args = parseArgs(rest)
|
|
138
|
+
const tenantId = args.tenant || args.tenantId || null
|
|
139
|
+
const organizationId = args.organization || args.organizationId || args.org || null
|
|
140
|
+
const roleCsv = args.roles || 'admin,employee'
|
|
141
|
+
if (!tenantId) {
|
|
142
|
+
console.error('Usage: mercato dashboards enable-analytics-widgets --tenant <tenantId> [--org <orgId>] [--roles admin,employee]')
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const roleNames = roleCsv
|
|
147
|
+
.split(',')
|
|
148
|
+
.map((name) => name.trim())
|
|
149
|
+
.filter(Boolean)
|
|
150
|
+
|
|
151
|
+
if (!roleNames.length) {
|
|
152
|
+
console.log('No roles provided, nothing to update.')
|
|
153
|
+
return
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const { resolve } = await createRequestContainer()
|
|
157
|
+
const em = resolve('em') as EntityManager
|
|
158
|
+
const widgetIds = await resolveAnalyticsWidgetIds()
|
|
159
|
+
|
|
160
|
+
const updated = await appendWidgetsToRoles(em, {
|
|
161
|
+
tenantId,
|
|
162
|
+
organizationId,
|
|
163
|
+
roleNames,
|
|
164
|
+
widgetIds,
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
if (!updated) {
|
|
168
|
+
console.log('No dashboard role widgets updated.')
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
}
|
|
172
|
+
|
|
123
173
|
const seedAnalytics: ModuleCli = {
|
|
124
174
|
command: 'seed-analytics',
|
|
125
175
|
async run(rest) {
|
|
@@ -282,4 +332,4 @@ const debugAnalytics: ModuleCli = {
|
|
|
282
332
|
},
|
|
283
333
|
}
|
|
284
334
|
|
|
285
|
-
export default [seedDefaults, seedAnalytics, debugAnalytics]
|
|
335
|
+
export default [seedDefaults, enableAnalyticsWidgets, seedAnalytics, debugAnalytics]
|
|
@@ -44,9 +44,13 @@ type UserProps = BaseProps & {
|
|
|
44
44
|
|
|
45
45
|
type WidgetVisibilityEditorProps = RoleProps | UserProps
|
|
46
46
|
|
|
47
|
+
export type WidgetVisibilityEditorHandle = {
|
|
48
|
+
save: () => Promise<void>
|
|
49
|
+
}
|
|
50
|
+
|
|
47
51
|
const EMPTY: string[] = []
|
|
48
52
|
|
|
49
|
-
export function WidgetVisibilityEditor(props
|
|
53
|
+
export const WidgetVisibilityEditor = React.forwardRef<WidgetVisibilityEditorHandle, WidgetVisibilityEditorProps>(function WidgetVisibilityEditor(props, ref) {
|
|
50
54
|
const t = useT()
|
|
51
55
|
const { kind, targetId, tenantId, organizationId } = props
|
|
52
56
|
const [catalog, setCatalog] = React.useState<WidgetCatalogItem[]>([])
|
|
@@ -60,6 +64,15 @@ export function WidgetVisibilityEditor(props: WidgetVisibilityEditorProps) {
|
|
|
60
64
|
const [originalMode, setOriginalMode] = React.useState<'inherit' | 'override'>('inherit')
|
|
61
65
|
const [effective, setEffective] = React.useState<string[]>(EMPTY)
|
|
62
66
|
|
|
67
|
+
const dirty = React.useMemo(() => {
|
|
68
|
+
if (kind === 'user') {
|
|
69
|
+
if (mode !== originalMode) return true
|
|
70
|
+
if (mode === 'override') return selected.join('|') !== original.join('|')
|
|
71
|
+
return false
|
|
72
|
+
}
|
|
73
|
+
return selected.join('|') !== original.join('|')
|
|
74
|
+
}, [kind, mode, original, originalMode, selected])
|
|
75
|
+
|
|
63
76
|
const loadCatalog = React.useCallback(async () => {
|
|
64
77
|
const data = await readApiResultOrThrow<{ items?: unknown[] }>(
|
|
65
78
|
'/api/dashboards/widgets/catalog',
|
|
@@ -149,6 +162,9 @@ export function WidgetVisibilityEditor(props: WidgetVisibilityEditorProps) {
|
|
|
149
162
|
}, [original, originalMode])
|
|
150
163
|
|
|
151
164
|
const save = React.useCallback(async () => {
|
|
165
|
+
if (loading) return
|
|
166
|
+
if (error && catalog.length === 0) return
|
|
167
|
+
if (!dirty) return
|
|
152
168
|
setSaving(true)
|
|
153
169
|
setError(null)
|
|
154
170
|
try {
|
|
@@ -202,16 +218,9 @@ export function WidgetVisibilityEditor(props: WidgetVisibilityEditorProps) {
|
|
|
202
218
|
} finally {
|
|
203
219
|
setSaving(false)
|
|
204
220
|
}
|
|
205
|
-
}, [kind, mode, organizationId, selected, t, targetId, tenantId])
|
|
221
|
+
}, [catalog.length, dirty, error, kind, loading, mode, organizationId, selected, t, targetId, tenantId])
|
|
206
222
|
|
|
207
|
-
|
|
208
|
-
if (kind === 'user') {
|
|
209
|
-
if (mode !== originalMode) return true
|
|
210
|
-
if (mode === 'override') return selected.join('|') !== original.join('|')
|
|
211
|
-
return false
|
|
212
|
-
}
|
|
213
|
-
return selected.join('|') !== original.join('|')
|
|
214
|
-
}, [kind, mode, original, originalMode, selected])
|
|
223
|
+
React.useImperativeHandle(ref, () => ({ save }), [save])
|
|
215
224
|
|
|
216
225
|
if (loading) {
|
|
217
226
|
return (
|
|
@@ -297,4 +306,6 @@ export function WidgetVisibilityEditor(props: WidgetVisibilityEditorProps) {
|
|
|
297
306
|
</div>
|
|
298
307
|
</div>
|
|
299
308
|
)
|
|
300
|
-
}
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
WidgetVisibilityEditor.displayName = 'WidgetVisibilityEditor'
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
2
|
+
import { Role } from '@open-mercato/core/modules/auth/data/entities'
|
|
3
|
+
import { DashboardRoleWidgets } from '../data/entities'
|
|
4
|
+
import { loadAllWidgets } from './widgets'
|
|
5
|
+
|
|
6
|
+
type RoleWidgetScope = {
|
|
7
|
+
tenantId: string
|
|
8
|
+
organizationId?: string | null
|
|
9
|
+
roleNames: string[]
|
|
10
|
+
widgetIds: string[]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function findRoleByName(
|
|
14
|
+
em: EntityManager,
|
|
15
|
+
roleName: string,
|
|
16
|
+
tenantId: string,
|
|
17
|
+
): Promise<Role | null> {
|
|
18
|
+
const tenantRole = await em.findOne(Role, { name: roleName, tenantId })
|
|
19
|
+
if (tenantRole) return tenantRole
|
|
20
|
+
return em.findOne(Role, { name: roleName, tenantId: null })
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function resolveAnalyticsWidgetIds(): Promise<string[]> {
|
|
24
|
+
const widgets = await loadAllWidgets()
|
|
25
|
+
return widgets
|
|
26
|
+
.filter((widget) => widget.metadata.category === 'analytics' || widget.metadata.id.startsWith('dashboards.analytics.'))
|
|
27
|
+
.map((widget) => widget.metadata.id)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function appendWidgetsToRoles(
|
|
31
|
+
em: EntityManager,
|
|
32
|
+
{ tenantId, organizationId = null, roleNames, widgetIds }: RoleWidgetScope,
|
|
33
|
+
): Promise<boolean> {
|
|
34
|
+
const trimmedTenantId = tenantId.trim()
|
|
35
|
+
const widgets = await loadAllWidgets()
|
|
36
|
+
const validWidgetIds = new Set(widgets.map((widget) => widget.metadata.id))
|
|
37
|
+
const resolvedWidgetIds = widgetIds.filter((id) => validWidgetIds.has(id))
|
|
38
|
+
if (!resolvedWidgetIds.length) return false
|
|
39
|
+
|
|
40
|
+
let updated = false
|
|
41
|
+
await em.transactional(async (tem) => {
|
|
42
|
+
for (const roleName of roleNames) {
|
|
43
|
+
const role = await findRoleByName(tem, roleName, trimmedTenantId)
|
|
44
|
+
if (!role) continue
|
|
45
|
+
|
|
46
|
+
const record = await tem.findOne(DashboardRoleWidgets, {
|
|
47
|
+
roleId: String(role.id),
|
|
48
|
+
tenantId: trimmedTenantId,
|
|
49
|
+
organizationId,
|
|
50
|
+
deletedAt: null,
|
|
51
|
+
})
|
|
52
|
+
const roleRecord = record ?? (organizationId
|
|
53
|
+
? await tem.findOne(DashboardRoleWidgets, {
|
|
54
|
+
roleId: String(role.id),
|
|
55
|
+
tenantId: trimmedTenantId,
|
|
56
|
+
organizationId: null,
|
|
57
|
+
deletedAt: null,
|
|
58
|
+
})
|
|
59
|
+
: null)
|
|
60
|
+
if (!roleRecord) continue
|
|
61
|
+
|
|
62
|
+
const current = Array.isArray(roleRecord.widgetIdsJson) ? roleRecord.widgetIdsJson : []
|
|
63
|
+
const next = [...current]
|
|
64
|
+
const existing = new Set(current)
|
|
65
|
+
for (const widgetId of resolvedWidgetIds) {
|
|
66
|
+
if (existing.has(widgetId)) continue
|
|
67
|
+
existing.add(widgetId)
|
|
68
|
+
next.push(widgetId)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (next.length === current.length) continue
|
|
72
|
+
roleRecord.widgetIdsJson = next
|
|
73
|
+
roleRecord.updatedAt = new Date()
|
|
74
|
+
tem.persist(roleRecord)
|
|
75
|
+
updated = true
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
return updated
|
|
80
|
+
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
2
2
|
import type { CacheStrategy } from '@open-mercato/cache'
|
|
3
3
|
import { createHash } from 'node:crypto'
|
|
4
|
+
import { decryptWithAesGcm } from '@open-mercato/shared/lib/encryption/aes'
|
|
5
|
+
import { resolveTenantEncryptionService } from '@open-mercato/shared/lib/encryption/customFieldValues'
|
|
6
|
+
import { resolveEntityIdFromMetadata } from '@open-mercato/shared/lib/encryption/entityIds'
|
|
7
|
+
import { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
4
8
|
import {
|
|
5
9
|
type DateRangePreset,
|
|
6
10
|
resolveDateRange,
|
|
@@ -17,6 +21,8 @@ import {
|
|
|
17
21
|
import type { AnalyticsRegistry } from './analyticsRegistry'
|
|
18
22
|
|
|
19
23
|
const WIDGET_DATA_CACHE_TTL = 120_000
|
|
24
|
+
const WIDGET_DATA_SEGMENT_TTL = 86_400_000
|
|
25
|
+
const WIDGET_DATA_SEGMENT_KEY = 'widget-data:__segment__'
|
|
20
26
|
|
|
21
27
|
const SAFE_IDENTIFIER_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/
|
|
22
28
|
|
|
@@ -168,6 +174,11 @@ export class WidgetDataService {
|
|
|
168
174
|
const tags = this.getCacheTags(request.entityType)
|
|
169
175
|
try {
|
|
170
176
|
await this.cache.set(cacheKey, response, { ttl: WIDGET_DATA_CACHE_TTL, tags })
|
|
177
|
+
await this.cache.set(
|
|
178
|
+
WIDGET_DATA_SEGMENT_KEY,
|
|
179
|
+
{ updatedAt: response.metadata.fetchedAt },
|
|
180
|
+
{ ttl: WIDGET_DATA_SEGMENT_TTL, tags: ['widget-data'] },
|
|
181
|
+
)
|
|
171
182
|
} catch {
|
|
172
183
|
}
|
|
173
184
|
}
|
|
@@ -282,6 +293,75 @@ export class WidgetDataService {
|
|
|
282
293
|
assertSafeIdentifier(config.idColumn, 'id column')
|
|
283
294
|
assertSafeIdentifier(config.labelColumn, 'label column')
|
|
284
295
|
|
|
296
|
+
const meta = this.resolveEntityMetadata(config.table)
|
|
297
|
+
const idProp = meta ? this.resolveEntityPropertyName(meta, config.idColumn) : null
|
|
298
|
+
const labelProp = meta ? this.resolveEntityPropertyName(meta, config.labelColumn) : null
|
|
299
|
+
const tenantProp = meta
|
|
300
|
+
? (this.resolveEntityPropertyName(meta, 'tenant_id') ?? this.resolveEntityPropertyName(meta, 'tenantId'))
|
|
301
|
+
: null
|
|
302
|
+
const organizationProp = meta
|
|
303
|
+
? (this.resolveEntityPropertyName(meta, 'organization_id') ?? this.resolveEntityPropertyName(meta, 'organizationId'))
|
|
304
|
+
: null
|
|
305
|
+
const entityName = meta ? ((meta as any).class ?? meta.className ?? meta.name) : null
|
|
306
|
+
|
|
307
|
+
if (meta && idProp && labelProp && tenantProp && entityName) {
|
|
308
|
+
const where: Record<string, unknown> = {
|
|
309
|
+
[idProp]: { $in: uniqueIds },
|
|
310
|
+
[tenantProp]: this.scope.tenantId,
|
|
311
|
+
}
|
|
312
|
+
if (organizationProp && this.scope.organizationIds && this.scope.organizationIds.length > 0) {
|
|
313
|
+
where[organizationProp] = { $in: this.scope.organizationIds }
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
try {
|
|
317
|
+
const records = await findWithDecryption(
|
|
318
|
+
this.em,
|
|
319
|
+
entityName,
|
|
320
|
+
where,
|
|
321
|
+
{ fields: [idProp, labelProp, tenantProp, organizationProp].filter(Boolean) },
|
|
322
|
+
{ tenantId: this.scope.tenantId, organizationId: this.resolveOrganizationId() },
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
const encryptionService = resolveTenantEncryptionService(this.em as any)
|
|
326
|
+
const dek = encryptionService?.isEnabled() ? await encryptionService.getDek(this.scope.tenantId) : null
|
|
327
|
+
let hasEncryptedLabels = false
|
|
328
|
+
let hasDecryptedLabels = false
|
|
329
|
+
|
|
330
|
+
const labelMap = new Map<string, string>()
|
|
331
|
+
for (const record of records as Array<Record<string, unknown>>) {
|
|
332
|
+
const id = record[idProp]
|
|
333
|
+
let labelValue = record[labelProp]
|
|
334
|
+
if (typeof labelValue === 'string' && this.isEncryptedPayload(labelValue)) {
|
|
335
|
+
hasEncryptedLabels = true
|
|
336
|
+
if (dek?.key) {
|
|
337
|
+
const decrypted = this.decryptWithDek(labelValue, dek.key)
|
|
338
|
+
if (decrypted !== null) {
|
|
339
|
+
labelValue = decrypted
|
|
340
|
+
hasDecryptedLabels = true
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
} else if (labelValue != null && labelValue !== '') {
|
|
344
|
+
hasDecryptedLabels = true
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (typeof id === 'string' && labelValue != null && labelValue !== '') {
|
|
348
|
+
labelMap.set(id, String(labelValue))
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (labelMap.size > 0 && (!hasEncryptedLabels || hasDecryptedLabels)) {
|
|
353
|
+
return data.map((item) => ({
|
|
354
|
+
...item,
|
|
355
|
+
groupLabel: typeof item.groupKey === 'string' && labelMap.has(item.groupKey)
|
|
356
|
+
? labelMap.get(item.groupKey)!
|
|
357
|
+
: undefined,
|
|
358
|
+
}))
|
|
359
|
+
}
|
|
360
|
+
} catch {
|
|
361
|
+
// fall through to SQL resolution
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
285
365
|
const clauses = [`"${config.idColumn}" = ANY(?::uuid[])`, 'tenant_id = ?']
|
|
286
366
|
const params: unknown[] = [`{${uniqueIds.join(',')}}`, this.scope.tenantId]
|
|
287
367
|
|
|
@@ -290,17 +370,43 @@ export class WidgetDataService {
|
|
|
290
370
|
params.push(`{${this.scope.organizationIds.join(',')}}`)
|
|
291
371
|
}
|
|
292
372
|
|
|
293
|
-
const sql = `SELECT "${config.idColumn}" as id, "${config.labelColumn}" as label FROM "${config.table}" WHERE ${clauses.join(
|
|
373
|
+
const sql = `SELECT "${config.idColumn}" as id, "${config.labelColumn}" as label, tenant_id, organization_id FROM "${config.table}" WHERE ${clauses.join(
|
|
294
374
|
' AND ',
|
|
295
375
|
)}`
|
|
296
376
|
|
|
297
377
|
try {
|
|
298
378
|
const labelRows = await this.em.getConnection().execute(sql, params)
|
|
379
|
+
const entityId = this.resolveEntityId(meta)
|
|
380
|
+
const encryptionService = resolveTenantEncryptionService(this.em as any)
|
|
381
|
+
const organizationId = this.resolveOrganizationId()
|
|
382
|
+
const dek = encryptionService?.isEnabled() ? await encryptionService.getDek(this.scope.tenantId) : null
|
|
299
383
|
|
|
300
384
|
const labelMap = new Map<string, string>()
|
|
301
|
-
for (const row of labelRows as Array<{ id: string; label: string | null }>) {
|
|
302
|
-
|
|
303
|
-
|
|
385
|
+
for (const row of labelRows as Array<{ id: string; label: string | null; tenant_id?: string | null; organization_id?: string | null }>) {
|
|
386
|
+
let labelValue = row.label
|
|
387
|
+
if (entityId && encryptionService?.isEnabled() && labelValue != null) {
|
|
388
|
+
const rowOrgId = row.organization_id ?? organizationId ?? null
|
|
389
|
+
const decrypted = await encryptionService.decryptEntityPayload(
|
|
390
|
+
entityId,
|
|
391
|
+
{ [config.labelColumn]: labelValue },
|
|
392
|
+
this.scope.tenantId,
|
|
393
|
+
rowOrgId,
|
|
394
|
+
)
|
|
395
|
+
const resolved = decrypted[config.labelColumn]
|
|
396
|
+
if (typeof resolved === 'string' || typeof resolved === 'number') {
|
|
397
|
+
labelValue = String(resolved)
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (labelValue && dek?.key && this.isEncryptedPayload(labelValue)) {
|
|
402
|
+
const decrypted = this.decryptWithDek(labelValue, dek.key)
|
|
403
|
+
if (decrypted !== null) {
|
|
404
|
+
labelValue = decrypted
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (row.id && labelValue != null && labelValue !== '') {
|
|
409
|
+
labelMap.set(row.id, labelValue)
|
|
304
410
|
}
|
|
305
411
|
}
|
|
306
412
|
|
|
@@ -317,6 +423,60 @@ export class WidgetDataService {
|
|
|
317
423
|
}))
|
|
318
424
|
}
|
|
319
425
|
}
|
|
426
|
+
|
|
427
|
+
private resolveOrganizationId(): string | null {
|
|
428
|
+
if (!this.scope.organizationIds || this.scope.organizationIds.length !== 1) return null
|
|
429
|
+
return this.scope.organizationIds[0] ?? null
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
private resolveEntityMetadata(tableName: string): Record<string, any> | null {
|
|
433
|
+
const registry = (this.em as any)?.getMetadata?.()
|
|
434
|
+
if (!registry) return null
|
|
435
|
+
const entries =
|
|
436
|
+
(typeof registry.getAll === 'function' && registry.getAll()) ||
|
|
437
|
+
(Array.isArray(registry.metadata) ? registry.metadata : Object.values(registry.metadata ?? {}))
|
|
438
|
+
const metas = Array.isArray(entries) ? entries : Object.values(entries ?? {})
|
|
439
|
+
const match = metas.find((meta: any) => {
|
|
440
|
+
const table = meta?.tableName ?? meta?.collection
|
|
441
|
+
if (typeof table !== 'string') return false
|
|
442
|
+
if (table === tableName) return true
|
|
443
|
+
return table.split('.').pop() === tableName
|
|
444
|
+
})
|
|
445
|
+
return match ?? null
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
private resolveEntityPropertyName(meta: Record<string, any>, columnName: string): string | null {
|
|
449
|
+
const properties = meta?.properties ? Object.values(meta.properties) : []
|
|
450
|
+
for (const prop of properties as Array<Record<string, any>>) {
|
|
451
|
+
const fieldName = prop?.fieldName
|
|
452
|
+
const fieldNames = prop?.fieldNames
|
|
453
|
+
if (typeof fieldName === 'string' && fieldName === columnName) return prop?.name ?? null
|
|
454
|
+
if (Array.isArray(fieldNames) && fieldNames.includes(columnName)) return prop?.name ?? null
|
|
455
|
+
if (prop?.name === columnName) return prop?.name ?? null
|
|
456
|
+
}
|
|
457
|
+
return null
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
private resolveEntityId(meta: Record<string, any> | null): string | null {
|
|
461
|
+
if (!meta) return null
|
|
462
|
+
try {
|
|
463
|
+
return resolveEntityIdFromMetadata(meta as any)
|
|
464
|
+
} catch {
|
|
465
|
+
return null
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
private isEncryptedPayload(value: string): boolean {
|
|
470
|
+
const parts = value.split(':')
|
|
471
|
+
return parts.length === 4 && parts[3] === 'v1'
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
private decryptWithDek(value: string, dek: string): string | null {
|
|
475
|
+
const first = decryptWithAesGcm(value, dek)
|
|
476
|
+
if (first === null) return null
|
|
477
|
+
if (!this.isEncryptedPayload(first)) return first
|
|
478
|
+
return decryptWithAesGcm(first, dek) ?? first
|
|
479
|
+
}
|
|
320
480
|
}
|
|
321
481
|
|
|
322
482
|
export function createWidgetDataService(
|
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'
|
|
2
|
+
import { seedDashboardDefaultsForTenant } from '@open-mercato/core/modules/dashboards/cli'
|
|
3
|
+
import { appendWidgetsToRoles, resolveAnalyticsWidgetIds } from '@open-mercato/core/modules/dashboards/lib/role-widgets'
|
|
2
4
|
|
|
3
5
|
export const setup: ModuleSetupConfig = {
|
|
4
6
|
defaultRoleFeatures: {
|
|
5
7
|
admin: ['dashboards.*', 'dashboards.admin.assign-widgets', 'analytics.view'],
|
|
6
8
|
employee: ['dashboards.view', 'dashboards.configure', 'analytics.view'],
|
|
7
9
|
},
|
|
10
|
+
|
|
11
|
+
async onTenantCreated({ em, tenantId, organizationId }) {
|
|
12
|
+
await seedDashboardDefaultsForTenant(em, { tenantId, organizationId, logger: () => {} })
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
async seedDefaults({ em, tenantId, organizationId }) {
|
|
16
|
+
const analyticsWidgetIds = await resolveAnalyticsWidgetIds()
|
|
17
|
+
await appendWidgetsToRoles(em, {
|
|
18
|
+
tenantId,
|
|
19
|
+
organizationId,
|
|
20
|
+
roleNames: ['admin', 'employee'],
|
|
21
|
+
widgetIds: analyticsWidgetIds,
|
|
22
|
+
})
|
|
23
|
+
},
|
|
8
24
|
}
|
|
9
25
|
|
|
10
26
|
export default setup
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import AovKpiWidget from './widget.client'
|
|
1
|
+
import { lazyDashboardWidget, type DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'
|
|
3
2
|
import { DEFAULT_SETTINGS, hydrateSettings, type AovKpiSettings } from './config'
|
|
3
|
+
const AovKpiWidget = lazyDashboardWidget(() => import('./widget.client'))
|
|
4
4
|
|
|
5
5
|
const widget: DashboardWidgetModule<AovKpiSettings> = {
|
|
6
6
|
metadata: {
|