@open-mercato/core 0.6.5-develop.5337.1.534b781eac → 0.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +1 -1
- package/dist/bootstrap.js +46 -6
- package/dist/bootstrap.js.map +2 -2
- package/dist/generated/entities/organization/index.js +2 -0
- package/dist/generated/entities/organization/index.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +1 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/crmFixtures.js +4 -0
- package/dist/helpers/integration/crmFixtures.js.map +2 -2
- package/dist/modules/attachments/api/library/route.js +2 -2
- package/dist/modules/attachments/api/library/route.js.map +2 -2
- package/dist/modules/attachments/api/route.js +2 -0
- package/dist/modules/attachments/api/route.js.map +2 -2
- package/dist/modules/attachments/components/AttachmentContentPreview.js +9 -5
- package/dist/modules/attachments/components/AttachmentContentPreview.js.map +2 -2
- package/dist/modules/attachments/lib/access.js +18 -0
- package/dist/modules/attachments/lib/access.js.map +2 -2
- package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js +3 -2
- package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js.map +2 -2
- package/dist/modules/audit_logs/data/entities.js +2 -1
- package/dist/modules/audit_logs/data/entities.js.map +2 -2
- package/dist/modules/audit_logs/migrations/Migration20260611104500.js +13 -0
- package/dist/modules/audit_logs/migrations/Migration20260611104500.js.map +7 -0
- package/dist/modules/audit_logs/services/accessLogService.js +10 -0
- package/dist/modules/audit_logs/services/accessLogService.js.map +2 -2
- package/dist/modules/auth/api/admin/nav.js +9 -0
- package/dist/modules/auth/api/admin/nav.js.map +2 -2
- package/dist/modules/auth/api/login.js +4 -13
- package/dist/modules/auth/api/login.js.map +2 -2
- package/dist/modules/auth/commands/users.js +20 -14
- package/dist/modules/auth/commands/users.js.map +2 -2
- package/dist/modules/auth/data/entities.js +4 -2
- package/dist/modules/auth/data/entities.js.map +2 -2
- package/dist/modules/auth/lib/backendChrome.js +35 -2
- package/dist/modules/auth/lib/backendChrome.js.map +2 -2
- package/dist/modules/auth/lib/consentIntegrity.js +3 -3
- package/dist/modules/auth/lib/consentIntegrity.js.map +2 -2
- package/dist/modules/auth/migrations/Migration20260610120000.js +30 -0
- package/dist/modules/auth/migrations/Migration20260610120000.js.map +7 -0
- package/dist/modules/auth/migrations/Migration20260611103000.js +15 -0
- package/dist/modules/auth/migrations/Migration20260611103000.js.map +7 -0
- package/dist/modules/auth/services/authService.js +5 -3
- package/dist/modules/auth/services/authService.js.map +2 -2
- package/dist/modules/auth/services/rbacService.js +3 -2
- package/dist/modules/auth/services/rbacService.js.map +2 -2
- package/dist/modules/catalog/ai-tools/configuration-pack.js.map +1 -1
- package/dist/modules/catalog/ai-tools/prices-offers-pack.js.map +1 -1
- package/dist/modules/catalog/ai-tools/products-pack.js.map +1 -1
- package/dist/modules/catalog/ai-tools/variants-pack.js.map +1 -1
- package/dist/modules/communication_channels/data/entities.js.map +1 -1
- package/dist/modules/communication_channels/encryption.js.map +1 -1
- package/dist/modules/communication_channels/lib/thread-matcher.js.map +1 -1
- package/dist/modules/communication_channels/lib/thread-token.js.map +1 -1
- package/dist/modules/currencies/api/currencies/route.js +4 -3
- package/dist/modules/currencies/api/currencies/route.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/roles.js +2 -1
- package/dist/modules/customer_accounts/api/admin/roles.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js +0 -3
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js.map +2 -2
- package/dist/modules/customer_accounts/events.js +1 -1
- package/dist/modules/customer_accounts/events.js.map +1 -1
- package/dist/modules/customer_accounts/lib/resolveTenantContext.js.map +1 -1
- package/dist/modules/customers/acl.js +1 -1
- package/dist/modules/customers/acl.js.map +1 -1
- package/dist/modules/customers/ai-tools/companies-pack.js.map +1 -1
- package/dist/modules/customers/ai-tools/deals-pack.js.map +1 -1
- package/dist/modules/customers/ai-tools/people-pack.js.map +1 -1
- package/dist/modules/customers/api/companies/route.js +4 -4
- package/dist/modules/customers/api/companies/route.js.map +2 -2
- package/dist/modules/customers/api/deals/route.js +43 -2
- package/dist/modules/customers/api/deals/route.js.map +2 -2
- package/dist/modules/customers/api/deals/summary/route.js +402 -0
- package/dist/modules/customers/api/deals/summary/route.js.map +7 -0
- package/dist/modules/customers/api/people/route.js +4 -4
- package/dist/modules/customers/api/people/route.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js +16 -5
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealData.js +22 -5
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealData.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +12 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +221 -56
- package/dist/modules/customers/backend/customers/deals/page.js.map +3 -3
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js +1 -1
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +18 -0
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/cli.js +15 -9
- package/dist/modules/customers/cli.js.map +2 -2
- package/dist/modules/customers/commands/addresses.js +5 -5
- package/dist/modules/customers/commands/addresses.js.map +2 -2
- package/dist/modules/customers/commands/comments.js +5 -5
- package/dist/modules/customers/commands/comments.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +2 -2
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/commands/entity-roles.js +2 -1
- package/dist/modules/customers/commands/entity-roles.js.map +2 -2
- package/dist/modules/customers/commands/interactions.js +8 -5
- package/dist/modules/customers/commands/interactions.js.map +2 -2
- package/dist/modules/customers/commands/shared.js +21 -6
- package/dist/modules/customers/commands/shared.js.map +2 -2
- package/dist/modules/customers/commands/tags.js +3 -3
- package/dist/modules/customers/commands/tags.js.map +2 -2
- package/dist/modules/customers/components/DealsKpiStrip.js +282 -0
- package/dist/modules/customers/components/DealsKpiStrip.js.map +7 -0
- package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js +0 -1
- package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/DealForm.js +100 -17
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/components/detail/PersonDetailTabs.js +11 -3
- package/dist/modules/customers/components/detail/PersonDetailTabs.js.map +2 -2
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +1 -2
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/assignableStaff.js +21 -8
- package/dist/modules/customers/components/detail/assignableStaff.js.map +2 -2
- package/dist/modules/customers/components/kpi/PipelineStageBar.js +63 -0
- package/dist/modules/customers/components/kpi/PipelineStageBar.js.map +7 -0
- package/dist/modules/customers/lib/dealsMetrics.js +82 -0
- package/dist/modules/customers/lib/dealsMetrics.js.map +7 -0
- package/dist/modules/customers/migrations/Migration20260519120000_pipeline_stage_color_tones.js.map +1 -1
- package/dist/modules/data_sync/api/run.js +1 -1
- package/dist/modules/data_sync/api/run.js.map +2 -2
- package/dist/modules/directory/api/organization-branding/route.js +214 -0
- package/dist/modules/directory/api/organization-branding/route.js.map +7 -0
- package/dist/modules/directory/api/organizations/route.js +7 -0
- package/dist/modules/directory/api/organizations/route.js.map +3 -3
- package/dist/modules/directory/backend/directory/branding/page.js +214 -0
- package/dist/modules/directory/backend/directory/branding/page.js.map +7 -0
- package/dist/modules/directory/backend/directory/branding/page.meta.js +26 -0
- package/dist/modules/directory/backend/directory/branding/page.meta.js.map +7 -0
- package/dist/modules/directory/commands/organizations.js +8 -1
- package/dist/modules/directory/commands/organizations.js.map +2 -2
- package/dist/modules/directory/data/entities.js +3 -0
- package/dist/modules/directory/data/entities.js.map +2 -2
- package/dist/modules/directory/data/validators.js +9 -0
- package/dist/modules/directory/data/validators.js.map +2 -2
- package/dist/modules/directory/migrations/Migration20260607222259_directory.js +13 -0
- package/dist/modules/directory/migrations/Migration20260607222259_directory.js.map +7 -0
- package/dist/modules/directory/subscribers/invalidateOrgScopeCache.js +2 -1
- package/dist/modules/directory/subscribers/invalidateOrgScopeCache.js.map +2 -2
- package/dist/modules/directory/utils/organizationScope.js +59 -27
- package/dist/modules/directory/utils/organizationScope.js.map +2 -2
- package/dist/modules/entities/api/definitions.batch.js +2 -1
- package/dist/modules/entities/api/definitions.batch.js.map +2 -2
- package/dist/modules/entities/api/entities.js +7 -0
- package/dist/modules/entities/api/entities.js.map +2 -2
- package/dist/modules/entities/api/records.js +26 -15
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js +14 -0
- package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/create/page.js +14 -0
- package/dist/modules/entities/backend/entities/user/[entityId]/records/create/page.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +12 -0
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
- package/dist/modules/entities/components/useRecordsEntityGuard.js +30 -0
- package/dist/modules/entities/components/useRecordsEntityGuard.js.map +7 -0
- package/dist/modules/payment_gateways/api/transactions/route.js +2 -4
- package/dist/modules/payment_gateways/api/transactions/route.js.map +2 -2
- package/dist/modules/progress/api/jobs/[id]/route.js +7 -2
- package/dist/modules/progress/api/jobs/[id]/route.js.map +2 -2
- package/dist/modules/progress/api/jobs/route.js +1 -1
- package/dist/modules/progress/api/jobs/route.js.map +2 -2
- package/dist/modules/progress/lib/progressServiceImpl.js +8 -2
- package/dist/modules/progress/lib/progressServiceImpl.js.map +2 -2
- package/dist/modules/query_index/data/entities.js +2 -1
- package/dist/modules/query_index/data/entities.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +4 -2
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/query_index/migrations/Migration20260611103000_query_index.js +16 -0
- package/dist/modules/query_index/migrations/Migration20260611103000_query_index.js.map +7 -0
- package/dist/modules/resources/api/resources.js +2 -3
- package/dist/modules/resources/api/resources.js.map +2 -2
- package/dist/modules/sales/api/documents/factory.js +2 -2
- package/dist/modules/sales/api/documents/factory.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +7 -5
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js +2 -1
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
- package/dist/modules/sales/components/documents/salesDocumentsColumns.js +10 -0
- package/dist/modules/sales/components/documents/salesDocumentsColumns.js.map +7 -0
- package/dist/modules/staff/api/team-members.js +9 -2
- package/dist/modules/staff/api/team-members.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js +24 -1
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js +11 -6
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
- package/dist/modules/staff/commands/team-members.js +1 -1
- package/dist/modules/staff/commands/team-members.js.map +2 -2
- package/dist/modules/staff/components/TeamMemberForm.js +1 -1
- package/dist/modules/staff/components/TeamMemberForm.js.map +2 -2
- package/dist/modules/staff/lib/scheduleSwitch.js +23 -0
- package/dist/modules/staff/lib/scheduleSwitch.js.map +7 -0
- package/dist/modules/sync_excel/api/import/route.js +1 -1
- package/dist/modules/sync_excel/api/import/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/route.js +3 -2
- package/dist/modules/workflows/api/definitions/route.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/create/page.js +1 -2
- package/dist/modules/workflows/backend/definitions/create/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js +1 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js +1 -2
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +2 -2
- package/dist/modules/workflows/components/NodeEditDialog.js +4 -13
- package/dist/modules/workflows/components/NodeEditDialog.js.map +2 -2
- package/dist/modules/workflows/components/NodeEditDialogCrudForm.js +4 -13
- package/dist/modules/workflows/components/NodeEditDialogCrudForm.js.map +2 -2
- package/dist/modules/workflows/components/WorkflowGraphImpl.js +1 -4
- package/dist/modules/workflows/components/WorkflowGraphImpl.js.map +2 -2
- package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js +2 -5
- package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js.map +2 -2
- package/generated/entities/organization/index.ts +1 -0
- package/generated/entity-fields-registry.ts +1 -0
- package/package.json +11 -12
- package/src/bootstrap.ts +65 -7
- package/src/helpers/integration/crmFixtures.ts +21 -1
- package/src/modules/attachments/AGENTS.md +79 -0
- package/src/modules/attachments/api/library/route.ts +2 -2
- package/src/modules/attachments/api/route.ts +2 -0
- package/src/modules/attachments/components/AttachmentContentPreview.tsx +6 -6
- package/src/modules/attachments/lib/access.ts +36 -0
- package/src/modules/audit_logs/api/audit-logs/actions/redo/route.ts +14 -2
- package/src/modules/audit_logs/data/entities.ts +1 -0
- package/src/modules/audit_logs/migrations/.snapshot-open-mercato.json +10 -0
- package/src/modules/audit_logs/migrations/Migration20260611104500.ts +13 -0
- package/src/modules/audit_logs/services/accessLogService.ts +15 -0
- package/src/modules/auth/api/admin/nav.ts +9 -0
- package/src/modules/auth/api/login.ts +13 -13
- package/src/modules/auth/commands/users.ts +32 -15
- package/src/modules/auth/data/entities.ts +13 -1
- package/src/modules/auth/i18n/de.json +0 -1
- package/src/modules/auth/i18n/en.json +0 -1
- package/src/modules/auth/i18n/es.json +0 -1
- package/src/modules/auth/i18n/pl.json +0 -1
- package/src/modules/auth/lib/backendChrome.tsx +37 -1
- package/src/modules/auth/lib/consentIntegrity.ts +6 -3
- package/src/modules/auth/migrations/.snapshot-open-mercato.json +20 -10
- package/src/modules/auth/migrations/Migration20260610120000.ts +53 -0
- package/src/modules/auth/migrations/Migration20260611103000.ts +21 -0
- package/src/modules/auth/services/authService.ts +24 -4
- package/src/modules/auth/services/rbacService.ts +11 -2
- package/src/modules/catalog/ai-tools/configuration-pack.ts +1 -1
- package/src/modules/catalog/ai-tools/prices-offers-pack.ts +1 -1
- package/src/modules/catalog/ai-tools/products-pack.ts +1 -1
- package/src/modules/catalog/ai-tools/variants-pack.ts +1 -1
- package/src/modules/communication_channels/data/entities.ts +2 -2
- package/src/modules/communication_channels/encryption.ts +1 -1
- package/src/modules/communication_channels/lib/adapter.ts +1 -1
- package/src/modules/communication_channels/lib/thread-matcher.ts +1 -1
- package/src/modules/communication_channels/lib/thread-token.ts +1 -1
- package/src/modules/currencies/api/currencies/route.ts +4 -3
- package/src/modules/customer_accounts/api/admin/roles.ts +2 -1
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.tsx +0 -3
- package/src/modules/customer_accounts/events.ts +1 -1
- package/src/modules/customer_accounts/lib/resolveTenantContext.ts +2 -2
- package/src/modules/customers/acl.ts +1 -1
- package/src/modules/customers/ai-tools/companies-pack.ts +1 -1
- package/src/modules/customers/ai-tools/deals-pack.ts +1 -1
- package/src/modules/customers/ai-tools/people-pack.ts +1 -1
- package/src/modules/customers/api/companies/route.ts +4 -4
- package/src/modules/customers/api/deals/route.ts +51 -2
- package/src/modules/customers/api/deals/summary/route.ts +496 -0
- package/src/modules/customers/api/people/route.ts +4 -4
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.ts +28 -6
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealData.ts +33 -6
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +17 -2
- package/src/modules/customers/backend/customers/deals/page.tsx +254 -66
- package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +1 -2
- package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +18 -0
- package/src/modules/customers/cli.ts +15 -15
- package/src/modules/customers/commands/addresses.ts +5 -5
- package/src/modules/customers/commands/comments.ts +5 -5
- package/src/modules/customers/commands/deals.ts +2 -2
- package/src/modules/customers/commands/entity-roles.ts +2 -1
- package/src/modules/customers/commands/interactions.ts +8 -5
- package/src/modules/customers/commands/shared.ts +26 -4
- package/src/modules/customers/commands/tags.ts +3 -3
- package/src/modules/customers/components/DealsKpiStrip.tsx +389 -0
- package/src/modules/customers/components/detail/ConfirmDealLostDialog.tsx +0 -1
- package/src/modules/customers/components/detail/DealForm.tsx +121 -19
- package/src/modules/customers/components/detail/PersonDetailTabs.tsx +12 -2
- package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +1 -2
- package/src/modules/customers/components/detail/assignableStaff.ts +32 -8
- package/src/modules/customers/components/kpi/PipelineStageBar.tsx +77 -0
- package/src/modules/customers/i18n/de.json +43 -0
- package/src/modules/customers/i18n/en.json +43 -0
- package/src/modules/customers/i18n/es.json +43 -0
- package/src/modules/customers/i18n/pl.json +43 -0
- package/src/modules/customers/lib/dealsMetrics.ts +159 -0
- package/src/modules/customers/migrations/Migration20260519120000_pipeline_stage_color_tones.ts +1 -1
- package/src/modules/data_sync/api/run.ts +1 -1
- package/src/modules/directory/api/organization-branding/route.ts +238 -0
- package/src/modules/directory/api/organizations/route.ts +7 -0
- package/src/modules/directory/backend/directory/branding/page.meta.ts +24 -0
- package/src/modules/directory/backend/directory/branding/page.tsx +248 -0
- package/src/modules/directory/commands/organizations.ts +9 -1
- package/src/modules/directory/data/entities.ts +3 -0
- package/src/modules/directory/data/validators.ts +12 -0
- package/src/modules/directory/i18n/de.json +21 -0
- package/src/modules/directory/i18n/en.json +21 -0
- package/src/modules/directory/i18n/es.json +21 -0
- package/src/modules/directory/i18n/pl.json +21 -0
- package/src/modules/directory/migrations/.snapshot-open-mercato.json +40 -0
- package/src/modules/directory/migrations/Migration20260607222259_directory.ts +13 -0
- package/src/modules/directory/subscribers/invalidateOrgScopeCache.ts +3 -1
- package/src/modules/directory/utils/organizationScope.ts +85 -30
- package/src/modules/entities/api/definitions.batch.ts +11 -7
- package/src/modules/entities/api/entities.ts +11 -0
- package/src/modules/entities/api/records.ts +46 -25
- package/src/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.tsx +15 -0
- package/src/modules/entities/backend/entities/user/[entityId]/records/create/page.tsx +15 -0
- package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +23 -0
- package/src/modules/entities/components/useRecordsEntityGuard.ts +41 -0
- package/src/modules/entities/i18n/de.json +1 -0
- package/src/modules/entities/i18n/en.json +1 -0
- package/src/modules/entities/i18n/es.json +1 -0
- package/src/modules/entities/i18n/pl.json +1 -0
- package/src/modules/payment_gateways/api/transactions/route.ts +2 -5
- package/src/modules/progress/api/jobs/[id]/route.ts +6 -1
- package/src/modules/progress/api/jobs/route.ts +1 -1
- package/src/modules/progress/lib/progressServiceImpl.ts +7 -1
- package/src/modules/query_index/data/entities.ts +1 -0
- package/src/modules/query_index/lib/engine.ts +11 -5
- package/src/modules/query_index/migrations/.snapshot-open-mercato.json +11 -0
- package/src/modules/query_index/migrations/Migration20260611103000_query_index.ts +29 -0
- package/src/modules/resources/api/resources.ts +2 -3
- package/src/modules/sales/api/documents/factory.ts +2 -2
- package/src/modules/sales/commands/documents.ts +7 -5
- package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +2 -1
- package/src/modules/sales/components/documents/salesDocumentsColumns.ts +6 -0
- package/src/modules/staff/AGENTS.md +1 -1
- package/src/modules/staff/api/team-members.ts +9 -2
- package/src/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.ts +31 -1
- package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +18 -8
- package/src/modules/staff/commands/team-members.ts +5 -2
- package/src/modules/staff/components/TeamMemberForm.tsx +4 -1
- package/src/modules/staff/i18n/de.json +1 -0
- package/src/modules/staff/i18n/en.json +1 -0
- package/src/modules/staff/i18n/es.json +1 -0
- package/src/modules/staff/i18n/pl.json +1 -0
- package/src/modules/staff/lib/scheduleSwitch.ts +46 -0
- package/src/modules/sync_excel/api/import/route.ts +1 -1
- package/src/modules/workflows/api/definitions/route.ts +3 -2
- package/src/modules/workflows/backend/definitions/create/page.tsx +1 -2
- package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +1 -2
- package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +1 -2
- package/src/modules/workflows/components/NodeEditDialog.tsx +1 -4
- package/src/modules/workflows/components/NodeEditDialogCrudForm.tsx +4 -7
- package/src/modules/workflows/components/WorkflowGraphImpl.tsx +1 -2
- package/src/modules/workflows/components/fields/FormFieldArrayEditor.tsx +2 -3
|
@@ -23,22 +23,46 @@ export type AssignableStaffMembersPage = {
|
|
|
23
23
|
pageSize: number
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
// The assignable-staff roster is owned by the optional, ejectable `staff` module.
|
|
27
|
+
// When that module is disabled, its `/api/staff/team-members/assignable` endpoint is
|
|
28
|
+
// absent and the request resolves to 404. Customers UI (deals / people / companies
|
|
29
|
+
// owner filters, role-assignment dialogs) is core and always enabled, so it must not
|
|
30
|
+
// break in that case — a missing staff module simply means there is no roster to
|
|
31
|
+
// offer. Treat the 404 as an empty page and let any other failure propagate.
|
|
32
|
+
function isAssignableEndpointMissing(error: unknown): boolean {
|
|
33
|
+
return (
|
|
34
|
+
typeof error === 'object' &&
|
|
35
|
+
error !== null &&
|
|
36
|
+
(error as { status?: unknown }).status === 404
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
26
40
|
export async function fetchAssignableStaffMembersPage(
|
|
27
41
|
query: string,
|
|
28
42
|
options?: { page?: number; pageSize?: number; signal?: AbortSignal },
|
|
29
43
|
): Promise<AssignableStaffMembersPage> {
|
|
44
|
+
const page = options?.page ?? 1
|
|
45
|
+
const pageSize = options?.pageSize ?? 24
|
|
30
46
|
const params = new URLSearchParams()
|
|
31
|
-
params.set('page', String(
|
|
32
|
-
params.set('pageSize', String(
|
|
47
|
+
params.set('page', String(page))
|
|
48
|
+
params.set('pageSize', String(pageSize))
|
|
33
49
|
const normalizedQuery = query.trim()
|
|
34
50
|
if (normalizedQuery.length > 0) {
|
|
35
51
|
params.set('search', normalizedQuery)
|
|
36
52
|
}
|
|
37
53
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
54
|
+
let data: AssignableStaffResponse
|
|
55
|
+
try {
|
|
56
|
+
data = await readApiResultOrThrow<AssignableStaffResponse>(
|
|
57
|
+
`/api/staff/team-members/assignable?${params.toString()}`,
|
|
58
|
+
options?.signal ? { signal: options.signal } : undefined,
|
|
59
|
+
)
|
|
60
|
+
} catch (error) {
|
|
61
|
+
if (isAssignableEndpointMissing(error)) {
|
|
62
|
+
return { items: [], total: 0, page, pageSize }
|
|
63
|
+
}
|
|
64
|
+
throw error
|
|
65
|
+
}
|
|
42
66
|
|
|
43
67
|
const rawItems = Array.isArray(data?.items) ? data.items : []
|
|
44
68
|
const deduped = new Map<string, AssignableStaffMember>()
|
|
@@ -108,11 +132,11 @@ export async function fetchAssignableStaffMembersPage(
|
|
|
108
132
|
page:
|
|
109
133
|
typeof data?.page === 'number' && Number.isFinite(data.page)
|
|
110
134
|
? data.page
|
|
111
|
-
:
|
|
135
|
+
: page,
|
|
112
136
|
pageSize:
|
|
113
137
|
typeof data?.pageSize === 'number' && Number.isFinite(data.pageSize)
|
|
114
138
|
? data.pageSize
|
|
115
|
-
:
|
|
139
|
+
: pageSize,
|
|
116
140
|
}
|
|
117
141
|
}
|
|
118
142
|
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { cn } from '@open-mercato/shared/lib/utils'
|
|
5
|
+
import type { DictionaryMap } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'
|
|
6
|
+
|
|
7
|
+
export type PipelineStageDatum = {
|
|
8
|
+
stage: string | null
|
|
9
|
+
count: number
|
|
10
|
+
value: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type PipelineStageBarProps = {
|
|
14
|
+
stages: PipelineStageDatum[]
|
|
15
|
+
stageDictionary: DictionaryMap
|
|
16
|
+
unassignedLabel: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function resolveStageColor(stage: string | null, stageDictionary: DictionaryMap): string | null {
|
|
20
|
+
if (!stage) return null
|
|
21
|
+
const entry = stageDictionary[stage]
|
|
22
|
+
return entry?.color ?? null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function resolveStageLabel(stage: string | null, stageDictionary: DictionaryMap, unassignedLabel: string): string {
|
|
26
|
+
if (!stage) return unassignedLabel
|
|
27
|
+
return stageDictionary[stage]?.label ?? unassignedLabel
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function PipelineStageBar({ stages, stageDictionary, unassignedLabel }: PipelineStageBarProps) {
|
|
31
|
+
const segments = React.useMemo(
|
|
32
|
+
() => stages.filter((entry) => entry.count > 0),
|
|
33
|
+
[stages],
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if (segments.length === 0) return null
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<div className="space-y-2">
|
|
40
|
+
<div className="flex h-2 w-full gap-1 overflow-hidden rounded-full">
|
|
41
|
+
{segments.map((entry, index) => {
|
|
42
|
+
const color = resolveStageColor(entry.stage, stageDictionary)
|
|
43
|
+
return (
|
|
44
|
+
<span
|
|
45
|
+
key={entry.stage ?? `unassigned-${index}`}
|
|
46
|
+
className={cn('h-full rounded-full', color ? null : 'bg-muted')}
|
|
47
|
+
style={{ flexGrow: entry.count, ...(color ? { backgroundColor: color } : {}) }}
|
|
48
|
+
aria-hidden
|
|
49
|
+
/>
|
|
50
|
+
)
|
|
51
|
+
})}
|
|
52
|
+
</div>
|
|
53
|
+
<ul className="flex flex-wrap gap-x-3 gap-y-1">
|
|
54
|
+
{segments.map((entry, index) => {
|
|
55
|
+
const color = resolveStageColor(entry.stage, stageDictionary)
|
|
56
|
+
const label = resolveStageLabel(entry.stage, stageDictionary, unassignedLabel)
|
|
57
|
+
return (
|
|
58
|
+
<li
|
|
59
|
+
key={entry.stage ?? `unassigned-legend-${index}`}
|
|
60
|
+
className="inline-flex items-center gap-1.5 text-xs text-muted-foreground"
|
|
61
|
+
>
|
|
62
|
+
<span
|
|
63
|
+
className={cn('inline-block h-2 w-2 rounded-full border border-border', color ? null : 'bg-muted')}
|
|
64
|
+
style={color ? { backgroundColor: color } : undefined}
|
|
65
|
+
aria-hidden
|
|
66
|
+
/>
|
|
67
|
+
<span>{label}</span>
|
|
68
|
+
<span className="font-medium text-foreground">{entry.count}</span>
|
|
69
|
+
</li>
|
|
70
|
+
)
|
|
71
|
+
})}
|
|
72
|
+
</ul>
|
|
73
|
+
</div>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export default PipelineStageBar
|
|
@@ -1154,6 +1154,9 @@
|
|
|
1154
1154
|
"customers.deals.list.bulkDelete.progressName": "Ausgewählte Deals löschen",
|
|
1155
1155
|
"customers.deals.list.bulkDelete.success": "{count} deals deleted",
|
|
1156
1156
|
"customers.deals.list.bulkDelete.title": "Delete {count} deals?",
|
|
1157
|
+
"customers.deals.list.close.lost": "Verloren",
|
|
1158
|
+
"customers.deals.list.close.overdue": "Überfällig",
|
|
1159
|
+
"customers.deals.list.close.won": "Gewonnen",
|
|
1157
1160
|
"customers.deals.list.columns.companies": "Unternehmen",
|
|
1158
1161
|
"customers.deals.list.columns.expectedClose": "Voraussichtlicher Abschluss",
|
|
1159
1162
|
"customers.deals.list.columns.owner": "Verantwortlich",
|
|
@@ -1171,12 +1174,52 @@
|
|
|
1171
1174
|
"customers.deals.list.error.load": "Geschäftsliste konnte nicht geladen werden",
|
|
1172
1175
|
"customers.deals.list.filters.companies": "Unternehmen",
|
|
1173
1176
|
"customers.deals.list.filters.companiesPlaceholder": "Nach Unternehmen filtern",
|
|
1177
|
+
"customers.deals.list.filters.needsAttention": "Benötigt Aufmerksamkeit",
|
|
1178
|
+
"customers.deals.list.filters.needsAttentionRemove": "Filter \"Benötigt Aufmerksamkeit\" entfernen",
|
|
1174
1179
|
"customers.deals.list.filters.people": "Personen",
|
|
1175
1180
|
"customers.deals.list.filters.peoplePlaceholder": "Nach Personen filtern",
|
|
1181
|
+
"customers.deals.list.kpi.activeAcrossPipelines": "{deals} in {pipelines}",
|
|
1182
|
+
"customers.deals.list.kpi.activeDeals": "Aktive Deals",
|
|
1183
|
+
"customers.deals.list.kpi.avgDeal": "Ø Deal {value}",
|
|
1184
|
+
"customers.deals.list.kpi.currencyApproxMissing": "Näherungswert; Deals in {currencies} ohne Wechselkurs sind ausgeschlossen.",
|
|
1185
|
+
"customers.deals.list.kpi.currencyApproxNoBase": "Näherungswert; keine Basiswährung ist konfiguriert.",
|
|
1186
|
+
"customers.deals.list.kpi.deltaTooltip": "Im Vergleich zum Vorzeitraum",
|
|
1187
|
+
"customers.deals.list.kpi.deltaUnavailable": "Keine vergleichbare Änderung",
|
|
1188
|
+
"customers.deals.list.kpi.error": "Metriken konnten nicht geladen werden",
|
|
1189
|
+
"customers.deals.list.kpi.frag.activeDeals.few": "{count} aktive Deals",
|
|
1190
|
+
"customers.deals.list.kpi.frag.activeDeals.many": "{count} aktive Deals",
|
|
1191
|
+
"customers.deals.list.kpi.frag.activeDeals.one": "{count} aktiver Deal",
|
|
1192
|
+
"customers.deals.list.kpi.frag.activeDeals.other": "{count} aktive Deals",
|
|
1193
|
+
"customers.deals.list.kpi.frag.dealsClosed.few": "{count} Deals in diesem Quartal abgeschlossen",
|
|
1194
|
+
"customers.deals.list.kpi.frag.dealsClosed.many": "{count} Deals in diesem Quartal abgeschlossen",
|
|
1195
|
+
"customers.deals.list.kpi.frag.dealsClosed.one": "{count} Deal in diesem Quartal abgeschlossen",
|
|
1196
|
+
"customers.deals.list.kpi.frag.dealsClosed.other": "{count} Deals in diesem Quartal abgeschlossen",
|
|
1197
|
+
"customers.deals.list.kpi.frag.needAttention.few": "{count} benötigen Aufmerksamkeit",
|
|
1198
|
+
"customers.deals.list.kpi.frag.needAttention.many": "{count} benötigen Aufmerksamkeit",
|
|
1199
|
+
"customers.deals.list.kpi.frag.needAttention.one": "{count} benötigt Aufmerksamkeit",
|
|
1200
|
+
"customers.deals.list.kpi.frag.needAttention.other": "{count} benötigen Aufmerksamkeit",
|
|
1201
|
+
"customers.deals.list.kpi.frag.owners.few": "{count} Inhaber",
|
|
1202
|
+
"customers.deals.list.kpi.frag.owners.many": "{count} Inhaber",
|
|
1203
|
+
"customers.deals.list.kpi.frag.owners.one": "{count} Inhaber",
|
|
1204
|
+
"customers.deals.list.kpi.frag.owners.other": "{count} Inhaber",
|
|
1205
|
+
"customers.deals.list.kpi.frag.pipelines.few": "{count} Pipelines",
|
|
1206
|
+
"customers.deals.list.kpi.frag.pipelines.many": "{count} Pipelines",
|
|
1207
|
+
"customers.deals.list.kpi.frag.pipelines.one": "{count} Pipeline",
|
|
1208
|
+
"customers.deals.list.kpi.frag.pipelines.other": "{count} Pipelines",
|
|
1209
|
+
"customers.deals.list.kpi.fromLastQuarter": "von {value}% im letzten Quartal",
|
|
1210
|
+
"customers.deals.list.kpi.ownersNeedAttention": "{owners} · {attention}",
|
|
1211
|
+
"customers.deals.list.kpi.pipelineValue": "Pipeline-Wert",
|
|
1212
|
+
"customers.deals.list.kpi.retry": "Erneut versuchen",
|
|
1213
|
+
"customers.deals.list.kpi.scopeAllPipelinesThisQuarter": "Alle Pipelines · dieses Quartal",
|
|
1214
|
+
"customers.deals.list.kpi.unassignedStage": "Nicht zugewiesen",
|
|
1215
|
+
"customers.deals.list.kpi.updating": "Metriken werden aktualisiert",
|
|
1216
|
+
"customers.deals.list.kpi.winRate": "Gewinnrate",
|
|
1217
|
+
"customers.deals.list.kpi.wonThisQuarter": "Gewonnen dieses Quartal",
|
|
1176
1218
|
"customers.deals.list.noValue": "Nicht gesetzt",
|
|
1177
1219
|
"customers.deals.list.refresh": "Aktualisieren",
|
|
1178
1220
|
"customers.deals.list.searchPlaceholder": "Geschäfte suchen…",
|
|
1179
1221
|
"customers.deals.list.title": "Geschäfte",
|
|
1222
|
+
"customers.deals.list.unknownOwner": "Unbekannter Inhaber",
|
|
1180
1223
|
"customers.deals.list.unnamedCompany": "Unbenanntes Unternehmen",
|
|
1181
1224
|
"customers.deals.list.unnamedPerson": "Unbenannte Person",
|
|
1182
1225
|
"customers.deals.pipeline.actions.openDeal": "Deal öffnen",
|
|
@@ -1154,6 +1154,9 @@
|
|
|
1154
1154
|
"customers.deals.list.bulkDelete.progressName": "Delete selected deals",
|
|
1155
1155
|
"customers.deals.list.bulkDelete.success": "{count} deals deleted",
|
|
1156
1156
|
"customers.deals.list.bulkDelete.title": "Delete {count} deals?",
|
|
1157
|
+
"customers.deals.list.close.lost": "Lost",
|
|
1158
|
+
"customers.deals.list.close.overdue": "Overdue",
|
|
1159
|
+
"customers.deals.list.close.won": "Won",
|
|
1157
1160
|
"customers.deals.list.columns.companies": "Companies",
|
|
1158
1161
|
"customers.deals.list.columns.expectedClose": "Expected close",
|
|
1159
1162
|
"customers.deals.list.columns.owner": "Owner",
|
|
@@ -1171,12 +1174,52 @@
|
|
|
1171
1174
|
"customers.deals.list.error.load": "Unable to load deals list",
|
|
1172
1175
|
"customers.deals.list.filters.companies": "Companies",
|
|
1173
1176
|
"customers.deals.list.filters.companiesPlaceholder": "Filter by companies",
|
|
1177
|
+
"customers.deals.list.filters.needsAttention": "Needs attention",
|
|
1178
|
+
"customers.deals.list.filters.needsAttentionRemove": "Remove needs attention filter",
|
|
1174
1179
|
"customers.deals.list.filters.people": "People",
|
|
1175
1180
|
"customers.deals.list.filters.peoplePlaceholder": "Filter by people",
|
|
1181
|
+
"customers.deals.list.kpi.activeAcrossPipelines": "{deals} across {pipelines}",
|
|
1182
|
+
"customers.deals.list.kpi.activeDeals": "Active deals",
|
|
1183
|
+
"customers.deals.list.kpi.avgDeal": "avg deal {value}",
|
|
1184
|
+
"customers.deals.list.kpi.currencyApproxMissing": "Approximate; deals in {currencies} without a rate are excluded.",
|
|
1185
|
+
"customers.deals.list.kpi.currencyApproxNoBase": "Approximate; no base currency is configured.",
|
|
1186
|
+
"customers.deals.list.kpi.deltaTooltip": "Compared to previous period",
|
|
1187
|
+
"customers.deals.list.kpi.deltaUnavailable": "No comparable change",
|
|
1188
|
+
"customers.deals.list.kpi.error": "Couldn't load deal metrics",
|
|
1189
|
+
"customers.deals.list.kpi.frag.activeDeals.few": "{count} active deals",
|
|
1190
|
+
"customers.deals.list.kpi.frag.activeDeals.many": "{count} active deals",
|
|
1191
|
+
"customers.deals.list.kpi.frag.activeDeals.one": "{count} active deal",
|
|
1192
|
+
"customers.deals.list.kpi.frag.activeDeals.other": "{count} active deals",
|
|
1193
|
+
"customers.deals.list.kpi.frag.dealsClosed.few": "{count} deals closed this quarter",
|
|
1194
|
+
"customers.deals.list.kpi.frag.dealsClosed.many": "{count} deals closed this quarter",
|
|
1195
|
+
"customers.deals.list.kpi.frag.dealsClosed.one": "{count} deal closed this quarter",
|
|
1196
|
+
"customers.deals.list.kpi.frag.dealsClosed.other": "{count} deals closed this quarter",
|
|
1197
|
+
"customers.deals.list.kpi.frag.needAttention.few": "{count} need attention",
|
|
1198
|
+
"customers.deals.list.kpi.frag.needAttention.many": "{count} need attention",
|
|
1199
|
+
"customers.deals.list.kpi.frag.needAttention.one": "{count} needs attention",
|
|
1200
|
+
"customers.deals.list.kpi.frag.needAttention.other": "{count} need attention",
|
|
1201
|
+
"customers.deals.list.kpi.frag.owners.few": "{count} owners",
|
|
1202
|
+
"customers.deals.list.kpi.frag.owners.many": "{count} owners",
|
|
1203
|
+
"customers.deals.list.kpi.frag.owners.one": "{count} owner",
|
|
1204
|
+
"customers.deals.list.kpi.frag.owners.other": "{count} owners",
|
|
1205
|
+
"customers.deals.list.kpi.frag.pipelines.few": "{count} pipelines",
|
|
1206
|
+
"customers.deals.list.kpi.frag.pipelines.many": "{count} pipelines",
|
|
1207
|
+
"customers.deals.list.kpi.frag.pipelines.one": "{count} pipeline",
|
|
1208
|
+
"customers.deals.list.kpi.frag.pipelines.other": "{count} pipelines",
|
|
1209
|
+
"customers.deals.list.kpi.fromLastQuarter": "from {value}% last quarter",
|
|
1210
|
+
"customers.deals.list.kpi.ownersNeedAttention": "{owners} · {attention}",
|
|
1211
|
+
"customers.deals.list.kpi.pipelineValue": "Pipeline value",
|
|
1212
|
+
"customers.deals.list.kpi.retry": "Retry",
|
|
1213
|
+
"customers.deals.list.kpi.scopeAllPipelinesThisQuarter": "All pipelines · this quarter",
|
|
1214
|
+
"customers.deals.list.kpi.unassignedStage": "Unassigned",
|
|
1215
|
+
"customers.deals.list.kpi.updating": "Updating metrics",
|
|
1216
|
+
"customers.deals.list.kpi.winRate": "Win rate",
|
|
1217
|
+
"customers.deals.list.kpi.wonThisQuarter": "Won this quarter",
|
|
1176
1218
|
"customers.deals.list.noValue": "Not set",
|
|
1177
1219
|
"customers.deals.list.refresh": "Refresh",
|
|
1178
1220
|
"customers.deals.list.searchPlaceholder": "Search by title, description…",
|
|
1179
1221
|
"customers.deals.list.title": "Deals",
|
|
1222
|
+
"customers.deals.list.unknownOwner": "Unknown owner",
|
|
1180
1223
|
"customers.deals.list.unnamedCompany": "Unnamed company",
|
|
1181
1224
|
"customers.deals.list.unnamedPerson": "Unnamed person",
|
|
1182
1225
|
"customers.deals.pipeline.actions.openDeal": "Open deal",
|
|
@@ -1154,6 +1154,9 @@
|
|
|
1154
1154
|
"customers.deals.list.bulkDelete.progressName": "Eliminar negocios seleccionados",
|
|
1155
1155
|
"customers.deals.list.bulkDelete.success": "{count} deals deleted",
|
|
1156
1156
|
"customers.deals.list.bulkDelete.title": "Delete {count} deals?",
|
|
1157
|
+
"customers.deals.list.close.lost": "Perdido",
|
|
1158
|
+
"customers.deals.list.close.overdue": "Vencido",
|
|
1159
|
+
"customers.deals.list.close.won": "Ganado",
|
|
1157
1160
|
"customers.deals.list.columns.companies": "Empresas",
|
|
1158
1161
|
"customers.deals.list.columns.expectedClose": "Cierre esperado",
|
|
1159
1162
|
"customers.deals.list.columns.owner": "Responsable",
|
|
@@ -1171,12 +1174,52 @@
|
|
|
1171
1174
|
"customers.deals.list.error.load": "No se pudo cargar la lista de negocios",
|
|
1172
1175
|
"customers.deals.list.filters.companies": "Empresas",
|
|
1173
1176
|
"customers.deals.list.filters.companiesPlaceholder": "Filtrar por empresas",
|
|
1177
|
+
"customers.deals.list.filters.needsAttention": "Requiere atención",
|
|
1178
|
+
"customers.deals.list.filters.needsAttentionRemove": "Quitar filtro de requiere atención",
|
|
1174
1179
|
"customers.deals.list.filters.people": "Personas",
|
|
1175
1180
|
"customers.deals.list.filters.peoplePlaceholder": "Filtrar por personas",
|
|
1181
|
+
"customers.deals.list.kpi.activeAcrossPipelines": "{deals} en {pipelines}",
|
|
1182
|
+
"customers.deals.list.kpi.activeDeals": "Negocios activos",
|
|
1183
|
+
"customers.deals.list.kpi.avgDeal": "negocio medio {value}",
|
|
1184
|
+
"customers.deals.list.kpi.currencyApproxMissing": "Aproximado; se excluyen los negocios en {currencies} sin tipo de cambio.",
|
|
1185
|
+
"customers.deals.list.kpi.currencyApproxNoBase": "Aproximado; no hay moneda base configurada.",
|
|
1186
|
+
"customers.deals.list.kpi.deltaTooltip": "Comparado con el período anterior",
|
|
1187
|
+
"customers.deals.list.kpi.deltaUnavailable": "Sin cambio comparable",
|
|
1188
|
+
"customers.deals.list.kpi.error": "No se pudieron cargar las métricas",
|
|
1189
|
+
"customers.deals.list.kpi.frag.activeDeals.few": "{count} negocios activos",
|
|
1190
|
+
"customers.deals.list.kpi.frag.activeDeals.many": "{count} negocios activos",
|
|
1191
|
+
"customers.deals.list.kpi.frag.activeDeals.one": "{count} negocio activo",
|
|
1192
|
+
"customers.deals.list.kpi.frag.activeDeals.other": "{count} negocios activos",
|
|
1193
|
+
"customers.deals.list.kpi.frag.dealsClosed.few": "{count} negocios cerrados este trimestre",
|
|
1194
|
+
"customers.deals.list.kpi.frag.dealsClosed.many": "{count} negocios cerrados este trimestre",
|
|
1195
|
+
"customers.deals.list.kpi.frag.dealsClosed.one": "{count} negocio cerrado este trimestre",
|
|
1196
|
+
"customers.deals.list.kpi.frag.dealsClosed.other": "{count} negocios cerrados este trimestre",
|
|
1197
|
+
"customers.deals.list.kpi.frag.needAttention.few": "{count} requieren atención",
|
|
1198
|
+
"customers.deals.list.kpi.frag.needAttention.many": "{count} requieren atención",
|
|
1199
|
+
"customers.deals.list.kpi.frag.needAttention.one": "{count} requiere atención",
|
|
1200
|
+
"customers.deals.list.kpi.frag.needAttention.other": "{count} requieren atención",
|
|
1201
|
+
"customers.deals.list.kpi.frag.owners.few": "{count} responsables",
|
|
1202
|
+
"customers.deals.list.kpi.frag.owners.many": "{count} responsables",
|
|
1203
|
+
"customers.deals.list.kpi.frag.owners.one": "{count} responsable",
|
|
1204
|
+
"customers.deals.list.kpi.frag.owners.other": "{count} responsables",
|
|
1205
|
+
"customers.deals.list.kpi.frag.pipelines.few": "{count} pipelines",
|
|
1206
|
+
"customers.deals.list.kpi.frag.pipelines.many": "{count} pipelines",
|
|
1207
|
+
"customers.deals.list.kpi.frag.pipelines.one": "{count} pipeline",
|
|
1208
|
+
"customers.deals.list.kpi.frag.pipelines.other": "{count} pipelines",
|
|
1209
|
+
"customers.deals.list.kpi.fromLastQuarter": "desde {value}% el trimestre pasado",
|
|
1210
|
+
"customers.deals.list.kpi.ownersNeedAttention": "{owners} · {attention}",
|
|
1211
|
+
"customers.deals.list.kpi.pipelineValue": "Valor del pipeline",
|
|
1212
|
+
"customers.deals.list.kpi.retry": "Reintentar",
|
|
1213
|
+
"customers.deals.list.kpi.scopeAllPipelinesThisQuarter": "Todos los pipelines · este trimestre",
|
|
1214
|
+
"customers.deals.list.kpi.unassignedStage": "Sin asignar",
|
|
1215
|
+
"customers.deals.list.kpi.updating": "Actualizando métricas",
|
|
1216
|
+
"customers.deals.list.kpi.winRate": "Tasa de éxito",
|
|
1217
|
+
"customers.deals.list.kpi.wonThisQuarter": "Ganados este trimestre",
|
|
1176
1218
|
"customers.deals.list.noValue": "Sin definir",
|
|
1177
1219
|
"customers.deals.list.refresh": "Actualizar",
|
|
1178
1220
|
"customers.deals.list.searchPlaceholder": "Buscar negocios…",
|
|
1179
1221
|
"customers.deals.list.title": "Negocios",
|
|
1222
|
+
"customers.deals.list.unknownOwner": "Responsable desconocido",
|
|
1180
1223
|
"customers.deals.list.unnamedCompany": "Empresa sin nombre",
|
|
1181
1224
|
"customers.deals.list.unnamedPerson": "Persona sin nombre",
|
|
1182
1225
|
"customers.deals.pipeline.actions.openDeal": "Abrir oportunidad",
|
|
@@ -1154,6 +1154,9 @@
|
|
|
1154
1154
|
"customers.deals.list.bulkDelete.progressName": "Usuń zaznaczone transakcje",
|
|
1155
1155
|
"customers.deals.list.bulkDelete.success": "{count} deals deleted",
|
|
1156
1156
|
"customers.deals.list.bulkDelete.title": "Delete {count} deals?",
|
|
1157
|
+
"customers.deals.list.close.lost": "Przegrana",
|
|
1158
|
+
"customers.deals.list.close.overdue": "Po terminie",
|
|
1159
|
+
"customers.deals.list.close.won": "Wygrana",
|
|
1157
1160
|
"customers.deals.list.columns.companies": "Firmy",
|
|
1158
1161
|
"customers.deals.list.columns.expectedClose": "Planowana finalizacja",
|
|
1159
1162
|
"customers.deals.list.columns.owner": "Właściciel",
|
|
@@ -1171,12 +1174,52 @@
|
|
|
1171
1174
|
"customers.deals.list.error.load": "Nie udało się wczytać listy szans",
|
|
1172
1175
|
"customers.deals.list.filters.companies": "Firmy",
|
|
1173
1176
|
"customers.deals.list.filters.companiesPlaceholder": "Filtruj po firmach",
|
|
1177
|
+
"customers.deals.list.filters.needsAttention": "Wymaga uwagi",
|
|
1178
|
+
"customers.deals.list.filters.needsAttentionRemove": "Usuń filtr wymaga uwagi",
|
|
1174
1179
|
"customers.deals.list.filters.people": "Osoby",
|
|
1175
1180
|
"customers.deals.list.filters.peoplePlaceholder": "Filtruj po osobach",
|
|
1181
|
+
"customers.deals.list.kpi.activeAcrossPipelines": "{deals} w {pipelines}",
|
|
1182
|
+
"customers.deals.list.kpi.activeDeals": "Aktywne szanse",
|
|
1183
|
+
"customers.deals.list.kpi.avgDeal": "śr. szansa {value}",
|
|
1184
|
+
"customers.deals.list.kpi.currencyApproxMissing": "Wartość przybliżona; szanse w {currencies} bez kursu są pomijane.",
|
|
1185
|
+
"customers.deals.list.kpi.currencyApproxNoBase": "Wartość przybliżona; nie skonfigurowano waluty bazowej.",
|
|
1186
|
+
"customers.deals.list.kpi.deltaTooltip": "W porównaniu z poprzednim okresem",
|
|
1187
|
+
"customers.deals.list.kpi.deltaUnavailable": "Brak porównywalnej zmiany",
|
|
1188
|
+
"customers.deals.list.kpi.error": "Nie udało się załadować metryk",
|
|
1189
|
+
"customers.deals.list.kpi.frag.activeDeals.few": "{count} aktywne szanse",
|
|
1190
|
+
"customers.deals.list.kpi.frag.activeDeals.many": "{count} aktywnych szans",
|
|
1191
|
+
"customers.deals.list.kpi.frag.activeDeals.one": "{count} aktywna szansa",
|
|
1192
|
+
"customers.deals.list.kpi.frag.activeDeals.other": "{count} aktywnych szans",
|
|
1193
|
+
"customers.deals.list.kpi.frag.dealsClosed.few": "{count} szanse zamknięte w tym kwartale",
|
|
1194
|
+
"customers.deals.list.kpi.frag.dealsClosed.many": "{count} szans zamkniętych w tym kwartale",
|
|
1195
|
+
"customers.deals.list.kpi.frag.dealsClosed.one": "{count} szansa zamknięta w tym kwartale",
|
|
1196
|
+
"customers.deals.list.kpi.frag.dealsClosed.other": "{count} szans zamkniętych w tym kwartale",
|
|
1197
|
+
"customers.deals.list.kpi.frag.needAttention.few": "{count} wymagają uwagi",
|
|
1198
|
+
"customers.deals.list.kpi.frag.needAttention.many": "{count} wymaga uwagi",
|
|
1199
|
+
"customers.deals.list.kpi.frag.needAttention.one": "{count} wymaga uwagi",
|
|
1200
|
+
"customers.deals.list.kpi.frag.needAttention.other": "{count} wymaga uwagi",
|
|
1201
|
+
"customers.deals.list.kpi.frag.owners.few": "{count} właścicieli",
|
|
1202
|
+
"customers.deals.list.kpi.frag.owners.many": "{count} właścicieli",
|
|
1203
|
+
"customers.deals.list.kpi.frag.owners.one": "{count} właściciel",
|
|
1204
|
+
"customers.deals.list.kpi.frag.owners.other": "{count} właścicieli",
|
|
1205
|
+
"customers.deals.list.kpi.frag.pipelines.few": "{count} pipeline'y",
|
|
1206
|
+
"customers.deals.list.kpi.frag.pipelines.many": "{count} pipeline'ów",
|
|
1207
|
+
"customers.deals.list.kpi.frag.pipelines.one": "{count} pipeline",
|
|
1208
|
+
"customers.deals.list.kpi.frag.pipelines.other": "{count} pipeline'ów",
|
|
1209
|
+
"customers.deals.list.kpi.fromLastQuarter": "z {value}% w zeszłym kwartale",
|
|
1210
|
+
"customers.deals.list.kpi.ownersNeedAttention": "{owners} · {attention}",
|
|
1211
|
+
"customers.deals.list.kpi.pipelineValue": "Wartość pipeline'u",
|
|
1212
|
+
"customers.deals.list.kpi.retry": "Ponów",
|
|
1213
|
+
"customers.deals.list.kpi.scopeAllPipelinesThisQuarter": "Wszystkie pipeline'y · ten kwartał",
|
|
1214
|
+
"customers.deals.list.kpi.unassignedStage": "Nieprzypisany",
|
|
1215
|
+
"customers.deals.list.kpi.updating": "Aktualizowanie metryk",
|
|
1216
|
+
"customers.deals.list.kpi.winRate": "Skuteczność",
|
|
1217
|
+
"customers.deals.list.kpi.wonThisQuarter": "Wygrane w tym kwartale",
|
|
1176
1218
|
"customers.deals.list.noValue": "Brak danych",
|
|
1177
1219
|
"customers.deals.list.refresh": "Odśwież",
|
|
1178
1220
|
"customers.deals.list.searchPlaceholder": "Szukaj szans…",
|
|
1179
1221
|
"customers.deals.list.title": "Szanse",
|
|
1222
|
+
"customers.deals.list.unknownOwner": "Nieznany właściciel",
|
|
1180
1223
|
"customers.deals.list.unnamedCompany": "Firma bez nazwy",
|
|
1181
1224
|
"customers.deals.list.unnamedPerson": "Osoba bez nazwy",
|
|
1182
1225
|
"customers.deals.pipeline.actions.openDeal": "Otwórz szansę",
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import type { RateResult } from '@open-mercato/core/modules/currencies/services/exchangeRateService'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Quarter / period helpers for the deals KPI summary. Computed in **UTC** so the
|
|
5
|
+
* window boundaries are stable regardless of the server timezone — `expected_close_at`
|
|
6
|
+
* is a bare `Date` (date-only) while `created_at` / `updated_at` are timestamptz, and
|
|
7
|
+
* mixing local-time boundaries would misbucket deals near a quarter edge.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type PeriodWindow = {
|
|
11
|
+
/** Inclusive lower bound (UTC). */
|
|
12
|
+
start: Date
|
|
13
|
+
/** Exclusive upper bound (UTC). */
|
|
14
|
+
end: Date
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type TrailingMonth = {
|
|
18
|
+
/** Inclusive lower bound (UTC) of the month bucket. */
|
|
19
|
+
start: Date
|
|
20
|
+
/** 'YYYY-MM' label for the bucket. */
|
|
21
|
+
label: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type DeltaDirection = 'up' | 'down' | 'unchanged'
|
|
25
|
+
|
|
26
|
+
export type Delta = {
|
|
27
|
+
value: number
|
|
28
|
+
direction: DeltaDirection
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type CurrencySum = {
|
|
32
|
+
currency: string
|
|
33
|
+
total: number
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type ConvertedSums = {
|
|
37
|
+
total: number
|
|
38
|
+
convertedAll: boolean
|
|
39
|
+
missingRateCurrencies: string[]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function startOfQuarterUtc(year: number, quarterStartMonth: number): Date {
|
|
43
|
+
return new Date(Date.UTC(year, quarterStartMonth, 1, 0, 0, 0, 0))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Returns the [start, end) window of the calendar quarter that contains `now`,
|
|
48
|
+
* in UTC. Quarters are fixed 3-month blocks: Jan–Mar, Apr–Jun, Jul–Sep, Oct–Dec.
|
|
49
|
+
* `end` is exclusive (the start of the next quarter).
|
|
50
|
+
*/
|
|
51
|
+
export function getQuarterWindow(now: Date): PeriodWindow {
|
|
52
|
+
const year = now.getUTCFullYear()
|
|
53
|
+
const quarterIndex = Math.floor(now.getUTCMonth() / 3)
|
|
54
|
+
const startMonth = quarterIndex * 3
|
|
55
|
+
const start = startOfQuarterUtc(year, startMonth)
|
|
56
|
+
const end = startOfQuarterUtc(year, startMonth + 3)
|
|
57
|
+
return { start, end }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Returns the [start, end) window of the quarter immediately preceding the one
|
|
62
|
+
* that contains `now`, in UTC. `end` is exclusive and equals the current quarter's start.
|
|
63
|
+
*/
|
|
64
|
+
export function getPreviousQuarterWindow(now: Date): PeriodWindow {
|
|
65
|
+
const current = getQuarterWindow(now)
|
|
66
|
+
const start = startOfQuarterUtc(current.start.getUTCFullYear(), current.start.getUTCMonth() - 3)
|
|
67
|
+
return { start, end: current.start }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function monthLabel(year: number, monthIndex: number): string {
|
|
71
|
+
const month = monthIndex + 1
|
|
72
|
+
return `${year}-${month < 10 ? `0${month}` : month}`
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Returns `count` trailing month buckets ending with the month that contains `now`,
|
|
77
|
+
* ordered oldest → newest. Each bucket exposes its UTC start and a 'YYYY-MM' label.
|
|
78
|
+
* Used to drive the win-rate sparkline series.
|
|
79
|
+
*/
|
|
80
|
+
export function getTrailingMonths(now: Date, count: number): TrailingMonth[] {
|
|
81
|
+
const buckets: TrailingMonth[] = []
|
|
82
|
+
const baseYear = now.getUTCFullYear()
|
|
83
|
+
const baseMonth = now.getUTCMonth()
|
|
84
|
+
for (let offset = count - 1; offset >= 0; offset -= 1) {
|
|
85
|
+
const start = new Date(Date.UTC(baseYear, baseMonth - offset, 1, 0, 0, 0, 0))
|
|
86
|
+
buckets.push({ start, label: monthLabel(start.getUTCFullYear(), start.getUTCMonth()) })
|
|
87
|
+
}
|
|
88
|
+
return buckets
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Percentage change of `current` relative to `previous`, rounded to whole percent.
|
|
93
|
+
* When there is no previous-period baseline, avoid reporting artificial growth.
|
|
94
|
+
*/
|
|
95
|
+
export function computeDelta(current: number, previous: number): Delta {
|
|
96
|
+
if (previous === 0) {
|
|
97
|
+
return { value: 0, direction: 'unchanged' }
|
|
98
|
+
}
|
|
99
|
+
const change = ((current - previous) / Math.abs(previous)) * 100
|
|
100
|
+
const value = Math.round(change)
|
|
101
|
+
if (value > 0) return { value, direction: 'up' }
|
|
102
|
+
if (value < 0) return { value, direction: 'down' }
|
|
103
|
+
return { value: 0, direction: 'unchanged' }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function extractRate(result: RateResult | undefined): number | null {
|
|
107
|
+
if (!result || result.rates.length === 0) return null
|
|
108
|
+
const rate = Number(result.rates[0].rate)
|
|
109
|
+
if (!Number.isFinite(rate) || rate <= 0) return null
|
|
110
|
+
return rate
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Converts per-currency sums to the tenant base currency, mirroring the conversion
|
|
115
|
+
* logic in `api/deals/aggregate/route.ts`:
|
|
116
|
+
* - the base currency stays 1:1,
|
|
117
|
+
* - other currencies multiply by the rate from `rates` (keyed `"FROM/BASE"`),
|
|
118
|
+
* - a currency with no usable rate is excluded from `total` and flagged in
|
|
119
|
+
* `missingRateCurrencies` (with `convertedAll: false`).
|
|
120
|
+
*
|
|
121
|
+
* When `baseCode` is null there is no base currency configured, so nothing can be
|
|
122
|
+
* converted: every present currency is reported as missing and `convertedAll` is false.
|
|
123
|
+
*
|
|
124
|
+
* `rates` accepts the `Map<string, RateResult>` shape returned by
|
|
125
|
+
* `exchangeRateService.getRates` so callers can pass its output directly.
|
|
126
|
+
*/
|
|
127
|
+
export function convertSumsToBase(
|
|
128
|
+
perCurrency: CurrencySum[],
|
|
129
|
+
baseCode: string | null,
|
|
130
|
+
rates: Map<string, RateResult>,
|
|
131
|
+
): ConvertedSums {
|
|
132
|
+
if (!baseCode) {
|
|
133
|
+
const missing = Array.from(
|
|
134
|
+
new Set(perCurrency.map((entry) => entry.currency).filter((code): code is string => Boolean(code))),
|
|
135
|
+
)
|
|
136
|
+
return { total: 0, convertedAll: missing.length === 0, missingRateCurrencies: missing }
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
let total = 0
|
|
140
|
+
let convertedAll = true
|
|
141
|
+
const missingRateCurrencies: string[] = []
|
|
142
|
+
for (const entry of perCurrency) {
|
|
143
|
+
if (!entry.currency) continue
|
|
144
|
+
if (entry.currency === baseCode) {
|
|
145
|
+
total += entry.total
|
|
146
|
+
continue
|
|
147
|
+
}
|
|
148
|
+
const rate = extractRate(rates.get(`${entry.currency}/${baseCode}`))
|
|
149
|
+
if (rate !== null) {
|
|
150
|
+
total += entry.total * rate
|
|
151
|
+
} else {
|
|
152
|
+
convertedAll = false
|
|
153
|
+
if (!missingRateCurrencies.includes(entry.currency)) {
|
|
154
|
+
missingRateCurrencies.push(entry.currency)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return { total: Math.round(total), convertedAll, missingRateCurrencies }
|
|
159
|
+
}
|
package/src/modules/customers/migrations/Migration20260519120000_pipeline_stage_color_tones.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { Migration } from '@mikro-orm/migrations';
|
|
|
15
15
|
* 'pink', '', or NULL. Lane.tsx now reads the value directly as a tone identifier.
|
|
16
16
|
*
|
|
17
17
|
* Unknown / unmappable hex values collapse to 'neutral'. Forward-only — reverting tones to
|
|
18
|
-
* the original hex is lossy and not supported. See `.ai/specs/2026-05-19-customers-deals-kanban-ux-review-fixes.md`.
|
|
18
|
+
* the original hex is lossy and not supported. See `.ai/specs/implemented/2026-05-19-customers-deals-kanban-ux-review-fixes.md`.
|
|
19
19
|
*/
|
|
20
20
|
export class Migration20260519120000_pipeline_stage_color_tones extends Migration {
|
|
21
21
|
override up(): void | Promise<void> {
|
|
@@ -107,7 +107,7 @@ export async function POST(req: Request) {
|
|
|
107
107
|
const stack = error instanceof Error ? error.stack : undefined
|
|
108
108
|
console.error('[data_sync.run] unhandled error', { message, stack })
|
|
109
109
|
return NextResponse.json(
|
|
110
|
-
{ error: 'Failed to start data sync run.'
|
|
110
|
+
{ error: 'Failed to start data sync run.' },
|
|
111
111
|
{ status: 500 },
|
|
112
112
|
)
|
|
113
113
|
}
|