@open-mercato/core 0.10.1-develop.7717.1.2befe9c634 → 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
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { registerCommand } from "@open-mercato/shared/lib/commands";
|
|
3
|
+
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
4
|
+
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
5
|
+
import { emitCrudSideEffects, emitCrudUndoSideEffects } from "@open-mercato/shared/lib/commands/helpers";
|
|
6
|
+
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
7
|
+
import { StaffTeamMember, StaffTimeTask, StaffTimeTaskStatus } from "../data/entities.js";
|
|
8
|
+
import {
|
|
9
|
+
isActiveProjectAssigner,
|
|
10
|
+
resolveAssignmentGraceDaysFromContainer
|
|
11
|
+
} from "../lib/time-tracking/projectAssignment.js";
|
|
12
|
+
import {
|
|
13
|
+
TASK_LIST_CACHE_RESOURCE,
|
|
14
|
+
emitTaskAssigneeChanged,
|
|
15
|
+
taskCrudEvents,
|
|
16
|
+
taskCrudIndexer
|
|
17
|
+
} from "./timesheets-tasks.js";
|
|
18
|
+
import {
|
|
19
|
+
applyScopeToWhere,
|
|
20
|
+
commandActorScope,
|
|
21
|
+
ensureOrganizationScope,
|
|
22
|
+
ensureTenantScope,
|
|
23
|
+
extractUndoPayload
|
|
24
|
+
} from "./shared.js";
|
|
25
|
+
const staffTimeTaskAssignmentCommandIds = {
|
|
26
|
+
claim: "staff.timesheets.tasks.claim",
|
|
27
|
+
release: "staff.timesheets.tasks.release"
|
|
28
|
+
};
|
|
29
|
+
const TASK_ALREADY_CLAIMED_CODE = "task_already_claimed";
|
|
30
|
+
const TASK_NOT_CLAIMABLE_CODE = "task_not_claimable";
|
|
31
|
+
const TASK_NOT_ASSIGNER_CODE = "task_not_assigner";
|
|
32
|
+
const TASK_NOT_ASSIGNEE_CODE = "task_not_assignee";
|
|
33
|
+
const TASK_ASSIGNMENT_CHANGED_CODE = "task_assignment_changed";
|
|
34
|
+
const assignmentInputSchema = z.object({ id: z.string().uuid() });
|
|
35
|
+
function notFound(translate) {
|
|
36
|
+
return new CrudHttpError(404, {
|
|
37
|
+
error: translate("staff.time_tracking.tasks.errors.notFound", "Task not found or not accessible.")
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function notClaimable(translate) {
|
|
41
|
+
return new CrudHttpError(422, {
|
|
42
|
+
code: TASK_NOT_CLAIMABLE_CODE,
|
|
43
|
+
error: translate("staff.timesheets.errors.taskNotClaimable", "A finished task cannot be claimed.")
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function alreadyClaimed(translate) {
|
|
47
|
+
return new CrudHttpError(409, {
|
|
48
|
+
code: TASK_ALREADY_CLAIMED_CODE,
|
|
49
|
+
error: translate("staff.timesheets.errors.taskAlreadyClaimed", "Someone else already took this task.")
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
async function loadDoneStatusIds(em, task) {
|
|
53
|
+
const statuses = await em.find(
|
|
54
|
+
StaffTimeTaskStatus,
|
|
55
|
+
{ timeProjectId: task.timeProjectId, tenantId: task.tenantId, organizationId: task.organizationId, isDone: true },
|
|
56
|
+
{ fields: ["id"] }
|
|
57
|
+
);
|
|
58
|
+
return statuses.map((status) => status.id);
|
|
59
|
+
}
|
|
60
|
+
async function resolveActorStaffMemberId(em, ctx, task) {
|
|
61
|
+
const userId = ctx.auth?.sub ?? null;
|
|
62
|
+
if (!userId) return null;
|
|
63
|
+
const member = await findOneWithDecryption(
|
|
64
|
+
em,
|
|
65
|
+
StaffTeamMember,
|
|
66
|
+
{ userId, tenantId: task.tenantId, organizationId: task.organizationId, deletedAt: null },
|
|
67
|
+
void 0,
|
|
68
|
+
{ tenantId: task.tenantId, organizationId: task.organizationId }
|
|
69
|
+
);
|
|
70
|
+
return member?.id ?? null;
|
|
71
|
+
}
|
|
72
|
+
async function loadScopedTask(em, ctx, id, translate) {
|
|
73
|
+
const task = await em.findOne(
|
|
74
|
+
StaffTimeTask,
|
|
75
|
+
applyScopeToWhere({ id, deletedAt: null }, commandActorScope(ctx))
|
|
76
|
+
);
|
|
77
|
+
if (!task) throw notFound(translate);
|
|
78
|
+
ensureTenantScope(ctx, task.tenantId);
|
|
79
|
+
ensureOrganizationScope(ctx, task.organizationId);
|
|
80
|
+
return task;
|
|
81
|
+
}
|
|
82
|
+
async function emitTaskSideEffects(ctx, task, undo = false) {
|
|
83
|
+
const params = {
|
|
84
|
+
dataEngine: ctx.container.resolve("dataEngine"),
|
|
85
|
+
action: "updated",
|
|
86
|
+
entity: task,
|
|
87
|
+
identifiers: { id: task.id, organizationId: task.organizationId, tenantId: task.tenantId },
|
|
88
|
+
events: taskCrudEvents,
|
|
89
|
+
indexer: taskCrudIndexer
|
|
90
|
+
};
|
|
91
|
+
if (undo) await emitCrudUndoSideEffects(params);
|
|
92
|
+
else await emitCrudSideEffects(params);
|
|
93
|
+
}
|
|
94
|
+
function captureWrittenState(em, task, assigneeStaffMemberId, updatedAt) {
|
|
95
|
+
em.clear();
|
|
96
|
+
task.assigneeStaffMemberId = assigneeStaffMemberId;
|
|
97
|
+
task.updatedAt = updatedAt;
|
|
98
|
+
return task;
|
|
99
|
+
}
|
|
100
|
+
function toResult(task) {
|
|
101
|
+
return {
|
|
102
|
+
taskId: task.id,
|
|
103
|
+
tenantId: task.tenantId,
|
|
104
|
+
organizationId: task.organizationId,
|
|
105
|
+
timeProjectId: task.timeProjectId,
|
|
106
|
+
assigneeStaffMemberId: task.assigneeStaffMemberId ?? null,
|
|
107
|
+
updatedAt: task.updatedAt ? task.updatedAt.toISOString() : null
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
async function restoreAssignee(ctx, payload) {
|
|
111
|
+
const em = ctx.container.resolve("em").fork();
|
|
112
|
+
const where = {
|
|
113
|
+
id: payload.taskId,
|
|
114
|
+
tenantId: payload.tenantId,
|
|
115
|
+
organizationId: payload.organizationId,
|
|
116
|
+
assigneeStaffMemberId: payload.nextAssigneeStaffMemberId,
|
|
117
|
+
deletedAt: null
|
|
118
|
+
};
|
|
119
|
+
const current = await em.findOne(StaffTimeTask, where);
|
|
120
|
+
if (!current) return;
|
|
121
|
+
const now = /* @__PURE__ */ new Date();
|
|
122
|
+
const affected = await em.nativeUpdate(
|
|
123
|
+
StaffTimeTask,
|
|
124
|
+
where,
|
|
125
|
+
{ assigneeStaffMemberId: payload.previousAssigneeStaffMemberId, updatedAt: now }
|
|
126
|
+
);
|
|
127
|
+
if (affected === 0) return;
|
|
128
|
+
const task = captureWrittenState(em, current, payload.previousAssigneeStaffMemberId, now);
|
|
129
|
+
await emitTaskSideEffects(ctx, task, true);
|
|
130
|
+
emitTaskAssigneeChanged(task, payload.nextAssigneeStaffMemberId, "manual", ctx.auth?.sub ?? null);
|
|
131
|
+
}
|
|
132
|
+
const claimTaskCommand = {
|
|
133
|
+
id: staffTimeTaskAssignmentCommandIds.claim,
|
|
134
|
+
async execute(rawInput, ctx) {
|
|
135
|
+
const parsed = assignmentInputSchema.parse(rawInput);
|
|
136
|
+
const { translate } = await resolveTranslations();
|
|
137
|
+
const em = ctx.container.resolve("em").fork();
|
|
138
|
+
const task = await loadScopedTask(em, ctx, parsed.id, translate);
|
|
139
|
+
const scope = { tenantId: task.tenantId, organizationId: task.organizationId };
|
|
140
|
+
const staffMemberId = await resolveActorStaffMemberId(em, ctx, task);
|
|
141
|
+
const graceDays = await resolveAssignmentGraceDaysFromContainer(ctx.container, task.tenantId);
|
|
142
|
+
const isAssigner = staffMemberId ? await isActiveProjectAssigner(em, scope, task.timeProjectId, staffMemberId, { graceDays }) : false;
|
|
143
|
+
if (!staffMemberId || !isAssigner) {
|
|
144
|
+
throw new CrudHttpError(403, {
|
|
145
|
+
code: TASK_NOT_ASSIGNER_CODE,
|
|
146
|
+
error: translate("staff.timesheets.errors.notAssigner", "Only active assigners of this project can claim tasks.")
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
const doneStatusIds = await loadDoneStatusIds(em, task);
|
|
150
|
+
if (doneStatusIds.includes(task.taskStatusId)) throw notClaimable(translate);
|
|
151
|
+
if (task.assigneeStaffMemberId) throw alreadyClaimed(translate);
|
|
152
|
+
const now = /* @__PURE__ */ new Date();
|
|
153
|
+
const affected = await em.nativeUpdate(
|
|
154
|
+
StaffTimeTask,
|
|
155
|
+
{
|
|
156
|
+
id: task.id,
|
|
157
|
+
tenantId: task.tenantId,
|
|
158
|
+
organizationId: task.organizationId,
|
|
159
|
+
assigneeStaffMemberId: null,
|
|
160
|
+
deletedAt: null,
|
|
161
|
+
...doneStatusIds.length > 0 ? { taskStatusId: { $nin: doneStatusIds } } : {}
|
|
162
|
+
},
|
|
163
|
+
{ assigneeStaffMemberId: staffMemberId, updatedAt: now }
|
|
164
|
+
);
|
|
165
|
+
if (affected === 0) {
|
|
166
|
+
em.clear();
|
|
167
|
+
const current = await em.findOne(StaffTimeTask, { id: task.id, tenantId: task.tenantId, organizationId: task.organizationId, deletedAt: null });
|
|
168
|
+
if (!current) throw notFound(translate);
|
|
169
|
+
if (!current.assigneeStaffMemberId && doneStatusIds.includes(current.taskStatusId)) throw notClaimable(translate);
|
|
170
|
+
throw alreadyClaimed(translate);
|
|
171
|
+
}
|
|
172
|
+
const claimed = captureWrittenState(em, task, staffMemberId, now);
|
|
173
|
+
await emitTaskSideEffects(ctx, claimed);
|
|
174
|
+
emitTaskAssigneeChanged(claimed, null, "claim", ctx.auth?.sub ?? null);
|
|
175
|
+
return toResult(claimed);
|
|
176
|
+
},
|
|
177
|
+
buildLog: async ({ result }) => {
|
|
178
|
+
const { translate } = await resolveTranslations();
|
|
179
|
+
return {
|
|
180
|
+
actionLabel: translate("staff.audit.timesheets.tasks.claim", "Claim task"),
|
|
181
|
+
resourceKind: "staff.timesheets.task",
|
|
182
|
+
resourceId: result.taskId,
|
|
183
|
+
tenantId: result.tenantId,
|
|
184
|
+
organizationId: result.organizationId,
|
|
185
|
+
context: { cacheAliases: [TASK_LIST_CACHE_RESOURCE] },
|
|
186
|
+
payload: {
|
|
187
|
+
undo: {
|
|
188
|
+
taskId: result.taskId,
|
|
189
|
+
tenantId: result.tenantId,
|
|
190
|
+
organizationId: result.organizationId,
|
|
191
|
+
previousAssigneeStaffMemberId: null,
|
|
192
|
+
nextAssigneeStaffMemberId: result.assigneeStaffMemberId
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
},
|
|
197
|
+
undo: async ({ logEntry, ctx }) => {
|
|
198
|
+
const payload = extractUndoPayload(logEntry);
|
|
199
|
+
if (!payload?.taskId) return;
|
|
200
|
+
await restoreAssignee(ctx, payload);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
const releaseTaskCommand = {
|
|
204
|
+
id: staffTimeTaskAssignmentCommandIds.release,
|
|
205
|
+
async execute(rawInput, ctx) {
|
|
206
|
+
const parsed = assignmentInputSchema.parse(rawInput);
|
|
207
|
+
const { translate } = await resolveTranslations();
|
|
208
|
+
const em = ctx.container.resolve("em").fork();
|
|
209
|
+
const task = await loadScopedTask(em, ctx, parsed.id, translate);
|
|
210
|
+
const staffMemberId = await resolveActorStaffMemberId(em, ctx, task);
|
|
211
|
+
if (!staffMemberId || task.assigneeStaffMemberId !== staffMemberId) {
|
|
212
|
+
throw new CrudHttpError(403, {
|
|
213
|
+
code: TASK_NOT_ASSIGNEE_CODE,
|
|
214
|
+
error: translate("staff.timesheets.errors.notAssignee", "Only the current assignee can release this task.")
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
const now = /* @__PURE__ */ new Date();
|
|
218
|
+
const affected = await em.nativeUpdate(
|
|
219
|
+
StaffTimeTask,
|
|
220
|
+
{ id: task.id, tenantId: task.tenantId, organizationId: task.organizationId, assigneeStaffMemberId: staffMemberId, deletedAt: null },
|
|
221
|
+
{ assigneeStaffMemberId: null, updatedAt: now }
|
|
222
|
+
);
|
|
223
|
+
if (affected === 0) {
|
|
224
|
+
throw new CrudHttpError(409, {
|
|
225
|
+
code: TASK_ASSIGNMENT_CHANGED_CODE,
|
|
226
|
+
error: translate("staff.timesheets.errors.taskChanged", "The task changed while you were releasing it. Refresh and try again.")
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
const released = captureWrittenState(em, task, null, now);
|
|
230
|
+
await emitTaskSideEffects(ctx, released);
|
|
231
|
+
emitTaskAssigneeChanged(released, staffMemberId, "release", ctx.auth?.sub ?? null);
|
|
232
|
+
return { ...toResult(released), previousAssigneeStaffMemberId: staffMemberId };
|
|
233
|
+
},
|
|
234
|
+
buildLog: async ({ result }) => {
|
|
235
|
+
const { translate } = await resolveTranslations();
|
|
236
|
+
return {
|
|
237
|
+
actionLabel: translate("staff.audit.timesheets.tasks.release", "Release task"),
|
|
238
|
+
resourceKind: "staff.timesheets.task",
|
|
239
|
+
resourceId: result.taskId,
|
|
240
|
+
tenantId: result.tenantId,
|
|
241
|
+
organizationId: result.organizationId,
|
|
242
|
+
context: { cacheAliases: [TASK_LIST_CACHE_RESOURCE] },
|
|
243
|
+
payload: {
|
|
244
|
+
undo: {
|
|
245
|
+
taskId: result.taskId,
|
|
246
|
+
tenantId: result.tenantId,
|
|
247
|
+
organizationId: result.organizationId,
|
|
248
|
+
previousAssigneeStaffMemberId: result.previousAssigneeStaffMemberId,
|
|
249
|
+
nextAssigneeStaffMemberId: null
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
},
|
|
254
|
+
undo: async ({ logEntry, ctx }) => {
|
|
255
|
+
const payload = extractUndoPayload(logEntry);
|
|
256
|
+
if (!payload?.taskId) return;
|
|
257
|
+
await restoreAssignee(ctx, payload);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
registerCommand(claimTaskCommand);
|
|
261
|
+
registerCommand(releaseTaskCommand);
|
|
262
|
+
export {
|
|
263
|
+
TASK_ALREADY_CLAIMED_CODE,
|
|
264
|
+
TASK_ASSIGNMENT_CHANGED_CODE,
|
|
265
|
+
TASK_NOT_ASSIGNEE_CODE,
|
|
266
|
+
TASK_NOT_ASSIGNER_CODE,
|
|
267
|
+
TASK_NOT_CLAIMABLE_CODE,
|
|
268
|
+
staffTimeTaskAssignmentCommandIds
|
|
269
|
+
};
|
|
270
|
+
//# sourceMappingURL=timesheets-task-assignment.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/staff/commands/timesheets-task-assignment.ts"],
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CommandHandler, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { emitCrudSideEffects, emitCrudUndoSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { StaffTeamMember, StaffTimeTask, StaffTimeTaskStatus } from '../data/entities'\nimport {\n isActiveProjectAssigner,\n resolveAssignmentGraceDaysFromContainer,\n} from '../lib/time-tracking/projectAssignment'\nimport {\n TASK_LIST_CACHE_RESOURCE,\n emitTaskAssigneeChanged,\n taskCrudEvents,\n taskCrudIndexer,\n} from './timesheets-tasks'\nimport {\n applyScopeToWhere,\n commandActorScope,\n ensureOrganizationScope,\n ensureTenantScope,\n extractUndoPayload,\n} from './shared'\n\nexport const staffTimeTaskAssignmentCommandIds = {\n claim: 'staff.timesheets.tasks.claim',\n release: 'staff.timesheets.tasks.release',\n} as const\n\nexport const TASK_ALREADY_CLAIMED_CODE = 'task_already_claimed'\nexport const TASK_NOT_CLAIMABLE_CODE = 'task_not_claimable'\nexport const TASK_NOT_ASSIGNER_CODE = 'task_not_assigner'\nexport const TASK_NOT_ASSIGNEE_CODE = 'task_not_assignee'\nexport const TASK_ASSIGNMENT_CHANGED_CODE = 'task_assignment_changed'\n\nconst assignmentInputSchema = z.object({ id: z.string().uuid() })\n\nexport type StaffTimeTaskAssignmentInput = z.infer<typeof assignmentInputSchema>\n\nexport type StaffTimeTaskAssignmentResult = {\n taskId: string\n tenantId: string\n organizationId: string\n timeProjectId: string\n assigneeStaffMemberId: string | null\n updatedAt: string | null\n}\n\ntype AssignmentUndoPayload = {\n taskId: string\n tenantId: string\n organizationId: string\n previousAssigneeStaffMemberId: string | null\n nextAssigneeStaffMemberId: string | null\n}\n\ntype Translate = (key: string, fallback: string) => string\n\nfunction notFound(translate: Translate): CrudHttpError {\n return new CrudHttpError(404, {\n error: translate('staff.time_tracking.tasks.errors.notFound', 'Task not found or not accessible.'),\n })\n}\n\nfunction notClaimable(translate: Translate): CrudHttpError {\n return new CrudHttpError(422, {\n code: TASK_NOT_CLAIMABLE_CODE,\n error: translate('staff.timesheets.errors.taskNotClaimable', 'A finished task cannot be claimed.'),\n })\n}\n\nfunction alreadyClaimed(translate: Translate): CrudHttpError {\n return new CrudHttpError(409, {\n code: TASK_ALREADY_CLAIMED_CODE,\n error: translate('staff.timesheets.errors.taskAlreadyClaimed', 'Someone else already took this task.'),\n })\n}\n\nasync function loadDoneStatusIds(em: EntityManager, task: StaffTimeTask): Promise<string[]> {\n const statuses = await em.find(\n StaffTimeTaskStatus,\n { timeProjectId: task.timeProjectId, tenantId: task.tenantId, organizationId: task.organizationId, isDone: true },\n { fields: ['id'] },\n )\n return statuses.map((status) => status.id)\n}\n\nasync function resolveActorStaffMemberId(em: EntityManager, ctx: CommandRuntimeContext, task: StaffTimeTask): Promise<string | null> {\n const userId = ctx.auth?.sub ?? null\n if (!userId) return null\n const member = await findOneWithDecryption(\n em,\n StaffTeamMember,\n { userId, tenantId: task.tenantId, organizationId: task.organizationId, deletedAt: null },\n undefined,\n { tenantId: task.tenantId, organizationId: task.organizationId },\n )\n return member?.id ?? null\n}\n\nasync function loadScopedTask(em: EntityManager, ctx: CommandRuntimeContext, id: string, translate: Translate): Promise<StaffTimeTask> {\n const task = await em.findOne(\n StaffTimeTask,\n applyScopeToWhere<StaffTimeTask>({ id, deletedAt: null }, commandActorScope(ctx)),\n )\n if (!task) throw notFound(translate)\n ensureTenantScope(ctx, task.tenantId)\n ensureOrganizationScope(ctx, task.organizationId)\n return task\n}\n\nasync function emitTaskSideEffects(ctx: CommandRuntimeContext, task: StaffTimeTask, undo = false): Promise<void> {\n const params = {\n dataEngine: ctx.container.resolve('dataEngine') as DataEngine,\n action: 'updated' as const,\n entity: task,\n identifiers: { id: task.id, organizationId: task.organizationId, tenantId: task.tenantId },\n events: taskCrudEvents,\n indexer: taskCrudIndexer,\n }\n if (undo) await emitCrudUndoSideEffects(params)\n else await emitCrudSideEffects(params)\n}\n\n/**\n * The conditional UPDATE is the only moment this writer owns the row, so the outcome is\n * built from what it wrote rather than re-read: a later writer may already have replaced\n * the assignee, and reporting or undoing their value would be wrong.\n */\nfunction captureWrittenState(\n em: EntityManager,\n task: StaffTimeTask,\n assigneeStaffMemberId: string | null,\n updatedAt: Date,\n): StaffTimeTask {\n em.clear()\n task.assigneeStaffMemberId = assigneeStaffMemberId\n task.updatedAt = updatedAt\n return task\n}\n\nfunction toResult(task: StaffTimeTask): StaffTimeTaskAssignmentResult {\n return {\n taskId: task.id,\n tenantId: task.tenantId,\n organizationId: task.organizationId,\n timeProjectId: task.timeProjectId,\n assigneeStaffMemberId: task.assigneeStaffMemberId ?? null,\n updatedAt: task.updatedAt ? task.updatedAt.toISOString() : null,\n }\n}\n\nasync function restoreAssignee(\n ctx: CommandRuntimeContext,\n payload: AssignmentUndoPayload,\n): Promise<void> {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const where = {\n id: payload.taskId,\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n assigneeStaffMemberId: payload.nextAssigneeStaffMemberId,\n deletedAt: null,\n }\n const current = await em.findOne(StaffTimeTask, where)\n if (!current) return\n const now = new Date()\n const affected = await em.nativeUpdate(\n StaffTimeTask,\n where,\n { assigneeStaffMemberId: payload.previousAssigneeStaffMemberId, updatedAt: now },\n )\n if (affected === 0) return\n const task = captureWrittenState(em, current, payload.previousAssigneeStaffMemberId, now)\n await emitTaskSideEffects(ctx, task, true)\n emitTaskAssigneeChanged(task, payload.nextAssigneeStaffMemberId, 'manual', ctx.auth?.sub ?? null)\n}\n\nconst claimTaskCommand: CommandHandler<StaffTimeTaskAssignmentInput, StaffTimeTaskAssignmentResult> = {\n id: staffTimeTaskAssignmentCommandIds.claim,\n async execute(rawInput, ctx) {\n const parsed = assignmentInputSchema.parse(rawInput)\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const task = await loadScopedTask(em, ctx, parsed.id, translate)\n const scope = { tenantId: task.tenantId, organizationId: task.organizationId }\n\n const staffMemberId = await resolveActorStaffMemberId(em, ctx, task)\n const graceDays = await resolveAssignmentGraceDaysFromContainer(ctx.container, task.tenantId)\n const isAssigner = staffMemberId\n ? await isActiveProjectAssigner(em, scope, task.timeProjectId, staffMemberId, { graceDays })\n : false\n if (!staffMemberId || !isAssigner) {\n throw new CrudHttpError(403, {\n code: TASK_NOT_ASSIGNER_CODE,\n error: translate('staff.timesheets.errors.notAssigner', 'Only active assigners of this project can claim tasks.'),\n })\n }\n\n const doneStatusIds = await loadDoneStatusIds(em, task)\n if (doneStatusIds.includes(task.taskStatusId)) throw notClaimable(translate)\n if (task.assigneeStaffMemberId) throw alreadyClaimed(translate)\n\n const now = new Date()\n const affected = await em.nativeUpdate(\n StaffTimeTask,\n {\n id: task.id,\n tenantId: task.tenantId,\n organizationId: task.organizationId,\n assigneeStaffMemberId: null,\n deletedAt: null,\n ...(doneStatusIds.length > 0 ? { taskStatusId: { $nin: doneStatusIds } } : {}),\n },\n { assigneeStaffMemberId: staffMemberId, updatedAt: now },\n )\n if (affected === 0) {\n em.clear()\n const current = await em.findOne(StaffTimeTask, { id: task.id, tenantId: task.tenantId, organizationId: task.organizationId, deletedAt: null })\n if (!current) throw notFound(translate)\n if (!current.assigneeStaffMemberId && doneStatusIds.includes(current.taskStatusId)) throw notClaimable(translate)\n throw alreadyClaimed(translate)\n }\n const claimed = captureWrittenState(em, task, staffMemberId, now)\n await emitTaskSideEffects(ctx, claimed)\n emitTaskAssigneeChanged(claimed, null, 'claim', ctx.auth?.sub ?? null)\n return toResult(claimed)\n },\n buildLog: async ({ result }) => {\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.tasks.claim', 'Claim task'),\n resourceKind: 'staff.timesheets.task',\n resourceId: result.taskId,\n tenantId: result.tenantId,\n organizationId: result.organizationId,\n context: { cacheAliases: [TASK_LIST_CACHE_RESOURCE] },\n payload: {\n undo: {\n taskId: result.taskId,\n tenantId: result.tenantId,\n organizationId: result.organizationId,\n previousAssigneeStaffMemberId: null,\n nextAssigneeStaffMemberId: result.assigneeStaffMemberId,\n } satisfies AssignmentUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AssignmentUndoPayload>(logEntry)\n if (!payload?.taskId) return\n await restoreAssignee(ctx, payload)\n },\n}\n\nconst releaseTaskCommand: CommandHandler<StaffTimeTaskAssignmentInput, StaffTimeTaskAssignmentResult & { previousAssigneeStaffMemberId: string | null }> = {\n id: staffTimeTaskAssignmentCommandIds.release,\n async execute(rawInput, ctx) {\n const parsed = assignmentInputSchema.parse(rawInput)\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const task = await loadScopedTask(em, ctx, parsed.id, translate)\n const staffMemberId = await resolveActorStaffMemberId(em, ctx, task)\n if (!staffMemberId || task.assigneeStaffMemberId !== staffMemberId) {\n throw new CrudHttpError(403, {\n code: TASK_NOT_ASSIGNEE_CODE,\n error: translate('staff.timesheets.errors.notAssignee', 'Only the current assignee can release this task.'),\n })\n }\n const now = new Date()\n const affected = await em.nativeUpdate(\n StaffTimeTask,\n { id: task.id, tenantId: task.tenantId, organizationId: task.organizationId, assigneeStaffMemberId: staffMemberId, deletedAt: null },\n { assigneeStaffMemberId: null, updatedAt: now },\n )\n if (affected === 0) {\n throw new CrudHttpError(409, {\n code: TASK_ASSIGNMENT_CHANGED_CODE,\n error: translate('staff.timesheets.errors.taskChanged', 'The task changed while you were releasing it. Refresh and try again.'),\n })\n }\n const released = captureWrittenState(em, task, null, now)\n await emitTaskSideEffects(ctx, released)\n emitTaskAssigneeChanged(released, staffMemberId, 'release', ctx.auth?.sub ?? null)\n return { ...toResult(released), previousAssigneeStaffMemberId: staffMemberId }\n },\n buildLog: async ({ result }) => {\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.tasks.release', 'Release task'),\n resourceKind: 'staff.timesheets.task',\n resourceId: result.taskId,\n tenantId: result.tenantId,\n organizationId: result.organizationId,\n context: { cacheAliases: [TASK_LIST_CACHE_RESOURCE] },\n payload: {\n undo: {\n taskId: result.taskId,\n tenantId: result.tenantId,\n organizationId: result.organizationId,\n previousAssigneeStaffMemberId: result.previousAssigneeStaffMemberId,\n nextAssigneeStaffMemberId: null,\n } satisfies AssignmentUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AssignmentUndoPayload>(logEntry)\n if (!payload?.taskId) return\n await restoreAssignee(ctx, payload)\n },\n}\n\nregisterCommand(claimTaskCommand)\nregisterCommand(releaseTaskCommand)\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -9,7 +9,7 @@ import { StaffTimeTask, StaffTimeTaskComment } from "../data/entities.js";
|
|
|
9
9
|
import { staffTimeTaskCommentCrudEvents } from "../lib/crud.js";
|
|
10
10
|
import {
|
|
11
11
|
staffTimeTaskCommentCreateSchema,
|
|
12
|
-
|
|
12
|
+
staffTimeTaskCommentUpdateRequestSchema
|
|
13
13
|
} from "../data/validators.js";
|
|
14
14
|
import {
|
|
15
15
|
applyScopeToWhere,
|
|
@@ -35,6 +35,34 @@ const commentCrudIndexer = {
|
|
|
35
35
|
entityType: "staff:staff_time_task_comment"
|
|
36
36
|
};
|
|
37
37
|
const commentCrudEvents = staffTimeTaskCommentCrudEvents;
|
|
38
|
+
function commentEvents(portalVisible) {
|
|
39
|
+
const base = staffTimeTaskCommentCrudEvents;
|
|
40
|
+
return {
|
|
41
|
+
...base,
|
|
42
|
+
buildPayload: (context) => ({
|
|
43
|
+
...base.buildPayload ? base.buildPayload(context) : {},
|
|
44
|
+
portalVisible
|
|
45
|
+
})
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const TASK_COMMENT_CUSTOMER_IMMUTABLE_CODE = "task_comment_customer_immutable";
|
|
49
|
+
const TASK_COMMENT_VISIBILITY_INVALID_CODE = "task_comment_visibility_invalid";
|
|
50
|
+
function customerCommentImmutableError(translate) {
|
|
51
|
+
return new CrudHttpError(403, {
|
|
52
|
+
code: TASK_COMMENT_CUSTOMER_IMMUTABLE_CODE,
|
|
53
|
+
error: translate(
|
|
54
|
+
"staff.time_tracking.taskComments.errors.customerImmutable",
|
|
55
|
+
"Messages written by the customer cannot be edited or deleted."
|
|
56
|
+
)
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function visibilityInvalidError(translate) {
|
|
60
|
+
const message = translate(
|
|
61
|
+
"staff.time_tracking.taskComments.errors.visibilityPortalOnly",
|
|
62
|
+
"Only customer support tickets can carry replies visible to the customer."
|
|
63
|
+
);
|
|
64
|
+
return new CrudHttpError(422, { code: TASK_COMMENT_VISIBILITY_INVALID_CODE, error: message, fieldErrors: { visibility: message } });
|
|
65
|
+
}
|
|
38
66
|
function toCommentSnapshot(comment) {
|
|
39
67
|
return {
|
|
40
68
|
id: comment.id,
|
|
@@ -43,6 +71,8 @@ function toCommentSnapshot(comment) {
|
|
|
43
71
|
taskId: comment.taskId,
|
|
44
72
|
body: comment.body,
|
|
45
73
|
authorUserId: comment.authorUserId ?? null,
|
|
74
|
+
visibility: comment.visibility ?? "internal",
|
|
75
|
+
authorKind: comment.authorKind ?? "staff",
|
|
46
76
|
createdAt: comment.createdAt ? comment.createdAt.toISOString() : null,
|
|
47
77
|
deletedAt: comment.deletedAt ? comment.deletedAt.toISOString() : null
|
|
48
78
|
};
|
|
@@ -98,6 +128,7 @@ async function callerCanManageAll(ctx) {
|
|
|
98
128
|
});
|
|
99
129
|
}
|
|
100
130
|
async function requireEditableComment(comment, ctx, translate) {
|
|
131
|
+
if (comment.authorKind === "customer") throw customerCommentImmutableError(translate);
|
|
101
132
|
const actorId = stampAuthorUserId(ctx);
|
|
102
133
|
if (actorId && comment.authorUserId === actorId) return;
|
|
103
134
|
if (await callerCanManageAll(ctx)) return;
|
|
@@ -130,7 +161,10 @@ const createTaskCommentCommand = {
|
|
|
130
161
|
await withAtomicFlush(
|
|
131
162
|
em,
|
|
132
163
|
[
|
|
133
|
-
() =>
|
|
164
|
+
async () => {
|
|
165
|
+
const task = await requireTaskInScope(em, parsed.taskId, scope, translate);
|
|
166
|
+
if (parsed.visibility === "customer" && task.source !== "portal") throw visibilityInvalidError(translate);
|
|
167
|
+
},
|
|
134
168
|
() => {
|
|
135
169
|
created = em.create(StaffTimeTaskComment, {
|
|
136
170
|
tenantId: parsed.tenantId,
|
|
@@ -138,6 +172,8 @@ const createTaskCommentCommand = {
|
|
|
138
172
|
taskId: parsed.taskId,
|
|
139
173
|
body: parsed.body,
|
|
140
174
|
authorUserId,
|
|
175
|
+
visibility: parsed.visibility ?? "internal",
|
|
176
|
+
authorKind: "staff",
|
|
141
177
|
createdAt: /* @__PURE__ */ new Date(),
|
|
142
178
|
updatedAt: /* @__PURE__ */ new Date(),
|
|
143
179
|
deletedAt: null
|
|
@@ -154,7 +190,7 @@ const createTaskCommentCommand = {
|
|
|
154
190
|
action: "created",
|
|
155
191
|
entity: record,
|
|
156
192
|
identifiers: { id: record.id, organizationId: record.organizationId, tenantId: record.tenantId },
|
|
157
|
-
events:
|
|
193
|
+
events: commentEvents(record.visibility === "customer"),
|
|
158
194
|
indexer: commentCrudIndexer
|
|
159
195
|
});
|
|
160
196
|
return { commentId: record.id, taskId: record.taskId, authorUserId: record.authorUserId ?? null };
|
|
@@ -208,7 +244,7 @@ const createTaskCommentCommand = {
|
|
|
208
244
|
action: "deleted",
|
|
209
245
|
entity: comment,
|
|
210
246
|
identifiers: { id: comment.id, organizationId: comment.organizationId, tenantId: comment.tenantId },
|
|
211
|
-
events:
|
|
247
|
+
events: commentEvents(comment.visibility === "customer"),
|
|
212
248
|
indexer: commentCrudIndexer
|
|
213
249
|
});
|
|
214
250
|
}
|
|
@@ -216,7 +252,7 @@ const createTaskCommentCommand = {
|
|
|
216
252
|
const updateTaskCommentCommand = {
|
|
217
253
|
id: staffTimeTaskCommentCommandIds.update,
|
|
218
254
|
async prepare(rawInput, ctx) {
|
|
219
|
-
const parsed =
|
|
255
|
+
const parsed = staffTimeTaskCommentUpdateRequestSchema.safeParse(rawInput);
|
|
220
256
|
if (!parsed.success) return {};
|
|
221
257
|
const em = ctx.container.resolve("em");
|
|
222
258
|
const before = await loadCommentSnapshot(em, parsed.data.id, staffSnapshotScopeFromContext(ctx));
|
|
@@ -224,7 +260,7 @@ const updateTaskCommentCommand = {
|
|
|
224
260
|
return { before: { snapshot: before } };
|
|
225
261
|
},
|
|
226
262
|
async execute(rawInput, ctx) {
|
|
227
|
-
const parsed =
|
|
263
|
+
const parsed = staffTimeTaskCommentUpdateRequestSchema.parse(rawInput);
|
|
228
264
|
const { translate } = await resolveTranslations();
|
|
229
265
|
const em = ctx.container.resolve("em").fork();
|
|
230
266
|
const scope = commandActorScope(ctx);
|
|
@@ -237,11 +273,17 @@ const updateTaskCommentCommand = {
|
|
|
237
273
|
ensureOrganizationScope(ctx, comment.organizationId);
|
|
238
274
|
assertCommentVersion(ctx, comment);
|
|
239
275
|
await requireEditableComment(comment, ctx, translate);
|
|
276
|
+
const visibilityBefore = comment.visibility ?? "internal";
|
|
277
|
+
if (parsed.visibility === "customer" && visibilityBefore !== "customer") {
|
|
278
|
+
const task = await requireTaskInScope(em, comment.taskId, scope, translate);
|
|
279
|
+
if (task.source !== "portal") throw visibilityInvalidError(translate);
|
|
280
|
+
}
|
|
240
281
|
await withAtomicFlush(
|
|
241
282
|
em,
|
|
242
283
|
[
|
|
243
284
|
() => {
|
|
244
|
-
comment.body = parsed.body;
|
|
285
|
+
if (parsed.body !== void 0) comment.body = parsed.body;
|
|
286
|
+
if (parsed.visibility !== void 0) comment.visibility = parsed.visibility;
|
|
245
287
|
comment.updatedAt = /* @__PURE__ */ new Date();
|
|
246
288
|
}
|
|
247
289
|
],
|
|
@@ -252,7 +294,7 @@ const updateTaskCommentCommand = {
|
|
|
252
294
|
action: "updated",
|
|
253
295
|
entity: comment,
|
|
254
296
|
identifiers: { id: comment.id, organizationId: comment.organizationId, tenantId: comment.tenantId },
|
|
255
|
-
events:
|
|
297
|
+
events: commentEvents(visibilityBefore === "customer" || comment.visibility === "customer"),
|
|
256
298
|
indexer: commentCrudIndexer
|
|
257
299
|
});
|
|
258
300
|
return { commentId: comment.id, taskId: comment.taskId };
|
|
@@ -277,7 +319,7 @@ const updateTaskCommentCommand = {
|
|
|
277
319
|
changes: buildChanges(
|
|
278
320
|
before,
|
|
279
321
|
after,
|
|
280
|
-
["body"]
|
|
322
|
+
["body", "visibility"]
|
|
281
323
|
),
|
|
282
324
|
payload: {
|
|
283
325
|
undo: { before, after }
|
|
@@ -299,6 +341,7 @@ const updateTaskCommentCommand = {
|
|
|
299
341
|
[
|
|
300
342
|
() => {
|
|
301
343
|
comment.body = before.body;
|
|
344
|
+
comment.visibility = before.visibility ?? "internal";
|
|
302
345
|
comment.updatedAt = /* @__PURE__ */ new Date();
|
|
303
346
|
}
|
|
304
347
|
],
|
|
@@ -309,7 +352,7 @@ const updateTaskCommentCommand = {
|
|
|
309
352
|
action: "updated",
|
|
310
353
|
entity: comment,
|
|
311
354
|
identifiers: { id: comment.id, organizationId: comment.organizationId, tenantId: comment.tenantId },
|
|
312
|
-
events:
|
|
355
|
+
events: commentEvents(true),
|
|
313
356
|
indexer: commentCrudIndexer
|
|
314
357
|
});
|
|
315
358
|
}
|
|
@@ -358,7 +401,7 @@ const deleteTaskCommentCommand = {
|
|
|
358
401
|
action: "deleted",
|
|
359
402
|
entity: comment,
|
|
360
403
|
identifiers: { id: comment.id, organizationId: comment.organizationId, tenantId: comment.tenantId },
|
|
361
|
-
events:
|
|
404
|
+
events: commentEvents(comment.visibility === "customer"),
|
|
362
405
|
indexer: commentCrudIndexer
|
|
363
406
|
});
|
|
364
407
|
return { commentId: comment.id, taskId: comment.taskId };
|
|
@@ -402,6 +445,7 @@ const deleteTaskCommentCommand = {
|
|
|
402
445
|
taskId: before.taskId,
|
|
403
446
|
body: before.body,
|
|
404
447
|
authorUserId: before.authorUserId,
|
|
448
|
+
visibility: before.visibility ?? "internal",
|
|
405
449
|
createdAt: before.createdAt ? new Date(before.createdAt) : /* @__PURE__ */ new Date(),
|
|
406
450
|
updatedAt: /* @__PURE__ */ new Date(),
|
|
407
451
|
deletedAt: null
|
|
@@ -422,7 +466,7 @@ const deleteTaskCommentCommand = {
|
|
|
422
466
|
action: "created",
|
|
423
467
|
entity: comment,
|
|
424
468
|
identifiers: { id: before.id, organizationId: before.organizationId, tenantId: before.tenantId },
|
|
425
|
-
events:
|
|
469
|
+
events: commentEvents(before.visibility === "customer"),
|
|
426
470
|
indexer: commentCrudIndexer
|
|
427
471
|
});
|
|
428
472
|
}
|
|
@@ -432,7 +476,9 @@ registerCommand(updateTaskCommentCommand);
|
|
|
432
476
|
registerCommand(deleteTaskCommentCommand);
|
|
433
477
|
export {
|
|
434
478
|
STAFF_TIME_TASK_COMMENT_RESOURCE_KIND,
|
|
479
|
+
TASK_COMMENT_CUSTOMER_IMMUTABLE_CODE,
|
|
435
480
|
TASK_COMMENT_NOT_AUTHOR_CODE,
|
|
481
|
+
TASK_COMMENT_VISIBILITY_INVALID_CODE,
|
|
436
482
|
staffTimeTaskCommentCommandIds
|
|
437
483
|
};
|
|
438
484
|
//# sourceMappingURL=timesheets-task-comments.js.map
|