@open-mercato/core 0.10.1-develop.7716.1.cefe7975df → 0.10.1-develop.7721.1.b5c4a00a68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +2 -2
- package/dist/generated/entities/staff_support_routing_rule/index.js +21 -0
- package/dist/generated/entities/staff_support_routing_rule/index.js.map +7 -0
- package/dist/generated/entities/staff_support_ticket/index.js +37 -0
- package/dist/generated/entities/staff_support_ticket/index.js.map +7 -0
- package/dist/generated/entities/staff_time_project/index.js +2 -0
- package/dist/generated/entities/staff_time_project/index.js.map +2 -2
- package/dist/generated/entities/staff_time_project_member/index.js +2 -0
- package/dist/generated/entities/staff_time_project_member/index.js.map +2 -2
- package/dist/generated/entities/staff_time_task/index.js +4 -0
- package/dist/generated/entities/staff_time_task/index.js.map +2 -2
- package/dist/generated/entities/staff_time_task_comment/index.js +9 -1
- package/dist/generated/entities/staff_time_task_comment/index.js.map +2 -2
- package/dist/generated/entities.ids.generated.js +2 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +38 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/auth.js +1 -0
- package/dist/helpers/integration/auth.js.map +2 -2
- package/dist/helpers/integration/communicationChannelsFixtures.js +20 -0
- package/dist/helpers/integration/communicationChannelsFixtures.js.map +2 -2
- package/dist/modules/catalog/api/bulk-delete/route.js +12 -2
- package/dist/modules/catalog/api/bulk-delete/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/test-seed/route.js +17 -3
- package/dist/modules/communication_channels/api/post/test-seed/route.js.map +2 -2
- package/dist/modules/communication_channels/lib/test-seed.js +38 -5
- package/dist/modules/communication_channels/lib/test-seed.js.map +2 -2
- package/dist/modules/messages/commands/messages.js +2 -3
- package/dist/modules/messages/commands/messages.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js.map +2 -2
- package/dist/modules/staff/api/portal/support-tickets/[id]/attachments/route.js +299 -0
- package/dist/modules/staff/api/portal/support-tickets/[id]/attachments/route.js.map +7 -0
- package/dist/modules/staff/api/portal/support-tickets/[id]/messages/route.js +91 -0
- package/dist/modules/staff/api/portal/support-tickets/[id]/messages/route.js.map +7 -0
- package/dist/modules/staff/api/portal/support-tickets/[id]/route.js +146 -0
- package/dist/modules/staff/api/portal/support-tickets/[id]/route.js.map +7 -0
- package/dist/modules/staff/api/portal/support-tickets/options/route.js +60 -0
- package/dist/modules/staff/api/portal/support-tickets/options/route.js.map +7 -0
- package/dist/modules/staff/api/portal/support-tickets/route.js +220 -0
- package/dist/modules/staff/api/portal/support-tickets/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/settings/route.js +7 -1
- package/dist/modules/staff/api/timesheets/settings/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/support-routing-rules/fallback-project/route.js +122 -0
- package/dist/modules/staff/api/timesheets/support-routing-rules/fallback-project/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/support-routing-rules/preview/route.js +180 -0
- package/dist/modules/staff/api/timesheets/support-routing-rules/preview/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/support-routing-rules/route.js +147 -0
- package/dist/modules/staff/api/timesheets/support-routing-rules/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/support-tickets/options/route.js +133 -0
- package/dist/modules/staff/api/timesheets/support-tickets/options/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tasks/[id]/claim/route.js +33 -0
- package/dist/modules/staff/api/timesheets/tasks/[id]/claim/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tasks/[id]/comments/route.js +25 -7
- package/dist/modules/staff/api/timesheets/tasks/[id]/comments/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/tasks/[id]/release/route.js +32 -0
- package/dist/modules/staff/api/timesheets/tasks/[id]/release/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tasks/route.js +94 -47
- package/dist/modules/staff/api/timesheets/tasks/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js +3 -0
- package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-projects/route.js +3 -0
- package/dist/modules/staff/api/timesheets/time-projects/route.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.js +1 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js +54 -4
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.js +21 -5
- package/dist/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/settings/page.js +319 -254
- package/dist/modules/staff/backend/staff/time-tracking/settings/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/support/page.js +363 -0
- package/dist/modules/staff/backend/staff/time-tracking/support/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/support/page.meta.js +18 -0
- package/dist/modules/staff/backend/staff/time-tracking/support/page.meta.js.map +7 -0
- package/dist/modules/staff/cli.js +27 -0
- package/dist/modules/staff/cli.js.map +2 -2
- package/dist/modules/staff/commands/index.js +3 -0
- package/dist/modules/staff/commands/index.js.map +2 -2
- package/dist/modules/staff/commands/shared.js +10 -1
- package/dist/modules/staff/commands/shared.js.map +2 -2
- package/dist/modules/staff/commands/support-routing-rules.js +505 -0
- package/dist/modules/staff/commands/support-routing-rules.js.map +7 -0
- package/dist/modules/staff/commands/support-tickets.js +513 -0
- package/dist/modules/staff/commands/support-tickets.js.map +7 -0
- package/dist/modules/staff/commands/timesheets-projects.js +72 -14
- package/dist/modules/staff/commands/timesheets-projects.js.map +2 -2
- package/dist/modules/staff/commands/timesheets-task-assignment.js +270 -0
- package/dist/modules/staff/commands/timesheets-task-assignment.js.map +7 -0
- package/dist/modules/staff/commands/timesheets-task-comments.js +58 -12
- package/dist/modules/staff/commands/timesheets-task-comments.js.map +2 -2
- package/dist/modules/staff/commands/timesheets-tasks.js +91 -9
- package/dist/modules/staff/commands/timesheets-tasks.js.map +3 -3
- package/dist/modules/staff/data/enrichers.js +141 -0
- package/dist/modules/staff/data/enrichers.js.map +2 -2
- package/dist/modules/staff/data/entities.js +143 -2
- package/dist/modules/staff/data/entities.js.map +2 -2
- package/dist/modules/staff/data/extensions.js +8 -0
- package/dist/modules/staff/data/extensions.js.map +2 -2
- package/dist/modules/staff/data/validators.js +34 -5
- package/dist/modules/staff/data/validators.js.map +2 -2
- package/dist/modules/staff/encryption.js +6 -0
- package/dist/modules/staff/encryption.js.map +2 -2
- package/dist/modules/staff/events.js +25 -0
- package/dist/modules/staff/events.js.map +2 -2
- package/dist/modules/staff/events.payloads.js +54 -0
- package/dist/modules/staff/events.payloads.js.map +2 -2
- package/dist/modules/staff/extension-points.js +1 -0
- package/dist/modules/staff/extension-points.js.map +2 -2
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.js +468 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.meta.js +12 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.meta.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/new/page.js +316 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/new/page.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/new/page.meta.js +12 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/new/page.meta.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/page.js +190 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/page.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/page.meta.js +19 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/page.meta.js.map +7 -0
- package/dist/modules/staff/index.js +1 -1
- package/dist/modules/staff/index.js.map +2 -2
- package/dist/modules/staff/lib/crud.js +2 -0
- package/dist/modules/staff/lib/crud.js.map +2 -2
- package/dist/modules/staff/lib/support/attachments.js +36 -0
- package/dist/modules/staff/lib/support/attachments.js.map +7 -0
- package/dist/modules/staff/lib/support/dictionaries.js +87 -0
- package/dist/modules/staff/lib/support/dictionaries.js.map +7 -0
- package/dist/modules/staff/lib/support/notifications.js +111 -0
- package/dist/modules/staff/lib/support/notifications.js.map +7 -0
- package/dist/modules/staff/lib/support/portalAttachments.js +100 -0
- package/dist/modules/staff/lib/support/portalAttachments.js.map +7 -0
- package/dist/modules/staff/lib/support/portalBroadcast.js +117 -0
- package/dist/modules/staff/lib/support/portalBroadcast.js.map +7 -0
- package/dist/modules/staff/lib/support/portalRoute.js +99 -0
- package/dist/modules/staff/lib/support/portalRoute.js.map +7 -0
- package/dist/modules/staff/lib/support/portalStatus.js +12 -0
- package/dist/modules/staff/lib/support/portalStatus.js.map +7 -0
- package/dist/modules/staff/lib/support/portalTickets.js +148 -0
- package/dist/modules/staff/lib/support/portalTickets.js.map +7 -0
- package/dist/modules/staff/lib/support/routing.js +81 -0
- package/dist/modules/staff/lib/support/routing.js.map +7 -0
- package/dist/modules/staff/lib/support/routingRuleDraft.js +25 -0
- package/dist/modules/staff/lib/support/routingRuleDraft.js.map +7 -0
- package/dist/modules/staff/lib/support/seedDefaults.js +114 -0
- package/dist/modules/staff/lib/support/seedDefaults.js.map +7 -0
- package/dist/modules/staff/lib/support/supportActivity.js +26 -0
- package/dist/modules/staff/lib/support/supportActivity.js.map +7 -0
- package/dist/modules/staff/lib/support-ui/PortalSupportNotLinked.js +23 -0
- package/dist/modules/staff/lib/support-ui/PortalSupportNotLinked.js.map +7 -0
- package/dist/modules/staff/lib/support-ui/portalAttachmentValidation.js +112 -0
- package/dist/modules/staff/lib/support-ui/portalAttachmentValidation.js.map +7 -0
- package/dist/modules/staff/lib/support-ui/portalPendingUploads.js +45 -0
- package/dist/modules/staff/lib/support-ui/portalPendingUploads.js.map +7 -0
- package/dist/modules/staff/lib/support-ui/portalSupport.js +51 -0
- package/dist/modules/staff/lib/support-ui/portalSupport.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/access.js +3 -39
- package/dist/modules/staff/lib/time-tracking/access.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking/listProjectAccess.js +54 -0
- package/dist/modules/staff/lib/time-tracking/listProjectAccess.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/portalRecipients.js +92 -2
- package/dist/modules/staff/lib/time-tracking/portalRecipients.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking/projectAssignment.js +112 -0
- package/dist/modules/staff/lib/time-tracking/projectAssignment.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/settingKeys.js +14 -0
- package/dist/modules/staff/lib/time-tracking/settingKeys.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking/settings.js +28 -1
- package/dist/modules/staff/lib/time-tracking/settings.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking/taskAssignmentRoute.js +143 -0
- package/dist/modules/staff/lib/time-tracking/taskAssignmentRoute.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/workRecipients.js +92 -0
- package/dist/modules/staff/lib/time-tracking/workRecipients.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/BoardFilterChips.js +32 -3
- package/dist/modules/staff/lib/time-tracking-ui/BoardFilterChips.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/KanbanBoard.js +29 -0
- package/dist/modules/staff/lib/time-tracking-ui/KanbanBoard.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/KanbanCard.js +4 -0
- package/dist/modules/staff/lib/time-tracking-ui/KanbanCard.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/ProjectFormSections.js +141 -1
- package/dist/modules/staff/lib/time-tracking-ui/ProjectFormSections.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/SupportAttachmentsSection.js +153 -0
- package/dist/modules/staff/lib/time-tracking-ui/SupportAttachmentsSection.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/SupportRequestPanel.js +67 -0
- package/dist/modules/staff/lib/time-tracking-ui/SupportRequestPanel.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskBoardScreen.js +3 -1
- package/dist/modules/staff/lib/time-tracking-ui/TaskBoardScreen.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/TaskCommentThread.js +80 -17
- package/dist/modules/staff/lib/time-tracking-ui/TaskCommentThread.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/TaskDrawer.js +230 -41
- package/dist/modules/staff/lib/time-tracking-ui/TaskDrawer.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/boardDirectory.js +21 -1
- package/dist/modules/staff/lib/time-tracking-ui/boardDirectory.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/boardFilters.js +16 -4
- package/dist/modules/staff/lib/time-tracking-ui/boardFilters.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/kanbanBoardData.js +3 -1
- package/dist/modules/staff/lib/time-tracking-ui/kanbanBoardData.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/projectDefaultAssignee.js +68 -0
- package/dist/modules/staff/lib/time-tracking-ui/projectDefaultAssignee.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/settingsTabs.js +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/settingsTabs.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/supportTicketData.js +50 -0
- package/dist/modules/staff/lib/time-tracking-ui/supportTicketData.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/taskDrawerData.js +13 -1
- package/dist/modules/staff/lib/time-tracking-ui/taskDrawerData.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.js +36 -2
- package/dist/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.js.map +2 -2
- package/dist/modules/staff/migrations/Migration20261009180543_staff.js +42 -0
- package/dist/modules/staff/migrations/Migration20261009180543_staff.js.map +7 -0
- package/dist/modules/staff/notifications.js +92 -0
- package/dist/modules/staff/notifications.js.map +2 -2
- package/dist/modules/staff/setup.js +6 -2
- package/dist/modules/staff/setup.js.map +2 -2
- package/dist/modules/staff/subscribers/support-portal-comment-created.js +26 -0
- package/dist/modules/staff/subscribers/support-portal-comment-created.js.map +7 -0
- package/dist/modules/staff/subscribers/support-portal-comment-deleted.js +26 -0
- package/dist/modules/staff/subscribers/support-portal-comment-deleted.js.map +7 -0
- package/dist/modules/staff/subscribers/support-portal-comment-updated.js +26 -0
- package/dist/modules/staff/subscribers/support-portal-comment-updated.js.map +7 -0
- package/dist/modules/staff/subscribers/support-portal-status-column-updated.js +24 -0
- package/dist/modules/staff/subscribers/support-portal-status-column-updated.js.map +7 -0
- package/dist/modules/staff/subscribers/support-portal-task-status-changed.js +25 -0
- package/dist/modules/staff/subscribers/support-portal-task-status-changed.js.map +7 -0
- package/dist/modules/staff/subscribers/support-portal-task-updated.js +25 -0
- package/dist/modules/staff/subscribers/support-portal-task-updated.js.map +7 -0
- package/dist/modules/staff/subscribers/support-ticket-created-notification.js +25 -0
- package/dist/modules/staff/subscribers/support-ticket-created-notification.js.map +7 -0
- package/dist/modules/staff/subscribers/support-ticket-customer-replied-notification.js +25 -0
- package/dist/modules/staff/subscribers/support-ticket-customer-replied-notification.js.map +7 -0
- package/dist/modules/staff/subscribers/support-ticket-unnotified-clear.js +26 -0
- package/dist/modules/staff/subscribers/support-ticket-unnotified-clear.js.map +7 -0
- package/dist/modules/staff/subscribers/time-task-assignee-notification.js +83 -0
- package/dist/modules/staff/subscribers/time-task-assignee-notification.js.map +7 -0
- package/dist/modules/staff/widgets/injection/portal-support-tickets/widget.client.js +112 -0
- package/dist/modules/staff/widgets/injection/portal-support-tickets/widget.client.js.map +7 -0
- package/dist/modules/staff/widgets/injection/portal-support-tickets/widget.js +17 -0
- package/dist/modules/staff/widgets/injection/portal-support-tickets/widget.js.map +7 -0
- package/dist/modules/staff/widgets/injection/time-tracking-settings-support/widget.client.js +870 -0
- package/dist/modules/staff/widgets/injection/time-tracking-settings-support/widget.client.js.map +7 -0
- package/dist/modules/staff/widgets/injection/time-tracking-settings-support/widget.js +17 -0
- package/dist/modules/staff/widgets/injection/time-tracking-settings-support/widget.js.map +7 -0
- package/dist/modules/staff/widgets/injection-table.js +16 -1
- package/dist/modules/staff/widgets/injection-table.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsLocationDetailPage.js +4 -18
- package/dist/modules/wms/components/backend/WmsLocationDetailPage.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsLotDetailPage.js +4 -18
- package/dist/modules/wms/components/backend/WmsLotDetailPage.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsSkuDetailPage.js +4 -18
- package/dist/modules/wms/components/backend/WmsSkuDetailPage.js.map +2 -2
- package/dist/modules/wms/lib/inventoryCsvExport.js +19 -0
- package/dist/modules/wms/lib/inventoryCsvExport.js.map +7 -0
- package/generated/entities/staff_support_routing_rule/index.ts +9 -0
- package/generated/entities/staff_support_ticket/index.ts +17 -0
- package/generated/entities/staff_time_project/index.ts +1 -0
- package/generated/entities/staff_time_project_member/index.ts +1 -0
- package/generated/entities/staff_time_task/index.ts +2 -0
- package/generated/entities/staff_time_task_comment/index.ts +4 -0
- package/generated/entities.ids.generated.ts +2 -0
- package/generated/entity-fields-registry.ts +38 -0
- package/package.json +8 -8
- package/src/helpers/integration/auth.ts +1 -1
- package/src/helpers/integration/communicationChannelsFixtures.ts +60 -2
- package/src/modules/catalog/api/bulk-delete/route.ts +12 -2
- package/src/modules/communication_channels/api/post/test-seed/route.ts +29 -12
- package/src/modules/communication_channels/lib/test-seed.ts +49 -10
- package/src/modules/messages/commands/messages.ts +8 -10
- package/src/modules/portal/frontend/[orgSlug]/portal/dashboard/page.tsx +2 -2
- package/src/modules/staff/AGENTS.md +22 -6
- package/src/modules/staff/api/portal/support-tickets/[id]/attachments/route.ts +327 -0
- package/src/modules/staff/api/portal/support-tickets/[id]/messages/route.ts +94 -0
- package/src/modules/staff/api/portal/support-tickets/[id]/route.ts +160 -0
- package/src/modules/staff/api/portal/support-tickets/options/route.ts +60 -0
- package/src/modules/staff/api/portal/support-tickets/route.ts +253 -0
- package/src/modules/staff/api/timesheets/settings/route.ts +7 -1
- package/src/modules/staff/api/timesheets/support-routing-rules/fallback-project/route.ts +131 -0
- package/src/modules/staff/api/timesheets/support-routing-rules/preview/route.ts +248 -0
- package/src/modules/staff/api/timesheets/support-routing-rules/route.ts +150 -0
- package/src/modules/staff/api/timesheets/support-tickets/options/route.ts +181 -0
- package/src/modules/staff/api/timesheets/tasks/[id]/claim/route.ts +31 -0
- package/src/modules/staff/api/timesheets/tasks/[id]/comments/route.ts +25 -7
- package/src/modules/staff/api/timesheets/tasks/[id]/release/route.ts +30 -0
- package/src/modules/staff/api/timesheets/tasks/route.ts +119 -67
- package/src/modules/staff/api/timesheets/time-projects/[id]/employees/route.ts +3 -0
- package/src/modules/staff/api/timesheets/time-projects/route.ts +3 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.tsx +1 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx +67 -2
- package/src/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.ts +22 -3
- package/src/modules/staff/backend/staff/time-tracking/settings/page.tsx +405 -308
- package/src/modules/staff/backend/staff/time-tracking/support/page.meta.ts +14 -0
- package/src/modules/staff/backend/staff/time-tracking/support/page.tsx +428 -0
- package/src/modules/staff/cli.ts +28 -0
- package/src/modules/staff/commands/index.ts +3 -0
- package/src/modules/staff/commands/shared.ts +22 -1
- package/src/modules/staff/commands/support-routing-rules.ts +595 -0
- package/src/modules/staff/commands/support-tickets.ts +580 -0
- package/src/modules/staff/commands/timesheets-projects.ts +84 -13
- package/src/modules/staff/commands/timesheets-task-assignment.ts +319 -0
- package/src/modules/staff/commands/timesheets-task-comments.ts +70 -15
- package/src/modules/staff/commands/timesheets-tasks.ts +106 -15
- package/src/modules/staff/data/enrichers.ts +203 -0
- package/src/modules/staff/data/entities.ts +133 -2
- package/src/modules/staff/data/extensions.ts +9 -1
- package/src/modules/staff/data/validators.ts +36 -0
- package/src/modules/staff/encryption.ts +6 -0
- package/src/modules/staff/events.payloads.ts +87 -1
- package/src/modules/staff/events.ts +26 -0
- package/src/modules/staff/extension-points.ts +1 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.meta.ts +10 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.tsx +553 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/support/new/page.meta.ts +10 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/support/new/page.tsx +346 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/support/page.meta.ts +17 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/support/page.tsx +253 -0
- package/src/modules/staff/i18n/de.json +291 -0
- package/src/modules/staff/i18n/en.json +291 -0
- package/src/modules/staff/i18n/es.json +291 -0
- package/src/modules/staff/i18n/ko.json +291 -0
- package/src/modules/staff/i18n/pl.json +291 -0
- package/src/modules/staff/index.ts +1 -1
- package/src/modules/staff/lib/crud.ts +2 -1
- package/src/modules/staff/lib/support/attachments.ts +43 -0
- package/src/modules/staff/lib/support/dictionaries.ts +120 -0
- package/src/modules/staff/lib/support/notifications.ts +138 -0
- package/src/modules/staff/lib/support/portalAttachments.ts +140 -0
- package/src/modules/staff/lib/support/portalBroadcast.ts +161 -0
- package/src/modules/staff/lib/support/portalRoute.ts +108 -0
- package/src/modules/staff/lib/support/portalStatus.ts +15 -0
- package/src/modules/staff/lib/support/portalTickets.ts +233 -0
- package/src/modules/staff/lib/support/routing.ts +117 -0
- package/src/modules/staff/lib/support/routingRuleDraft.ts +45 -0
- package/src/modules/staff/lib/support/seedDefaults.ts +151 -0
- package/src/modules/staff/lib/support/supportActivity.ts +39 -0
- package/src/modules/staff/lib/support-ui/PortalSupportNotLinked.tsx +20 -0
- package/src/modules/staff/lib/support-ui/portalAttachmentValidation.ts +125 -0
- package/src/modules/staff/lib/support-ui/portalPendingUploads.ts +49 -0
- package/src/modules/staff/lib/support-ui/portalSupport.ts +116 -0
- package/src/modules/staff/lib/time-tracking/access.ts +2 -62
- package/src/modules/staff/lib/time-tracking/listProjectAccess.ts +78 -0
- package/src/modules/staff/lib/time-tracking/portalRecipients.ts +173 -5
- package/src/modules/staff/lib/time-tracking/projectAssignment.ts +172 -0
- package/src/modules/staff/lib/time-tracking/settingKeys.ts +11 -0
- package/src/modules/staff/lib/time-tracking/settings.ts +46 -1
- package/src/modules/staff/lib/time-tracking/taskAssignmentRoute.ts +165 -0
- package/src/modules/staff/lib/time-tracking/workRecipients.ts +183 -0
- package/src/modules/staff/lib/time-tracking-ui/BoardFilterChips.tsx +36 -1
- package/src/modules/staff/lib/time-tracking-ui/KanbanBoard.tsx +30 -0
- package/src/modules/staff/lib/time-tracking-ui/KanbanCard.tsx +9 -0
- package/src/modules/staff/lib/time-tracking-ui/ProjectFormSections.tsx +185 -6
- package/src/modules/staff/lib/time-tracking-ui/SupportAttachmentsSection.tsx +188 -0
- package/src/modules/staff/lib/time-tracking-ui/SupportRequestPanel.tsx +102 -0
- package/src/modules/staff/lib/time-tracking-ui/TaskBoardScreen.tsx +2 -0
- package/src/modules/staff/lib/time-tracking-ui/TaskCommentThread.tsx +105 -22
- package/src/modules/staff/lib/time-tracking-ui/TaskDrawer.tsx +278 -43
- package/src/modules/staff/lib/time-tracking-ui/boardDirectory.ts +21 -0
- package/src/modules/staff/lib/time-tracking-ui/boardFilters.ts +16 -2
- package/src/modules/staff/lib/time-tracking-ui/kanbanBoardData.ts +4 -0
- package/src/modules/staff/lib/time-tracking-ui/projectDefaultAssignee.ts +117 -0
- package/src/modules/staff/lib/time-tracking-ui/settingsTabs.ts +17 -0
- package/src/modules/staff/lib/time-tracking-ui/supportTicketData.ts +71 -0
- package/src/modules/staff/lib/time-tracking-ui/taskDrawerData.ts +15 -0
- package/src/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.ts +48 -1
- package/src/modules/staff/migrations/.snapshot-open-mercato.json +714 -0
- package/src/modules/staff/migrations/Migration20261009180543_staff.ts +50 -0
- package/src/modules/staff/notifications.ts +92 -0
- package/src/modules/staff/setup.ts +6 -2
- package/src/modules/staff/subscribers/support-portal-comment-created.ts +35 -0
- package/src/modules/staff/subscribers/support-portal-comment-deleted.ts +35 -0
- package/src/modules/staff/subscribers/support-portal-comment-updated.ts +35 -0
- package/src/modules/staff/subscribers/support-portal-status-column-updated.ts +36 -0
- package/src/modules/staff/subscribers/support-portal-task-status-changed.ts +34 -0
- package/src/modules/staff/subscribers/support-portal-task-updated.ts +34 -0
- package/src/modules/staff/subscribers/support-ticket-created-notification.ts +27 -0
- package/src/modules/staff/subscribers/support-ticket-customer-replied-notification.ts +27 -0
- package/src/modules/staff/subscribers/support-ticket-unnotified-clear.ts +29 -0
- package/src/modules/staff/subscribers/time-task-assignee-notification.ts +102 -0
- package/src/modules/staff/widgets/injection/portal-support-tickets/widget.client.tsx +151 -0
- package/src/modules/staff/widgets/injection/portal-support-tickets/widget.ts +16 -0
- package/src/modules/staff/widgets/injection/time-tracking-settings-support/widget.client.tsx +1058 -0
- package/src/modules/staff/widgets/injection/time-tracking-settings-support/widget.ts +17 -0
- package/src/modules/staff/widgets/injection-table.ts +15 -0
- package/src/modules/wms/components/backend/WmsLocationDetailPage.tsx +4 -20
- package/src/modules/wms/components/backend/WmsLotDetailPage.tsx +4 -20
- package/src/modules/wms/components/backend/WmsSkuDetailPage.tsx +4 -19
- package/src/modules/wms/lib/inventoryCsvExport.ts +18 -0
|
@@ -24,9 +24,6 @@ import {
|
|
|
24
24
|
type StaffTimeProjectMemberStatus,
|
|
25
25
|
} from '../data/entities'
|
|
26
26
|
|
|
27
|
-
const timeProjectCrudIndexer: CrudIndexerConfig<StaffTimeProject> = {
|
|
28
|
-
entityType: 'staff:staff_time_project',
|
|
29
|
-
}
|
|
30
27
|
const timeProjectMemberCrudIndexer: CrudIndexerConfig<StaffTimeProjectMember> = {
|
|
31
28
|
entityType: 'staff:staff_time_project_member',
|
|
32
29
|
}
|
|
@@ -41,7 +38,7 @@ import {
|
|
|
41
38
|
type StaffTimeProjectMemberAssignInput,
|
|
42
39
|
type StaffTimeProjectMemberUpdateInput,
|
|
43
40
|
} from '../data/validators'
|
|
44
|
-
import { staffTimeProjectCrudEvents, staffTimeProjectMemberCrudEvents } from '../lib/crud'
|
|
41
|
+
import { staffTimeProjectCrudEvents, staffTimeProjectCrudIndexer, staffTimeProjectMemberCrudEvents } from '../lib/crud'
|
|
45
42
|
import { DEFAULT_BUDGET_WARN_AT_PERCENT } from '../lib/timesheets-projects/budgetBurn'
|
|
46
43
|
import { seedProjectTaskStatuses } from '../lib/timesheets-tasks/seedProjectStatuses'
|
|
47
44
|
import {
|
|
@@ -142,6 +139,7 @@ type TimeProjectSnapshot = {
|
|
|
142
139
|
budgetKind: string
|
|
143
140
|
budgetValue: string | null
|
|
144
141
|
budgetWarnAtPercent: number
|
|
142
|
+
defaultAssigneeStaffMemberId: string | null
|
|
145
143
|
deletedAt: string | null
|
|
146
144
|
}
|
|
147
145
|
|
|
@@ -159,6 +157,7 @@ type TimeProjectMemberSnapshot = {
|
|
|
159
157
|
role: string | null
|
|
160
158
|
status: string
|
|
161
159
|
showInGrid: boolean
|
|
160
|
+
isAssigner: boolean
|
|
162
161
|
assignedStartDate: string
|
|
163
162
|
assignedEndDate: string | null
|
|
164
163
|
deletedAt: string | null
|
|
@@ -199,6 +198,7 @@ async function loadTimeProjectSnapshot(em: EntityManager, id: string, scope?: St
|
|
|
199
198
|
budgetKind: project.budgetKind ?? 'none',
|
|
200
199
|
budgetValue: project.budgetValue ?? null,
|
|
201
200
|
budgetWarnAtPercent: project.budgetWarnAtPercent ?? DEFAULT_BUDGET_WARN_AT_PERCENT,
|
|
201
|
+
defaultAssigneeStaffMemberId: project.defaultAssigneeStaffMemberId ?? null,
|
|
202
202
|
deletedAt: project.deletedAt ? project.deletedAt.toISOString() : null,
|
|
203
203
|
}
|
|
204
204
|
}
|
|
@@ -221,6 +221,7 @@ async function loadTimeProjectMemberSnapshot(em: EntityManager, id: string, scop
|
|
|
221
221
|
role: member.role ?? null,
|
|
222
222
|
status: member.status,
|
|
223
223
|
showInGrid: member.showInGrid ?? false,
|
|
224
|
+
isAssigner: member.isAssigner === true,
|
|
224
225
|
assignedStartDate: member.assignedStartDate instanceof Date ? member.assignedStartDate.toISOString().split('T')[0] : String(member.assignedStartDate),
|
|
225
226
|
assignedEndDate: member.assignedEndDate instanceof Date ? member.assignedEndDate.toISOString().split('T')[0] : (member.assignedEndDate ?? null),
|
|
226
227
|
deletedAt: member.deletedAt ? member.deletedAt.toISOString() : null,
|
|
@@ -249,6 +250,7 @@ function timeProjectSeedFromSnapshot(snapshot: TimeProjectSnapshot): Record<stri
|
|
|
249
250
|
budgetKind: (snapshot.budgetKind ?? 'none') as StaffTimeProjectBudgetKind,
|
|
250
251
|
budgetValue: snapshot.budgetValue ?? null,
|
|
251
252
|
budgetWarnAtPercent: snapshot.budgetWarnAtPercent ?? DEFAULT_BUDGET_WARN_AT_PERCENT,
|
|
253
|
+
defaultAssigneeStaffMemberId: snapshot.defaultAssigneeStaffMemberId ?? null,
|
|
252
254
|
createdAt: new Date(),
|
|
253
255
|
updatedAt: new Date(),
|
|
254
256
|
deletedAt: null,
|
|
@@ -265,6 +267,7 @@ function timeProjectMemberSeedFromSnapshot(snapshot: TimeProjectMemberSnapshot):
|
|
|
265
267
|
role: snapshot.role ?? null,
|
|
266
268
|
status: (snapshot.status ?? 'active') as StaffTimeProjectMemberStatus,
|
|
267
269
|
showInGrid: snapshot.showInGrid ?? false,
|
|
270
|
+
isAssigner: snapshot.isAssigner === true,
|
|
268
271
|
assignedStartDate: new Date(snapshot.assignedStartDate),
|
|
269
272
|
assignedEndDate: snapshot.assignedEndDate ? new Date(snapshot.assignedEndDate) : null,
|
|
270
273
|
createdAt: new Date(),
|
|
@@ -368,7 +371,7 @@ const createTimeProjectCommand: CommandHandler<StaffTimeProjectCreateInput, { ti
|
|
|
368
371
|
tenantId: project.tenantId,
|
|
369
372
|
},
|
|
370
373
|
events: staffTimeProjectCrudEvents,
|
|
371
|
-
indexer:
|
|
374
|
+
indexer: staffTimeProjectCrudIndexer,
|
|
372
375
|
})
|
|
373
376
|
|
|
374
377
|
return { timeProjectId: project.id }
|
|
@@ -418,7 +421,7 @@ const createTimeProjectCommand: CommandHandler<StaffTimeProjectCreateInput, { ti
|
|
|
418
421
|
tenantId: project.tenantId,
|
|
419
422
|
},
|
|
420
423
|
events: staffTimeProjectCrudEvents,
|
|
421
|
-
indexer:
|
|
424
|
+
indexer: staffTimeProjectCrudIndexer,
|
|
422
425
|
})
|
|
423
426
|
}
|
|
424
427
|
},
|
|
@@ -428,10 +431,60 @@ const createTimeProjectCommand: CommandHandler<StaffTimeProjectCreateInput, { ti
|
|
|
428
431
|
seedFromSnapshot: timeProjectSeedFromSnapshot,
|
|
429
432
|
buildResult: (entity) => ({ timeProjectId: entity.id }),
|
|
430
433
|
events: staffTimeProjectCrudEvents,
|
|
431
|
-
indexer:
|
|
434
|
+
indexer: staffTimeProjectCrudIndexer,
|
|
432
435
|
}),
|
|
433
436
|
}
|
|
434
437
|
|
|
438
|
+
async function requireAssignerUserAccount(
|
|
439
|
+
em: EntityManager,
|
|
440
|
+
member: { staffMemberId: string; tenantId: string; organizationId: string },
|
|
441
|
+
): Promise<void> {
|
|
442
|
+
const teamMember = await findOneWithDecryption(
|
|
443
|
+
em,
|
|
444
|
+
StaffTeamMember,
|
|
445
|
+
{ id: member.staffMemberId, tenantId: member.tenantId, organizationId: member.organizationId, deletedAt: null },
|
|
446
|
+
undefined,
|
|
447
|
+
{ tenantId: member.tenantId, organizationId: member.organizationId },
|
|
448
|
+
)
|
|
449
|
+
if (teamMember?.userId) return
|
|
450
|
+
const { translate } = await resolveTranslations()
|
|
451
|
+
const message = translate(
|
|
452
|
+
'staff.timesheets.errors.assignerNeedsUser',
|
|
453
|
+
'Only team members with a user account can be assigners, because they must log in and receive notifications.',
|
|
454
|
+
)
|
|
455
|
+
throw new CrudHttpError(422, { code: 'assigner_needs_user', error: message, fieldErrors: { isAssigner: message } })
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
async function requireDefaultAssigneeMember(
|
|
459
|
+
em: EntityManager,
|
|
460
|
+
project: { id: string; tenantId: string; organizationId: string },
|
|
461
|
+
staffMemberId: string,
|
|
462
|
+
): Promise<void> {
|
|
463
|
+
const membership = await em.findOne(
|
|
464
|
+
StaffTimeProjectMember,
|
|
465
|
+
{
|
|
466
|
+
timeProjectId: project.id,
|
|
467
|
+
staffMemberId,
|
|
468
|
+
tenantId: project.tenantId,
|
|
469
|
+
organizationId: project.organizationId,
|
|
470
|
+
status: 'active',
|
|
471
|
+
deletedAt: null,
|
|
472
|
+
},
|
|
473
|
+
{ fields: ['id'] },
|
|
474
|
+
)
|
|
475
|
+
if (membership) return
|
|
476
|
+
const { translate } = await resolveTranslations()
|
|
477
|
+
const message = translate(
|
|
478
|
+
'staff.timesheets.errors.defaultAssigneeNotMember',
|
|
479
|
+
'The default assignee must be an active member of this project.',
|
|
480
|
+
)
|
|
481
|
+
throw new CrudHttpError(422, {
|
|
482
|
+
code: 'default_assignee_not_member',
|
|
483
|
+
error: message,
|
|
484
|
+
fieldErrors: { defaultAssigneeStaffMemberId: message },
|
|
485
|
+
})
|
|
486
|
+
}
|
|
487
|
+
|
|
435
488
|
const updateTimeProjectCommand: CommandHandler<StaffTimeProjectUpdateInput, { timeProjectId: string }> = {
|
|
436
489
|
id: 'staff.timesheets.time_projects.update',
|
|
437
490
|
async prepare(rawInput, ctx) {
|
|
@@ -480,6 +533,10 @@ const updateTimeProjectCommand: CommandHandler<StaffTimeProjectUpdateInput, { ti
|
|
|
480
533
|
?? (customerUnchanged && nextCustomerId ? project.customerSnapshot ?? null : null)
|
|
481
534
|
}
|
|
482
535
|
|
|
536
|
+
if (parsed.defaultAssigneeStaffMemberId) {
|
|
537
|
+
await requireDefaultAssigneeMember(em, project, parsed.defaultAssigneeStaffMemberId)
|
|
538
|
+
}
|
|
539
|
+
|
|
483
540
|
if (parsed.name !== undefined) project.name = parsed.name
|
|
484
541
|
if (parsed.customerId !== undefined) project.customerId = parsed.customerId ?? null
|
|
485
542
|
if (nextCustomerSnapshot !== undefined) project.customerSnapshot = nextCustomerSnapshot
|
|
@@ -496,6 +553,7 @@ const updateTimeProjectCommand: CommandHandler<StaffTimeProjectUpdateInput, { ti
|
|
|
496
553
|
if (parsed.budgetKind !== undefined) project.budgetKind = parsed.budgetKind
|
|
497
554
|
if (parsed.budgetValue !== undefined) project.budgetValue = parsed.budgetValue ?? null
|
|
498
555
|
if (parsed.budgetWarnAtPercent !== undefined) project.budgetWarnAtPercent = parsed.budgetWarnAtPercent
|
|
556
|
+
if (parsed.defaultAssigneeStaffMemberId !== undefined) project.defaultAssigneeStaffMemberId = parsed.defaultAssigneeStaffMemberId ?? null
|
|
499
557
|
// `currency_code` is intentionally absent here and from the update schema:
|
|
500
558
|
// D-3 gives that column to `staff.timesheets.time_projects.change_currency`,
|
|
501
559
|
// which owns the acknowledgement and the locked-entry refusal. A caller that
|
|
@@ -536,7 +594,7 @@ const updateTimeProjectCommand: CommandHandler<StaffTimeProjectUpdateInput, { ti
|
|
|
536
594
|
tenantId: project.tenantId,
|
|
537
595
|
},
|
|
538
596
|
events: staffTimeProjectCrudEvents,
|
|
539
|
-
indexer:
|
|
597
|
+
indexer: staffTimeProjectCrudIndexer,
|
|
540
598
|
})
|
|
541
599
|
|
|
542
600
|
return { timeProjectId: project.id }
|
|
@@ -563,6 +621,7 @@ const updateTimeProjectCommand: CommandHandler<StaffTimeProjectUpdateInput, { ti
|
|
|
563
621
|
'budgetKind',
|
|
564
622
|
'budgetValue',
|
|
565
623
|
'budgetWarnAtPercent',
|
|
624
|
+
'defaultAssigneeStaffMemberId',
|
|
566
625
|
'deletedAt',
|
|
567
626
|
])
|
|
568
627
|
// `buildChanges` compares by identity, and the two snapshots are loaded through
|
|
@@ -612,6 +671,7 @@ const updateTimeProjectCommand: CommandHandler<StaffTimeProjectUpdateInput, { ti
|
|
|
612
671
|
project.budgetKind = (before.budgetKind ?? 'none') as StaffTimeProjectBudgetKind
|
|
613
672
|
project.budgetValue = before.budgetValue ?? null
|
|
614
673
|
project.budgetWarnAtPercent = before.budgetWarnAtPercent ?? DEFAULT_BUDGET_WARN_AT_PERCENT
|
|
674
|
+
project.defaultAssigneeStaffMemberId = before.defaultAssigneeStaffMemberId ?? null
|
|
615
675
|
// Undo of a plain update restores what a plain update could change; the currency
|
|
616
676
|
// stays where change_currency left it (D-3).
|
|
617
677
|
project.deletedAt = before.deletedAt ? new Date(before.deletedAt) : null
|
|
@@ -628,7 +688,7 @@ const updateTimeProjectCommand: CommandHandler<StaffTimeProjectUpdateInput, { ti
|
|
|
628
688
|
tenantId: project.tenantId,
|
|
629
689
|
},
|
|
630
690
|
events: staffTimeProjectCrudEvents,
|
|
631
|
-
indexer:
|
|
691
|
+
indexer: staffTimeProjectCrudIndexer,
|
|
632
692
|
})
|
|
633
693
|
},
|
|
634
694
|
}
|
|
@@ -673,7 +733,7 @@ const deleteTimeProjectCommand: CommandHandler<{ id?: string }, { timeProjectId:
|
|
|
673
733
|
tenantId: project.tenantId,
|
|
674
734
|
},
|
|
675
735
|
events: staffTimeProjectCrudEvents,
|
|
676
|
-
indexer:
|
|
736
|
+
indexer: staffTimeProjectCrudIndexer,
|
|
677
737
|
})
|
|
678
738
|
return { timeProjectId: project.id }
|
|
679
739
|
},
|
|
@@ -759,7 +819,7 @@ const deleteTimeProjectCommand: CommandHandler<{ id?: string }, { timeProjectId:
|
|
|
759
819
|
tenantId: project.tenantId,
|
|
760
820
|
},
|
|
761
821
|
events: staffTimeProjectCrudEvents,
|
|
762
|
-
indexer:
|
|
822
|
+
indexer: staffTimeProjectCrudIndexer,
|
|
763
823
|
})
|
|
764
824
|
},
|
|
765
825
|
}
|
|
@@ -932,7 +992,7 @@ const changeTimeProjectCurrencyCommand: CommandHandler<
|
|
|
932
992
|
tenantId: project.tenantId,
|
|
933
993
|
},
|
|
934
994
|
events: timeProjectCurrencyChangeEvents(previousCurrencyCode, parsed.currencyCode),
|
|
935
|
-
indexer:
|
|
995
|
+
indexer: staffTimeProjectCrudIndexer,
|
|
936
996
|
})
|
|
937
997
|
|
|
938
998
|
return {
|
|
@@ -1002,7 +1062,7 @@ const changeTimeProjectCurrencyCommand: CommandHandler<
|
|
|
1002
1062
|
tenantId: project.tenantId,
|
|
1003
1063
|
},
|
|
1004
1064
|
events: staffTimeProjectCrudEvents,
|
|
1005
|
-
indexer:
|
|
1065
|
+
indexer: staffTimeProjectCrudIndexer,
|
|
1006
1066
|
})
|
|
1007
1067
|
},
|
|
1008
1068
|
}
|
|
@@ -1048,6 +1108,10 @@ const assignTimeProjectMemberCommand: CommandHandler<StaffTimeProjectMemberAssig
|
|
|
1048
1108
|
})
|
|
1049
1109
|
}
|
|
1050
1110
|
|
|
1111
|
+
if (parsed.isAssigner === true) {
|
|
1112
|
+
await requireAssignerUserAccount(em, { staffMemberId: parsed.staffMemberId, tenantId: parsed.tenantId, organizationId: parsed.organizationId })
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1051
1115
|
const now = new Date()
|
|
1052
1116
|
const member = em.create(StaffTimeProjectMember, {
|
|
1053
1117
|
tenantId: parsed.tenantId,
|
|
@@ -1057,6 +1121,7 @@ const assignTimeProjectMemberCommand: CommandHandler<StaffTimeProjectMemberAssig
|
|
|
1057
1121
|
role: parsed.role ?? null,
|
|
1058
1122
|
status: parsed.status ?? 'active',
|
|
1059
1123
|
showInGrid: false,
|
|
1124
|
+
isAssigner: parsed.isAssigner === true,
|
|
1060
1125
|
assignedStartDate: parsed.assignedStartDate,
|
|
1061
1126
|
assignedEndDate: parsed.assignedEndDate ?? null,
|
|
1062
1127
|
createdAt: now,
|
|
@@ -1187,9 +1252,13 @@ const updateTimeProjectMemberCommand: CommandHandler<StaffTimeProjectMemberUpdat
|
|
|
1187
1252
|
ensureTenantScope(ctx, member.tenantId)
|
|
1188
1253
|
ensureOrganizationScope(ctx, member.organizationId)
|
|
1189
1254
|
|
|
1255
|
+
if (parsed.isAssigner === true && member.isAssigner !== true) {
|
|
1256
|
+
await requireAssignerUserAccount(em, member)
|
|
1257
|
+
}
|
|
1190
1258
|
if (parsed.role !== undefined) member.role = parsed.role ?? null
|
|
1191
1259
|
if (parsed.status !== undefined) member.status = parsed.status
|
|
1192
1260
|
if (parsed.assignedEndDate !== undefined) member.assignedEndDate = parsed.assignedEndDate ?? null
|
|
1261
|
+
if (parsed.isAssigner !== undefined) member.isAssigner = parsed.isAssigner
|
|
1193
1262
|
member.updatedAt = new Date()
|
|
1194
1263
|
await em.flush()
|
|
1195
1264
|
|
|
@@ -1214,6 +1283,7 @@ const updateTimeProjectMemberCommand: CommandHandler<StaffTimeProjectMemberUpdat
|
|
|
1214
1283
|
'role',
|
|
1215
1284
|
'status',
|
|
1216
1285
|
'assignedEndDate',
|
|
1286
|
+
'isAssigner',
|
|
1217
1287
|
])
|
|
1218
1288
|
const { translate } = await resolveTranslations()
|
|
1219
1289
|
return {
|
|
@@ -1245,6 +1315,7 @@ const updateTimeProjectMemberCommand: CommandHandler<StaffTimeProjectMemberUpdat
|
|
|
1245
1315
|
member.role = before.role ?? null
|
|
1246
1316
|
member.status = (before.status ?? 'active') as StaffTimeProjectMemberStatus
|
|
1247
1317
|
member.assignedEndDate = before.assignedEndDate ? new Date(before.assignedEndDate) : null
|
|
1318
|
+
member.isAssigner = before.isAssigner === true
|
|
1248
1319
|
member.updatedAt = new Date()
|
|
1249
1320
|
await em.flush()
|
|
1250
1321
|
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
3
|
+
import type { CommandHandler, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'
|
|
4
|
+
import { registerCommand } from '@open-mercato/shared/lib/commands'
|
|
5
|
+
import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
6
|
+
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
7
|
+
import { emitCrudSideEffects, emitCrudUndoSideEffects } from '@open-mercato/shared/lib/commands/helpers'
|
|
8
|
+
import type { DataEngine } from '@open-mercato/shared/lib/data/engine'
|
|
9
|
+
import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
10
|
+
import { StaffTeamMember, StaffTimeTask, StaffTimeTaskStatus } from '../data/entities'
|
|
11
|
+
import {
|
|
12
|
+
isActiveProjectAssigner,
|
|
13
|
+
resolveAssignmentGraceDaysFromContainer,
|
|
14
|
+
} from '../lib/time-tracking/projectAssignment'
|
|
15
|
+
import {
|
|
16
|
+
TASK_LIST_CACHE_RESOURCE,
|
|
17
|
+
emitTaskAssigneeChanged,
|
|
18
|
+
taskCrudEvents,
|
|
19
|
+
taskCrudIndexer,
|
|
20
|
+
} from './timesheets-tasks'
|
|
21
|
+
import {
|
|
22
|
+
applyScopeToWhere,
|
|
23
|
+
commandActorScope,
|
|
24
|
+
ensureOrganizationScope,
|
|
25
|
+
ensureTenantScope,
|
|
26
|
+
extractUndoPayload,
|
|
27
|
+
} from './shared'
|
|
28
|
+
|
|
29
|
+
export const staffTimeTaskAssignmentCommandIds = {
|
|
30
|
+
claim: 'staff.timesheets.tasks.claim',
|
|
31
|
+
release: 'staff.timesheets.tasks.release',
|
|
32
|
+
} as const
|
|
33
|
+
|
|
34
|
+
export const TASK_ALREADY_CLAIMED_CODE = 'task_already_claimed'
|
|
35
|
+
export const TASK_NOT_CLAIMABLE_CODE = 'task_not_claimable'
|
|
36
|
+
export const TASK_NOT_ASSIGNER_CODE = 'task_not_assigner'
|
|
37
|
+
export const TASK_NOT_ASSIGNEE_CODE = 'task_not_assignee'
|
|
38
|
+
export const TASK_ASSIGNMENT_CHANGED_CODE = 'task_assignment_changed'
|
|
39
|
+
|
|
40
|
+
const assignmentInputSchema = z.object({ id: z.string().uuid() })
|
|
41
|
+
|
|
42
|
+
export type StaffTimeTaskAssignmentInput = z.infer<typeof assignmentInputSchema>
|
|
43
|
+
|
|
44
|
+
export type StaffTimeTaskAssignmentResult = {
|
|
45
|
+
taskId: string
|
|
46
|
+
tenantId: string
|
|
47
|
+
organizationId: string
|
|
48
|
+
timeProjectId: string
|
|
49
|
+
assigneeStaffMemberId: string | null
|
|
50
|
+
updatedAt: string | null
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type AssignmentUndoPayload = {
|
|
54
|
+
taskId: string
|
|
55
|
+
tenantId: string
|
|
56
|
+
organizationId: string
|
|
57
|
+
previousAssigneeStaffMemberId: string | null
|
|
58
|
+
nextAssigneeStaffMemberId: string | null
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
type Translate = (key: string, fallback: string) => string
|
|
62
|
+
|
|
63
|
+
function notFound(translate: Translate): CrudHttpError {
|
|
64
|
+
return new CrudHttpError(404, {
|
|
65
|
+
error: translate('staff.time_tracking.tasks.errors.notFound', 'Task not found or not accessible.'),
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function notClaimable(translate: Translate): CrudHttpError {
|
|
70
|
+
return new CrudHttpError(422, {
|
|
71
|
+
code: TASK_NOT_CLAIMABLE_CODE,
|
|
72
|
+
error: translate('staff.timesheets.errors.taskNotClaimable', 'A finished task cannot be claimed.'),
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function alreadyClaimed(translate: Translate): CrudHttpError {
|
|
77
|
+
return new CrudHttpError(409, {
|
|
78
|
+
code: TASK_ALREADY_CLAIMED_CODE,
|
|
79
|
+
error: translate('staff.timesheets.errors.taskAlreadyClaimed', 'Someone else already took this task.'),
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function loadDoneStatusIds(em: EntityManager, task: StaffTimeTask): Promise<string[]> {
|
|
84
|
+
const statuses = await em.find(
|
|
85
|
+
StaffTimeTaskStatus,
|
|
86
|
+
{ timeProjectId: task.timeProjectId, tenantId: task.tenantId, organizationId: task.organizationId, isDone: true },
|
|
87
|
+
{ fields: ['id'] },
|
|
88
|
+
)
|
|
89
|
+
return statuses.map((status) => status.id)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function resolveActorStaffMemberId(em: EntityManager, ctx: CommandRuntimeContext, task: StaffTimeTask): Promise<string | null> {
|
|
93
|
+
const userId = ctx.auth?.sub ?? null
|
|
94
|
+
if (!userId) return null
|
|
95
|
+
const member = await findOneWithDecryption(
|
|
96
|
+
em,
|
|
97
|
+
StaffTeamMember,
|
|
98
|
+
{ userId, tenantId: task.tenantId, organizationId: task.organizationId, deletedAt: null },
|
|
99
|
+
undefined,
|
|
100
|
+
{ tenantId: task.tenantId, organizationId: task.organizationId },
|
|
101
|
+
)
|
|
102
|
+
return member?.id ?? null
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function loadScopedTask(em: EntityManager, ctx: CommandRuntimeContext, id: string, translate: Translate): Promise<StaffTimeTask> {
|
|
106
|
+
const task = await em.findOne(
|
|
107
|
+
StaffTimeTask,
|
|
108
|
+
applyScopeToWhere<StaffTimeTask>({ id, deletedAt: null }, commandActorScope(ctx)),
|
|
109
|
+
)
|
|
110
|
+
if (!task) throw notFound(translate)
|
|
111
|
+
ensureTenantScope(ctx, task.tenantId)
|
|
112
|
+
ensureOrganizationScope(ctx, task.organizationId)
|
|
113
|
+
return task
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function emitTaskSideEffects(ctx: CommandRuntimeContext, task: StaffTimeTask, undo = false): Promise<void> {
|
|
117
|
+
const params = {
|
|
118
|
+
dataEngine: ctx.container.resolve('dataEngine') as DataEngine,
|
|
119
|
+
action: 'updated' as const,
|
|
120
|
+
entity: task,
|
|
121
|
+
identifiers: { id: task.id, organizationId: task.organizationId, tenantId: task.tenantId },
|
|
122
|
+
events: taskCrudEvents,
|
|
123
|
+
indexer: taskCrudIndexer,
|
|
124
|
+
}
|
|
125
|
+
if (undo) await emitCrudUndoSideEffects(params)
|
|
126
|
+
else await emitCrudSideEffects(params)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* The conditional UPDATE is the only moment this writer owns the row, so the outcome is
|
|
131
|
+
* built from what it wrote rather than re-read: a later writer may already have replaced
|
|
132
|
+
* the assignee, and reporting or undoing their value would be wrong.
|
|
133
|
+
*/
|
|
134
|
+
function captureWrittenState(
|
|
135
|
+
em: EntityManager,
|
|
136
|
+
task: StaffTimeTask,
|
|
137
|
+
assigneeStaffMemberId: string | null,
|
|
138
|
+
updatedAt: Date,
|
|
139
|
+
): StaffTimeTask {
|
|
140
|
+
em.clear()
|
|
141
|
+
task.assigneeStaffMemberId = assigneeStaffMemberId
|
|
142
|
+
task.updatedAt = updatedAt
|
|
143
|
+
return task
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function toResult(task: StaffTimeTask): StaffTimeTaskAssignmentResult {
|
|
147
|
+
return {
|
|
148
|
+
taskId: task.id,
|
|
149
|
+
tenantId: task.tenantId,
|
|
150
|
+
organizationId: task.organizationId,
|
|
151
|
+
timeProjectId: task.timeProjectId,
|
|
152
|
+
assigneeStaffMemberId: task.assigneeStaffMemberId ?? null,
|
|
153
|
+
updatedAt: task.updatedAt ? task.updatedAt.toISOString() : null,
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function restoreAssignee(
|
|
158
|
+
ctx: CommandRuntimeContext,
|
|
159
|
+
payload: AssignmentUndoPayload,
|
|
160
|
+
): Promise<void> {
|
|
161
|
+
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
162
|
+
const where = {
|
|
163
|
+
id: payload.taskId,
|
|
164
|
+
tenantId: payload.tenantId,
|
|
165
|
+
organizationId: payload.organizationId,
|
|
166
|
+
assigneeStaffMemberId: payload.nextAssigneeStaffMemberId,
|
|
167
|
+
deletedAt: null,
|
|
168
|
+
}
|
|
169
|
+
const current = await em.findOne(StaffTimeTask, where)
|
|
170
|
+
if (!current) return
|
|
171
|
+
const now = new Date()
|
|
172
|
+
const affected = await em.nativeUpdate(
|
|
173
|
+
StaffTimeTask,
|
|
174
|
+
where,
|
|
175
|
+
{ assigneeStaffMemberId: payload.previousAssigneeStaffMemberId, updatedAt: now },
|
|
176
|
+
)
|
|
177
|
+
if (affected === 0) return
|
|
178
|
+
const task = captureWrittenState(em, current, payload.previousAssigneeStaffMemberId, now)
|
|
179
|
+
await emitTaskSideEffects(ctx, task, true)
|
|
180
|
+
emitTaskAssigneeChanged(task, payload.nextAssigneeStaffMemberId, 'manual', ctx.auth?.sub ?? null)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const claimTaskCommand: CommandHandler<StaffTimeTaskAssignmentInput, StaffTimeTaskAssignmentResult> = {
|
|
184
|
+
id: staffTimeTaskAssignmentCommandIds.claim,
|
|
185
|
+
async execute(rawInput, ctx) {
|
|
186
|
+
const parsed = assignmentInputSchema.parse(rawInput)
|
|
187
|
+
const { translate } = await resolveTranslations()
|
|
188
|
+
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
189
|
+
const task = await loadScopedTask(em, ctx, parsed.id, translate)
|
|
190
|
+
const scope = { tenantId: task.tenantId, organizationId: task.organizationId }
|
|
191
|
+
|
|
192
|
+
const staffMemberId = await resolveActorStaffMemberId(em, ctx, task)
|
|
193
|
+
const graceDays = await resolveAssignmentGraceDaysFromContainer(ctx.container, task.tenantId)
|
|
194
|
+
const isAssigner = staffMemberId
|
|
195
|
+
? await isActiveProjectAssigner(em, scope, task.timeProjectId, staffMemberId, { graceDays })
|
|
196
|
+
: false
|
|
197
|
+
if (!staffMemberId || !isAssigner) {
|
|
198
|
+
throw new CrudHttpError(403, {
|
|
199
|
+
code: TASK_NOT_ASSIGNER_CODE,
|
|
200
|
+
error: translate('staff.timesheets.errors.notAssigner', 'Only active assigners of this project can claim tasks.'),
|
|
201
|
+
})
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const doneStatusIds = await loadDoneStatusIds(em, task)
|
|
205
|
+
if (doneStatusIds.includes(task.taskStatusId)) throw notClaimable(translate)
|
|
206
|
+
if (task.assigneeStaffMemberId) throw alreadyClaimed(translate)
|
|
207
|
+
|
|
208
|
+
const now = new Date()
|
|
209
|
+
const affected = await em.nativeUpdate(
|
|
210
|
+
StaffTimeTask,
|
|
211
|
+
{
|
|
212
|
+
id: task.id,
|
|
213
|
+
tenantId: task.tenantId,
|
|
214
|
+
organizationId: task.organizationId,
|
|
215
|
+
assigneeStaffMemberId: null,
|
|
216
|
+
deletedAt: null,
|
|
217
|
+
...(doneStatusIds.length > 0 ? { taskStatusId: { $nin: doneStatusIds } } : {}),
|
|
218
|
+
},
|
|
219
|
+
{ assigneeStaffMemberId: staffMemberId, updatedAt: now },
|
|
220
|
+
)
|
|
221
|
+
if (affected === 0) {
|
|
222
|
+
em.clear()
|
|
223
|
+
const current = await em.findOne(StaffTimeTask, { id: task.id, tenantId: task.tenantId, organizationId: task.organizationId, deletedAt: null })
|
|
224
|
+
if (!current) throw notFound(translate)
|
|
225
|
+
if (!current.assigneeStaffMemberId && doneStatusIds.includes(current.taskStatusId)) throw notClaimable(translate)
|
|
226
|
+
throw alreadyClaimed(translate)
|
|
227
|
+
}
|
|
228
|
+
const claimed = captureWrittenState(em, task, staffMemberId, now)
|
|
229
|
+
await emitTaskSideEffects(ctx, claimed)
|
|
230
|
+
emitTaskAssigneeChanged(claimed, null, 'claim', ctx.auth?.sub ?? null)
|
|
231
|
+
return toResult(claimed)
|
|
232
|
+
},
|
|
233
|
+
buildLog: async ({ result }) => {
|
|
234
|
+
const { translate } = await resolveTranslations()
|
|
235
|
+
return {
|
|
236
|
+
actionLabel: translate('staff.audit.timesheets.tasks.claim', 'Claim task'),
|
|
237
|
+
resourceKind: 'staff.timesheets.task',
|
|
238
|
+
resourceId: result.taskId,
|
|
239
|
+
tenantId: result.tenantId,
|
|
240
|
+
organizationId: result.organizationId,
|
|
241
|
+
context: { cacheAliases: [TASK_LIST_CACHE_RESOURCE] },
|
|
242
|
+
payload: {
|
|
243
|
+
undo: {
|
|
244
|
+
taskId: result.taskId,
|
|
245
|
+
tenantId: result.tenantId,
|
|
246
|
+
organizationId: result.organizationId,
|
|
247
|
+
previousAssigneeStaffMemberId: null,
|
|
248
|
+
nextAssigneeStaffMemberId: result.assigneeStaffMemberId,
|
|
249
|
+
} satisfies AssignmentUndoPayload,
|
|
250
|
+
},
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
undo: async ({ logEntry, ctx }) => {
|
|
254
|
+
const payload = extractUndoPayload<AssignmentUndoPayload>(logEntry)
|
|
255
|
+
if (!payload?.taskId) return
|
|
256
|
+
await restoreAssignee(ctx, payload)
|
|
257
|
+
},
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const releaseTaskCommand: CommandHandler<StaffTimeTaskAssignmentInput, StaffTimeTaskAssignmentResult & { previousAssigneeStaffMemberId: string | null }> = {
|
|
261
|
+
id: staffTimeTaskAssignmentCommandIds.release,
|
|
262
|
+
async execute(rawInput, ctx) {
|
|
263
|
+
const parsed = assignmentInputSchema.parse(rawInput)
|
|
264
|
+
const { translate } = await resolveTranslations()
|
|
265
|
+
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
266
|
+
const task = await loadScopedTask(em, ctx, parsed.id, translate)
|
|
267
|
+
const staffMemberId = await resolveActorStaffMemberId(em, ctx, task)
|
|
268
|
+
if (!staffMemberId || task.assigneeStaffMemberId !== staffMemberId) {
|
|
269
|
+
throw new CrudHttpError(403, {
|
|
270
|
+
code: TASK_NOT_ASSIGNEE_CODE,
|
|
271
|
+
error: translate('staff.timesheets.errors.notAssignee', 'Only the current assignee can release this task.'),
|
|
272
|
+
})
|
|
273
|
+
}
|
|
274
|
+
const now = new Date()
|
|
275
|
+
const affected = await em.nativeUpdate(
|
|
276
|
+
StaffTimeTask,
|
|
277
|
+
{ id: task.id, tenantId: task.tenantId, organizationId: task.organizationId, assigneeStaffMemberId: staffMemberId, deletedAt: null },
|
|
278
|
+
{ assigneeStaffMemberId: null, updatedAt: now },
|
|
279
|
+
)
|
|
280
|
+
if (affected === 0) {
|
|
281
|
+
throw new CrudHttpError(409, {
|
|
282
|
+
code: TASK_ASSIGNMENT_CHANGED_CODE,
|
|
283
|
+
error: translate('staff.timesheets.errors.taskChanged', 'The task changed while you were releasing it. Refresh and try again.'),
|
|
284
|
+
})
|
|
285
|
+
}
|
|
286
|
+
const released = captureWrittenState(em, task, null, now)
|
|
287
|
+
await emitTaskSideEffects(ctx, released)
|
|
288
|
+
emitTaskAssigneeChanged(released, staffMemberId, 'release', ctx.auth?.sub ?? null)
|
|
289
|
+
return { ...toResult(released), previousAssigneeStaffMemberId: staffMemberId }
|
|
290
|
+
},
|
|
291
|
+
buildLog: async ({ result }) => {
|
|
292
|
+
const { translate } = await resolveTranslations()
|
|
293
|
+
return {
|
|
294
|
+
actionLabel: translate('staff.audit.timesheets.tasks.release', 'Release task'),
|
|
295
|
+
resourceKind: 'staff.timesheets.task',
|
|
296
|
+
resourceId: result.taskId,
|
|
297
|
+
tenantId: result.tenantId,
|
|
298
|
+
organizationId: result.organizationId,
|
|
299
|
+
context: { cacheAliases: [TASK_LIST_CACHE_RESOURCE] },
|
|
300
|
+
payload: {
|
|
301
|
+
undo: {
|
|
302
|
+
taskId: result.taskId,
|
|
303
|
+
tenantId: result.tenantId,
|
|
304
|
+
organizationId: result.organizationId,
|
|
305
|
+
previousAssigneeStaffMemberId: result.previousAssigneeStaffMemberId,
|
|
306
|
+
nextAssigneeStaffMemberId: null,
|
|
307
|
+
} satisfies AssignmentUndoPayload,
|
|
308
|
+
},
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
undo: async ({ logEntry, ctx }) => {
|
|
312
|
+
const payload = extractUndoPayload<AssignmentUndoPayload>(logEntry)
|
|
313
|
+
if (!payload?.taskId) return
|
|
314
|
+
await restoreAssignee(ctx, payload)
|
|
315
|
+
},
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
registerCommand(claimTaskCommand)
|
|
319
|
+
registerCommand(releaseTaskCommand)
|