@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/staff/commands/timesheets-task-comments.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Task comment commands (T3.4b) \u2014 the \"Komentarze (2)\" thread of screen 7.\n *\n * US-C4 asks for context that lives with the work rather than in chat, so a\n * comment is only useful if the two things rendered next to its body are true:\n * *who* wrote it and *when*. Three decisions follow from that:\n *\n * 1. **`author_user_id` is stamped from the authenticated caller and is never\n * read from input.** `staffTimeTaskCommentCreateSchema` still declares the\n * field (it is the shared scoped-create shape), but this command discards\n * whatever arrives there. Attribution a client can set is not attribution \u2014\n * it is a free-text field with an authoritative-looking name. Deliberately\n * stricter than the shared `normalizeAuthorUserId` helper, which lets a\n * super-admin post on someone else's behalf: on a client-facing thread that\n * escape hatch would turn the avatar into a claim rather than a fact.\n * 2. **Editing is the author's own comment only**, unless the caller holds\n * `staff.timesheets.manage_all`. Deleting follows the same rule \u2014 a thread\n * someone else can rewrite is not a record. The manager exemption exists\n * because a client-facing thread sometimes has to have something removed,\n * and the grant is checked through `authorizeFeatures` so a wildcard\n * (`staff.*`, `staff.timesheets.*`) counts the way it does everywhere else.\n * 3. **Delete is soft.** The row keeps its body, its author and its timestamps,\n * so the audit trail (and undo) survives a comment leaving the thread.\n *\n * The parent task's *project* access is NOT resolved here \u2014 it is the route's\n * job (see `api/timesheets/tasks/[id]/comments/route.ts`), because only the\n * route knows the task id the caller addressed. What this file does enforce is\n * that the task exists inside the caller's tenant and organization, so a direct\n * command invocation cannot write a comment onto a foreign row.\n */\n\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 { assertOptimisticLock, readOptimisticLockExpected } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { buildChanges, emitCrudSideEffects, emitCrudUndoSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport { authorizeFeatures } from '@open-mercato/shared/security/featurePolicy'\nimport type { CrudEventsConfig, CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'\nimport { StaffTimeTask, StaffTimeTaskComment } from '../data/entities'\nimport { staffTimeTaskCommentCrudEvents } from '../lib/crud'\nimport {\n staffTimeTaskCommentCreateSchema,\n staffTimeTaskCommentUpdateSchema,\n type StaffTimeTaskCommentCreateInput,\n type StaffTimeTaskCommentUpdateInput,\n} from '../data/validators'\nimport {\n applyScopeToWhere,\n commandActorScope,\n commandInputScope,\n ensureOrganizationScope,\n ensureTenantScope,\n extractUndoPayload,\n scopedStaffSnapshotWhere,\n staffSnapshotScopeFromContext,\n staffSnapshotScopeFromSnapshot,\n type StaffCommandScope,\n type StaffSnapshotScope,\n} from './shared'\n\nexport const staffTimeTaskCommentCommandIds = {\n create: 'staff.timesheets.task_comments.create',\n update: 'staff.timesheets.task_comments.update',\n delete: 'staff.timesheets.task_comments.delete',\n} as const\n\n/**\n * Resource kind used for the audit log, the mutation-guard registry and the\n * command-level optimistic lock, matching the `staff.timesheets.<entity>` shape\n * the other hand-written timesheets routes present so an `OM_OPTIMISTIC_LOCK`\n * allow-list can name it.\n */\nexport const STAFF_TIME_TASK_COMMENT_RESOURCE_KIND = 'staff.timesheets.task_comment'\n\nexport const TASK_COMMENT_NOT_AUTHOR_CODE = 'task_comment_not_author'\n\nconst MANAGE_ALL_FEATURE = 'staff.timesheets.manage_all'\n\nconst AUTHOR_UUID_REGEX = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/\n\nconst commentCrudIndexer: CrudIndexerConfig<StaffTimeTaskComment> = {\n entityType: 'staff:staff_time_task_comment',\n}\n\n/**\n * `staff.timesheets.time_task_comment.{created,updated,deleted}` from `events.ts`.\n *\n * The route is hand-written (it must answer 404 rather than an empty page for a\n * task the caller cannot see), so it takes no `events:` config the CRUD factory\n * could read \u2014 the command emits the lifecycle events itself. It uses the shared\n * export rather than a local copy so the ids the module publishes and the ids this\n * command emits cannot drift apart.\n */\nconst commentCrudEvents: CrudEventsConfig<StaffTimeTaskComment> = staffTimeTaskCommentCrudEvents\n\ntype Translate = (key: string, fallback: string) => string\n\ntype CommentSnapshot = {\n id: string\n tenantId: string\n organizationId: string\n taskId: string\n body: string\n authorUserId: string | null\n createdAt: string | null\n deletedAt: string | null\n}\n\ntype CommentUndoPayload = {\n before?: CommentSnapshot | null\n after?: CommentSnapshot | null\n}\n\ntype RbacServiceLike = {\n getGrantedFeatures?: (\n userId: string,\n options: { tenantId: string | null; organizationId: string | null },\n ) => Promise<string[]>\n}\n\nfunction toCommentSnapshot(comment: StaffTimeTaskComment): CommentSnapshot {\n return {\n id: comment.id,\n tenantId: comment.tenantId,\n organizationId: comment.organizationId,\n taskId: comment.taskId,\n body: comment.body,\n authorUserId: comment.authorUserId ?? null,\n createdAt: comment.createdAt ? comment.createdAt.toISOString() : null,\n deletedAt: comment.deletedAt ? comment.deletedAt.toISOString() : null,\n }\n}\n\nasync function loadCommentSnapshot(\n em: EntityManager,\n id: string,\n scope?: StaffSnapshotScope | null,\n): Promise<CommentSnapshot | null> {\n const comment = await em.findOne(StaffTimeTaskComment, scopedStaffSnapshotWhere(id, scope))\n return comment ? toCommentSnapshot(comment) : null\n}\n\nfunction commentNotFoundError(translate: Translate): CrudHttpError {\n return new CrudHttpError(404, {\n error: translate('staff.time_tracking.taskComments.errors.notFound', 'Comment not found or not accessible.'),\n })\n}\n\nfunction taskNotFoundError(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 notAuthorError(translate: Translate): CrudHttpError {\n return new CrudHttpError(403, {\n code: TASK_COMMENT_NOT_AUTHOR_CODE,\n error: translate(\n 'staff.time_tracking.taskComments.errors.notAuthor',\n 'You can only edit or delete your own comments.',\n ),\n })\n}\n\n/**\n * The comment's author is whoever the request authenticated as \u2014 never the\n * `authorUserId` the payload carried. An API key has no human behind it, so it\n * writes an unattributed comment rather than borrowing an identity.\n */\nfunction stampAuthorUserId(ctx: CommandRuntimeContext): string | null {\n const auth = ctx.auth as { isApiKey?: boolean; sub?: string | null } | null | undefined\n if (!auth || auth.isApiKey === true) return null\n const sub = typeof auth.sub === 'string' ? auth.sub.trim() : ''\n if (!sub || !AUTHOR_UUID_REGEX.test(sub)) return null\n return sub\n}\n\n/**\n * Wildcard-aware `staff.timesheets.manage_all` check. Reads the grants from\n * `rbacService` rather than the token's feature array so a stale or trimmed\n * browser payload cannot widen the exemption, and fails closed when the grants\n * cannot be read \u2014 the caller then falls back to \"author only\", which is the\n * safe half of the rule.\n */\nasync function callerCanManageAll(ctx: CommandRuntimeContext): Promise<boolean> {\n if (ctx.auth?.isSuperAdmin === true || ctx.systemActor === true) return true\n const userId = ctx.auth?.sub ?? null\n const tenantId = ctx.auth?.tenantId ?? null\n const organizationId = ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null\n if (!userId || !tenantId) return false\n let grantedFeatures: string[] = []\n try {\n const rbac = ctx.container.resolve('rbacService') as RbacServiceLike | undefined\n if (rbac?.getGrantedFeatures) {\n grantedFeatures = (await rbac.getGrantedFeatures(userId, { tenantId, organizationId })) ?? []\n }\n } catch {\n return false\n }\n return authorizeFeatures([MANAGE_ALL_FEATURE], {\n grantedFeatures,\n scopeAllowed: Boolean(tenantId) && Boolean(organizationId),\n })\n}\n\nasync function requireEditableComment(\n comment: StaffTimeTaskComment,\n ctx: CommandRuntimeContext,\n translate: Translate,\n): Promise<void> {\n const actorId = stampAuthorUserId(ctx)\n if (actorId && comment.authorUserId === actorId) return\n if (await callerCanManageAll(ctx)) return\n throw notAuthorError(translate)\n}\n\nfunction assertCommentVersion(ctx: CommandRuntimeContext, comment: StaffTimeTaskComment): void {\n assertOptimisticLock({\n resourceKind: STAFF_TIME_TASK_COMMENT_RESOURCE_KIND,\n resourceId: comment.id,\n expected: readOptimisticLockExpected(ctx.request ?? null),\n current: comment.updatedAt,\n })\n}\n\nasync function requireTaskInScope(\n em: EntityManager,\n taskId: string,\n scope: StaffCommandScope,\n translate: Translate,\n): Promise<StaffTimeTask> {\n const task = await em.findOne(StaffTimeTask, applyScopeToWhere<StaffTimeTask>({ id: taskId, deletedAt: null }, scope))\n if (!task) throw taskNotFoundError(translate)\n return task\n}\n\nconst createTaskCommentCommand: CommandHandler<\n StaffTimeTaskCommentCreateInput,\n { commentId: string; taskId: string; authorUserId: string | null }\n> = {\n id: staffTimeTaskCommentCommandIds.create,\n async execute(rawInput, ctx) {\n const parsed = staffTimeTaskCommentCreateSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n const scope = commandInputScope(ctx, parsed.tenantId, parsed.organizationId)\n const { translate } = await resolveTranslations()\n // `parsed.authorUserId` is intentionally unread \u2014 see the file header.\n const authorUserId = stampAuthorUserId(ctx)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let created: StaffTimeTaskComment | null = null\n\n await withAtomicFlush(\n em,\n [\n () => requireTaskInScope(em, parsed.taskId, scope, translate),\n () => {\n created = em.create(StaffTimeTaskComment, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n taskId: parsed.taskId,\n body: parsed.body,\n authorUserId,\n createdAt: new Date(),\n updatedAt: new Date(),\n deletedAt: null,\n })\n em.persist(created)\n },\n ],\n { transaction: true, label: staffTimeTaskCommentCommandIds.create },\n )\n\n const record = created as StaffTimeTaskComment | null\n if (!record) throw commentNotFoundError(translate)\n\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'created',\n entity: record,\n identifiers: { id: record.id, organizationId: record.organizationId, tenantId: record.tenantId },\n events: commentCrudEvents,\n indexer: commentCrudIndexer,\n })\n\n return { commentId: record.id, taskId: record.taskId, authorUserId: record.authorUserId ?? null }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadCommentSnapshot(em, result.commentId, staffSnapshotScopeFromContext(ctx))\n if (!snapshot) return null\n return { snapshot }\n },\n buildLog: async ({ result, snapshots }) => {\n const snapshot = (snapshots.after as { snapshot?: CommentSnapshot } | undefined)?.snapshot\n if (!snapshot) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.task_comments.create', 'Create task comment'),\n resourceKind: STAFF_TIME_TASK_COMMENT_RESOURCE_KIND,\n resourceId: result.commentId,\n parentResourceKind: 'staff.timesheets.task',\n parentResourceId: snapshot.taskId,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n snapshotAfter: snapshot,\n payload: {\n undo: { after: snapshot } satisfies CommentUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<CommentUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const comment = await em.findOne(\n StaffTimeTaskComment,\n scopedStaffSnapshotWhere(after.id, staffSnapshotScopeFromSnapshot(after)),\n )\n if (!comment) return\n\n await withAtomicFlush(\n em,\n [\n () => {\n comment.deletedAt = new Date()\n comment.updatedAt = new Date()\n },\n ],\n { transaction: true, label: `${staffTimeTaskCommentCommandIds.create}.undo` },\n )\n\n await emitCrudUndoSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'deleted',\n entity: comment,\n identifiers: { id: comment.id, organizationId: comment.organizationId, tenantId: comment.tenantId },\n events: commentCrudEvents,\n indexer: commentCrudIndexer,\n })\n },\n}\n\nconst updateTaskCommentCommand: CommandHandler<\n StaffTimeTaskCommentUpdateInput,\n { commentId: string; taskId: string }\n> = {\n id: staffTimeTaskCommentCommandIds.update,\n async prepare(rawInput, ctx) {\n const parsed = staffTimeTaskCommentUpdateSchema.safeParse(rawInput)\n if (!parsed.success) return {}\n const em = ctx.container.resolve('em') as EntityManager\n const before = await loadCommentSnapshot(em, parsed.data.id, staffSnapshotScopeFromContext(ctx))\n if (!before) return {}\n return { before: { snapshot: before } }\n },\n async execute(rawInput, ctx) {\n const parsed = staffTimeTaskCommentUpdateSchema.parse(rawInput)\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const scope = commandActorScope(ctx)\n\n const comment = await em.findOne(\n StaffTimeTaskComment,\n applyScopeToWhere<StaffTimeTaskComment>({ id: parsed.id, deletedAt: null }, scope),\n )\n if (!comment) throw commentNotFoundError(translate)\n ensureTenantScope(ctx, comment.tenantId)\n ensureOrganizationScope(ctx, comment.organizationId)\n assertCommentVersion(ctx, comment)\n await requireEditableComment(comment, ctx, translate)\n\n await withAtomicFlush(\n em,\n [\n () => {\n comment.body = parsed.body\n comment.updatedAt = new Date()\n },\n ],\n { transaction: true, label: staffTimeTaskCommentCommandIds.update },\n )\n\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'updated',\n entity: comment,\n identifiers: { id: comment.id, organizationId: comment.organizationId, tenantId: comment.tenantId },\n events: commentCrudEvents,\n indexer: commentCrudIndexer,\n })\n\n return { commentId: comment.id, taskId: comment.taskId }\n },\n buildLog: async ({ snapshots, ctx }) => {\n const before = (snapshots.before as { snapshot?: CommentSnapshot } | undefined)?.snapshot\n if (!before) return null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const after = await loadCommentSnapshot(em, before.id, staffSnapshotScopeFromSnapshot(before))\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.task_comments.update', 'Update task comment'),\n resourceKind: STAFF_TIME_TASK_COMMENT_RESOURCE_KIND,\n resourceId: before.id,\n parentResourceKind: 'staff.timesheets.task',\n parentResourceId: before.taskId,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n snapshotAfter: after,\n changes: buildChanges(\n before as unknown as Record<string, unknown>,\n after as unknown as Record<string, unknown>,\n ['body'],\n ),\n payload: {\n undo: { before, after } satisfies CommentUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<CommentUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const comment = await em.findOne(\n StaffTimeTaskComment,\n scopedStaffSnapshotWhere(before.id, staffSnapshotScopeFromSnapshot(before)),\n )\n if (!comment) return\n\n await withAtomicFlush(\n em,\n [\n () => {\n comment.body = before.body\n comment.updatedAt = new Date()\n },\n ],\n { transaction: true, label: `${staffTimeTaskCommentCommandIds.update}.undo` },\n )\n\n await emitCrudUndoSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'updated',\n entity: comment,\n identifiers: { id: comment.id, organizationId: comment.organizationId, tenantId: comment.tenantId },\n events: commentCrudEvents,\n indexer: commentCrudIndexer,\n })\n },\n}\n\nconst deleteTaskCommentCommand: CommandHandler<{ id?: string }, { commentId: string; taskId: string }> = {\n id: staffTimeTaskCommentCommandIds.delete,\n async prepare(input, ctx) {\n const id = input?.id\n if (!id) return {}\n const em = ctx.container.resolve('em') as EntityManager\n const before = await loadCommentSnapshot(em, id, staffSnapshotScopeFromContext(ctx))\n if (!before) return {}\n return { before: { snapshot: before } }\n },\n async execute(input, ctx) {\n const { translate } = await resolveTranslations()\n const id = input?.id\n if (!id) {\n throw new CrudHttpError(400, {\n error: translate('staff.time_tracking.taskComments.errors.idRequired', 'Comment id is required.'),\n })\n }\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const scope = commandActorScope(ctx)\n const comment = await em.findOne(\n StaffTimeTaskComment,\n applyScopeToWhere<StaffTimeTaskComment>({ id, deletedAt: null }, scope),\n )\n if (!comment) throw commentNotFoundError(translate)\n ensureTenantScope(ctx, comment.tenantId)\n ensureOrganizationScope(ctx, comment.organizationId)\n assertCommentVersion(ctx, comment)\n await requireEditableComment(comment, ctx, translate)\n\n // Soft delete: the body, the author and the timestamps stay on the row so\n // the audit trail \u2014 and undo \u2014 still has something to point at.\n await withAtomicFlush(\n em,\n [\n () => {\n comment.deletedAt = new Date()\n comment.updatedAt = new Date()\n },\n ],\n { transaction: true, label: staffTimeTaskCommentCommandIds.delete },\n )\n\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'deleted',\n entity: comment,\n identifiers: { id: comment.id, organizationId: comment.organizationId, tenantId: comment.tenantId },\n events: commentCrudEvents,\n indexer: commentCrudIndexer,\n })\n\n return { commentId: comment.id, taskId: comment.taskId }\n },\n buildLog: async ({ snapshots }) => {\n const before = (snapshots.before as { snapshot?: CommentSnapshot } | undefined)?.snapshot\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.task_comments.delete', 'Delete task comment'),\n resourceKind: STAFF_TIME_TASK_COMMENT_RESOURCE_KIND,\n resourceId: before.id,\n parentResourceKind: 'staff.timesheets.task',\n parentResourceId: before.taskId,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: {\n undo: { before } satisfies CommentUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<CommentUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let comment = await em.findOne(\n StaffTimeTaskComment,\n scopedStaffSnapshotWhere(before.id, staffSnapshotScopeFromSnapshot(before)),\n )\n\n await withAtomicFlush(\n em,\n [\n () => {\n if (!comment) {\n comment = em.create(StaffTimeTaskComment, {\n id: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n taskId: before.taskId,\n body: before.body,\n authorUserId: before.authorUserId,\n createdAt: before.createdAt ? new Date(before.createdAt) : new Date(),\n updatedAt: new Date(),\n deletedAt: null,\n })\n em.persist(comment)\n return\n }\n comment.body = before.body\n comment.authorUserId = before.authorUserId\n comment.deletedAt = null\n comment.updatedAt = new Date()\n },\n ],\n { transaction: true, label: `${staffTimeTaskCommentCommandIds.delete}.undo` },\n )\n\n await emitCrudUndoSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'created',\n entity: comment,\n identifiers: { id: before.id, organizationId: before.organizationId, tenantId: before.tenantId },\n events: commentCrudEvents,\n indexer: commentCrudIndexer,\n })\n },\n}\n\nregisterCommand(createTaskCommentCommand)\nregisterCommand(updateTaskCommentCommand)\nregisterCommand(deleteTaskCommentCommand)\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * Task comment commands (T3.4b) \u2014 the \"Komentarze (2)\" thread of screen 7.\n *\n * US-C4 asks for context that lives with the work rather than in chat, so a\n * comment is only useful if the two things rendered next to its body are true:\n * *who* wrote it and *when*. Three decisions follow from that:\n *\n * 1. **`author_user_id` is stamped from the authenticated caller and is never\n * read from input.** `staffTimeTaskCommentCreateSchema` still declares the\n * field (it is the shared scoped-create shape), but this command discards\n * whatever arrives there. Attribution a client can set is not attribution \u2014\n * it is a free-text field with an authoritative-looking name. Deliberately\n * stricter than the shared `normalizeAuthorUserId` helper, which lets a\n * super-admin post on someone else's behalf: on a client-facing thread that\n * escape hatch would turn the avatar into a claim rather than a fact.\n * 2. **Editing is the author's own comment only**, unless the caller holds\n * `staff.timesheets.manage_all`. Deleting follows the same rule \u2014 a thread\n * someone else can rewrite is not a record. The manager exemption exists\n * because a client-facing thread sometimes has to have something removed,\n * and the grant is checked through `authorizeFeatures` so a wildcard\n * (`staff.*`, `staff.timesheets.*`) counts the way it does everywhere else.\n * 3. **Delete is soft.** The row keeps its body, its author and its timestamps,\n * so the audit trail (and undo) survives a comment leaving the thread.\n *\n * The parent task's *project* access is NOT resolved here \u2014 it is the route's\n * job (see `api/timesheets/tasks/[id]/comments/route.ts`), because only the\n * route knows the task id the caller addressed. What this file does enforce is\n * that the task exists inside the caller's tenant and organization, so a direct\n * command invocation cannot write a comment onto a foreign row.\n */\n\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 { assertOptimisticLock, readOptimisticLockExpected } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { buildChanges, emitCrudSideEffects, emitCrudUndoSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport { authorizeFeatures } from '@open-mercato/shared/security/featurePolicy'\nimport type { CrudEventsConfig, CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'\nimport { StaffTimeTask, StaffTimeTaskComment, type StaffTimeTaskCommentVisibility } from '../data/entities'\nimport { staffTimeTaskCommentCrudEvents } from '../lib/crud'\nimport {\n staffTimeTaskCommentCreateSchema,\n staffTimeTaskCommentUpdateRequestSchema,\n type StaffTimeTaskCommentCreateInput,\n type StaffTimeTaskCommentUpdateRequestInput,\n} from '../data/validators'\nimport {\n applyScopeToWhere,\n commandActorScope,\n commandInputScope,\n ensureOrganizationScope,\n ensureTenantScope,\n extractUndoPayload,\n scopedStaffSnapshotWhere,\n staffSnapshotScopeFromContext,\n staffSnapshotScopeFromSnapshot,\n type StaffCommandScope,\n type StaffSnapshotScope,\n} from './shared'\n\nexport const staffTimeTaskCommentCommandIds = {\n create: 'staff.timesheets.task_comments.create',\n update: 'staff.timesheets.task_comments.update',\n delete: 'staff.timesheets.task_comments.delete',\n} as const\n\n/**\n * Resource kind used for the audit log, the mutation-guard registry and the\n * command-level optimistic lock, matching the `staff.timesheets.<entity>` shape\n * the other hand-written timesheets routes present so an `OM_OPTIMISTIC_LOCK`\n * allow-list can name it.\n */\nexport const STAFF_TIME_TASK_COMMENT_RESOURCE_KIND = 'staff.timesheets.task_comment'\n\nexport const TASK_COMMENT_NOT_AUTHOR_CODE = 'task_comment_not_author'\n\nconst MANAGE_ALL_FEATURE = 'staff.timesheets.manage_all'\n\nconst AUTHOR_UUID_REGEX = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/\n\nconst commentCrudIndexer: CrudIndexerConfig<StaffTimeTaskComment> = {\n entityType: 'staff:staff_time_task_comment',\n}\n\n/**\n * `staff.timesheets.time_task_comment.{created,updated,deleted}` from `events.ts`.\n *\n * The route is hand-written (it must answer 404 rather than an empty page for a\n * task the caller cannot see), so it takes no `events:` config the CRUD factory\n * could read \u2014 the command emits the lifecycle events itself. It uses the shared\n * export rather than a local copy so the ids the module publishes and the ids this\n * command emits cannot drift apart.\n */\nconst commentCrudEvents: CrudEventsConfig<StaffTimeTaskComment> = staffTimeTaskCommentCrudEvents\n\n/**\n * The comment CRUD events carry `portalVisible`: true when the comment is visible to\n * the customer before OR after the change, so publishing, retracting and deleting a\n * reply all refresh the portal. Internal notes never set it.\n */\nfunction commentEvents(portalVisible: boolean): CrudEventsConfig<StaffTimeTaskComment> {\n const base = staffTimeTaskCommentCrudEvents\n return {\n ...base,\n buildPayload: (context) => ({\n ...((base.buildPayload ? base.buildPayload(context) : {}) as Record<string, unknown>),\n portalVisible,\n }),\n }\n}\n\nexport const TASK_COMMENT_CUSTOMER_IMMUTABLE_CODE = 'task_comment_customer_immutable'\nexport const TASK_COMMENT_VISIBILITY_INVALID_CODE = 'task_comment_visibility_invalid'\n\nfunction customerCommentImmutableError(translate: Translate): CrudHttpError {\n return new CrudHttpError(403, {\n code: TASK_COMMENT_CUSTOMER_IMMUTABLE_CODE,\n error: translate(\n 'staff.time_tracking.taskComments.errors.customerImmutable',\n 'Messages written by the customer cannot be edited or deleted.',\n ),\n })\n}\n\nfunction visibilityInvalidError(translate: Translate): CrudHttpError {\n const message = translate(\n 'staff.time_tracking.taskComments.errors.visibilityPortalOnly',\n 'Only customer support tickets can carry replies visible to the customer.',\n )\n return new CrudHttpError(422, { code: TASK_COMMENT_VISIBILITY_INVALID_CODE, error: message, fieldErrors: { visibility: message } })\n}\n\ntype Translate = (key: string, fallback: string) => string\n\ntype CommentSnapshot = {\n id: string\n tenantId: string\n organizationId: string\n taskId: string\n body: string\n authorUserId: string | null\n visibility: StaffTimeTaskCommentVisibility\n authorKind: string\n createdAt: string | null\n deletedAt: string | null\n}\n\ntype CommentUndoPayload = {\n before?: CommentSnapshot | null\n after?: CommentSnapshot | null\n}\n\ntype RbacServiceLike = {\n getGrantedFeatures?: (\n userId: string,\n options: { tenantId: string | null; organizationId: string | null },\n ) => Promise<string[]>\n}\n\nfunction toCommentSnapshot(comment: StaffTimeTaskComment): CommentSnapshot {\n return {\n id: comment.id,\n tenantId: comment.tenantId,\n organizationId: comment.organizationId,\n taskId: comment.taskId,\n body: comment.body,\n authorUserId: comment.authorUserId ?? null,\n visibility: comment.visibility ?? 'internal',\n authorKind: comment.authorKind ?? 'staff',\n createdAt: comment.createdAt ? comment.createdAt.toISOString() : null,\n deletedAt: comment.deletedAt ? comment.deletedAt.toISOString() : null,\n }\n}\n\nasync function loadCommentSnapshot(\n em: EntityManager,\n id: string,\n scope?: StaffSnapshotScope | null,\n): Promise<CommentSnapshot | null> {\n const comment = await em.findOne(StaffTimeTaskComment, scopedStaffSnapshotWhere(id, scope))\n return comment ? toCommentSnapshot(comment) : null\n}\n\nfunction commentNotFoundError(translate: Translate): CrudHttpError {\n return new CrudHttpError(404, {\n error: translate('staff.time_tracking.taskComments.errors.notFound', 'Comment not found or not accessible.'),\n })\n}\n\nfunction taskNotFoundError(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 notAuthorError(translate: Translate): CrudHttpError {\n return new CrudHttpError(403, {\n code: TASK_COMMENT_NOT_AUTHOR_CODE,\n error: translate(\n 'staff.time_tracking.taskComments.errors.notAuthor',\n 'You can only edit or delete your own comments.',\n ),\n })\n}\n\n/**\n * The comment's author is whoever the request authenticated as \u2014 never the\n * `authorUserId` the payload carried. An API key has no human behind it, so it\n * writes an unattributed comment rather than borrowing an identity.\n */\nfunction stampAuthorUserId(ctx: CommandRuntimeContext): string | null {\n const auth = ctx.auth as { isApiKey?: boolean; sub?: string | null } | null | undefined\n if (!auth || auth.isApiKey === true) return null\n const sub = typeof auth.sub === 'string' ? auth.sub.trim() : ''\n if (!sub || !AUTHOR_UUID_REGEX.test(sub)) return null\n return sub\n}\n\n/**\n * Wildcard-aware `staff.timesheets.manage_all` check. Reads the grants from\n * `rbacService` rather than the token's feature array so a stale or trimmed\n * browser payload cannot widen the exemption, and fails closed when the grants\n * cannot be read \u2014 the caller then falls back to \"author only\", which is the\n * safe half of the rule.\n */\nasync function callerCanManageAll(ctx: CommandRuntimeContext): Promise<boolean> {\n if (ctx.auth?.isSuperAdmin === true || ctx.systemActor === true) return true\n const userId = ctx.auth?.sub ?? null\n const tenantId = ctx.auth?.tenantId ?? null\n const organizationId = ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null\n if (!userId || !tenantId) return false\n let grantedFeatures: string[] = []\n try {\n const rbac = ctx.container.resolve('rbacService') as RbacServiceLike | undefined\n if (rbac?.getGrantedFeatures) {\n grantedFeatures = (await rbac.getGrantedFeatures(userId, { tenantId, organizationId })) ?? []\n }\n } catch {\n return false\n }\n return authorizeFeatures([MANAGE_ALL_FEATURE], {\n grantedFeatures,\n scopeAllowed: Boolean(tenantId) && Boolean(organizationId),\n })\n}\n\nasync function requireEditableComment(\n comment: StaffTimeTaskComment,\n ctx: CommandRuntimeContext,\n translate: Translate,\n): Promise<void> {\n if (comment.authorKind === 'customer') throw customerCommentImmutableError(translate)\n const actorId = stampAuthorUserId(ctx)\n if (actorId && comment.authorUserId === actorId) return\n if (await callerCanManageAll(ctx)) return\n throw notAuthorError(translate)\n}\n\nfunction assertCommentVersion(ctx: CommandRuntimeContext, comment: StaffTimeTaskComment): void {\n assertOptimisticLock({\n resourceKind: STAFF_TIME_TASK_COMMENT_RESOURCE_KIND,\n resourceId: comment.id,\n expected: readOptimisticLockExpected(ctx.request ?? null),\n current: comment.updatedAt,\n })\n}\n\nasync function requireTaskInScope(\n em: EntityManager,\n taskId: string,\n scope: StaffCommandScope,\n translate: Translate,\n): Promise<StaffTimeTask> {\n const task = await em.findOne(StaffTimeTask, applyScopeToWhere<StaffTimeTask>({ id: taskId, deletedAt: null }, scope))\n if (!task) throw taskNotFoundError(translate)\n return task\n}\n\nconst createTaskCommentCommand: CommandHandler<\n StaffTimeTaskCommentCreateInput,\n { commentId: string; taskId: string; authorUserId: string | null }\n> = {\n id: staffTimeTaskCommentCommandIds.create,\n async execute(rawInput, ctx) {\n const parsed = staffTimeTaskCommentCreateSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n const scope = commandInputScope(ctx, parsed.tenantId, parsed.organizationId)\n const { translate } = await resolveTranslations()\n // `parsed.authorUserId` is intentionally unread \u2014 see the file header.\n const authorUserId = stampAuthorUserId(ctx)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let created: StaffTimeTaskComment | null = null\n\n await withAtomicFlush(\n em,\n [\n async () => {\n const task = await requireTaskInScope(em, parsed.taskId, scope, translate)\n if (parsed.visibility === 'customer' && task.source !== 'portal') throw visibilityInvalidError(translate)\n },\n () => {\n created = em.create(StaffTimeTaskComment, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n taskId: parsed.taskId,\n body: parsed.body,\n authorUserId,\n visibility: parsed.visibility ?? 'internal',\n authorKind: 'staff',\n createdAt: new Date(),\n updatedAt: new Date(),\n deletedAt: null,\n })\n em.persist(created)\n },\n ],\n { transaction: true, label: staffTimeTaskCommentCommandIds.create },\n )\n\n const record = created as StaffTimeTaskComment | null\n if (!record) throw commentNotFoundError(translate)\n\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'created',\n entity: record,\n identifiers: { id: record.id, organizationId: record.organizationId, tenantId: record.tenantId },\n events: commentEvents(record.visibility === 'customer'),\n indexer: commentCrudIndexer,\n })\n\n return { commentId: record.id, taskId: record.taskId, authorUserId: record.authorUserId ?? null }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadCommentSnapshot(em, result.commentId, staffSnapshotScopeFromContext(ctx))\n if (!snapshot) return null\n return { snapshot }\n },\n buildLog: async ({ result, snapshots }) => {\n const snapshot = (snapshots.after as { snapshot?: CommentSnapshot } | undefined)?.snapshot\n if (!snapshot) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.task_comments.create', 'Create task comment'),\n resourceKind: STAFF_TIME_TASK_COMMENT_RESOURCE_KIND,\n resourceId: result.commentId,\n parentResourceKind: 'staff.timesheets.task',\n parentResourceId: snapshot.taskId,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n snapshotAfter: snapshot,\n payload: {\n undo: { after: snapshot } satisfies CommentUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<CommentUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const comment = await em.findOne(\n StaffTimeTaskComment,\n scopedStaffSnapshotWhere(after.id, staffSnapshotScopeFromSnapshot(after)),\n )\n if (!comment) return\n\n await withAtomicFlush(\n em,\n [\n () => {\n comment.deletedAt = new Date()\n comment.updatedAt = new Date()\n },\n ],\n { transaction: true, label: `${staffTimeTaskCommentCommandIds.create}.undo` },\n )\n\n await emitCrudUndoSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'deleted',\n entity: comment,\n identifiers: { id: comment.id, organizationId: comment.organizationId, tenantId: comment.tenantId },\n events: commentEvents(comment.visibility === 'customer'),\n indexer: commentCrudIndexer,\n })\n },\n}\n\nconst updateTaskCommentCommand: CommandHandler<\n StaffTimeTaskCommentUpdateRequestInput,\n { commentId: string; taskId: string }\n> = {\n id: staffTimeTaskCommentCommandIds.update,\n async prepare(rawInput, ctx) {\n const parsed = staffTimeTaskCommentUpdateRequestSchema.safeParse(rawInput)\n if (!parsed.success) return {}\n const em = ctx.container.resolve('em') as EntityManager\n const before = await loadCommentSnapshot(em, parsed.data.id, staffSnapshotScopeFromContext(ctx))\n if (!before) return {}\n return { before: { snapshot: before } }\n },\n async execute(rawInput, ctx) {\n const parsed = staffTimeTaskCommentUpdateRequestSchema.parse(rawInput)\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const scope = commandActorScope(ctx)\n\n const comment = await em.findOne(\n StaffTimeTaskComment,\n applyScopeToWhere<StaffTimeTaskComment>({ id: parsed.id, deletedAt: null }, scope),\n )\n if (!comment) throw commentNotFoundError(translate)\n ensureTenantScope(ctx, comment.tenantId)\n ensureOrganizationScope(ctx, comment.organizationId)\n assertCommentVersion(ctx, comment)\n await requireEditableComment(comment, ctx, translate)\n const visibilityBefore = comment.visibility ?? 'internal'\n if (parsed.visibility === 'customer' && visibilityBefore !== 'customer') {\n const task = await requireTaskInScope(em, comment.taskId, scope, translate)\n if (task.source !== 'portal') throw visibilityInvalidError(translate)\n }\n\n await withAtomicFlush(\n em,\n [\n () => {\n if (parsed.body !== undefined) comment.body = parsed.body\n if (parsed.visibility !== undefined) comment.visibility = parsed.visibility\n comment.updatedAt = new Date()\n },\n ],\n { transaction: true, label: staffTimeTaskCommentCommandIds.update },\n )\n\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'updated',\n entity: comment,\n identifiers: { id: comment.id, organizationId: comment.organizationId, tenantId: comment.tenantId },\n events: commentEvents(visibilityBefore === 'customer' || comment.visibility === 'customer'),\n indexer: commentCrudIndexer,\n })\n\n return { commentId: comment.id, taskId: comment.taskId }\n },\n buildLog: async ({ snapshots, ctx }) => {\n const before = (snapshots.before as { snapshot?: CommentSnapshot } | undefined)?.snapshot\n if (!before) return null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const after = await loadCommentSnapshot(em, before.id, staffSnapshotScopeFromSnapshot(before))\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.task_comments.update', 'Update task comment'),\n resourceKind: STAFF_TIME_TASK_COMMENT_RESOURCE_KIND,\n resourceId: before.id,\n parentResourceKind: 'staff.timesheets.task',\n parentResourceId: before.taskId,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n snapshotAfter: after,\n changes: buildChanges(\n before as unknown as Record<string, unknown>,\n after as unknown as Record<string, unknown>,\n ['body', 'visibility'],\n ),\n payload: {\n undo: { before, after } satisfies CommentUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<CommentUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const comment = await em.findOne(\n StaffTimeTaskComment,\n scopedStaffSnapshotWhere(before.id, staffSnapshotScopeFromSnapshot(before)),\n )\n if (!comment) return\n\n await withAtomicFlush(\n em,\n [\n () => {\n comment.body = before.body\n comment.visibility = before.visibility ?? 'internal'\n comment.updatedAt = new Date()\n },\n ],\n { transaction: true, label: `${staffTimeTaskCommentCommandIds.update}.undo` },\n )\n\n await emitCrudUndoSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'updated',\n entity: comment,\n identifiers: { id: comment.id, organizationId: comment.organizationId, tenantId: comment.tenantId },\n events: commentEvents(true),\n indexer: commentCrudIndexer,\n })\n },\n}\n\nconst deleteTaskCommentCommand: CommandHandler<{ id?: string }, { commentId: string; taskId: string }> = {\n id: staffTimeTaskCommentCommandIds.delete,\n async prepare(input, ctx) {\n const id = input?.id\n if (!id) return {}\n const em = ctx.container.resolve('em') as EntityManager\n const before = await loadCommentSnapshot(em, id, staffSnapshotScopeFromContext(ctx))\n if (!before) return {}\n return { before: { snapshot: before } }\n },\n async execute(input, ctx) {\n const { translate } = await resolveTranslations()\n const id = input?.id\n if (!id) {\n throw new CrudHttpError(400, {\n error: translate('staff.time_tracking.taskComments.errors.idRequired', 'Comment id is required.'),\n })\n }\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const scope = commandActorScope(ctx)\n const comment = await em.findOne(\n StaffTimeTaskComment,\n applyScopeToWhere<StaffTimeTaskComment>({ id, deletedAt: null }, scope),\n )\n if (!comment) throw commentNotFoundError(translate)\n ensureTenantScope(ctx, comment.tenantId)\n ensureOrganizationScope(ctx, comment.organizationId)\n assertCommentVersion(ctx, comment)\n await requireEditableComment(comment, ctx, translate)\n\n // Soft delete: the body, the author and the timestamps stay on the row so\n // the audit trail \u2014 and undo \u2014 still has something to point at.\n await withAtomicFlush(\n em,\n [\n () => {\n comment.deletedAt = new Date()\n comment.updatedAt = new Date()\n },\n ],\n { transaction: true, label: staffTimeTaskCommentCommandIds.delete },\n )\n\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'deleted',\n entity: comment,\n identifiers: { id: comment.id, organizationId: comment.organizationId, tenantId: comment.tenantId },\n events: commentEvents(comment.visibility === 'customer'),\n indexer: commentCrudIndexer,\n })\n\n return { commentId: comment.id, taskId: comment.taskId }\n },\n buildLog: async ({ snapshots }) => {\n const before = (snapshots.before as { snapshot?: CommentSnapshot } | undefined)?.snapshot\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.task_comments.delete', 'Delete task comment'),\n resourceKind: STAFF_TIME_TASK_COMMENT_RESOURCE_KIND,\n resourceId: before.id,\n parentResourceKind: 'staff.timesheets.task',\n parentResourceId: before.taskId,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: {\n undo: { before } satisfies CommentUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<CommentUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let comment = await em.findOne(\n StaffTimeTaskComment,\n scopedStaffSnapshotWhere(before.id, staffSnapshotScopeFromSnapshot(before)),\n )\n\n await withAtomicFlush(\n em,\n [\n () => {\n if (!comment) {\n comment = em.create(StaffTimeTaskComment, {\n id: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n taskId: before.taskId,\n body: before.body,\n authorUserId: before.authorUserId,\n visibility: before.visibility ?? 'internal',\n createdAt: before.createdAt ? new Date(before.createdAt) : new Date(),\n updatedAt: new Date(),\n deletedAt: null,\n })\n em.persist(comment)\n return\n }\n comment.body = before.body\n comment.authorUserId = before.authorUserId\n comment.deletedAt = null\n comment.updatedAt = new Date()\n },\n ],\n { transaction: true, label: `${staffTimeTaskCommentCommandIds.delete}.undo` },\n )\n\n await emitCrudUndoSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'created',\n entity: comment,\n identifiers: { id: before.id, organizationId: before.organizationId, tenantId: before.tenantId },\n events: commentEvents(before.visibility === 'customer'),\n indexer: commentCrudIndexer,\n })\n },\n}\n\nregisterCommand(createTaskCommentCommand)\nregisterCommand(updateTaskCommentCommand)\nregisterCommand(deleteTaskCommentCommand)\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -3,11 +3,13 @@ import { registerCommand } from "@open-mercato/shared/lib/commands";
|
|
|
3
3
|
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
4
4
|
import { enforceCommandOptimisticLockWithGuards } from "@open-mercato/shared/lib/crud/optimistic-lock-command";
|
|
5
5
|
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
6
|
+
import { getTelemetryRuntime } from "@open-mercato/shared/lib/telemetry/runtime";
|
|
6
7
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
7
8
|
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
8
9
|
import { buildChanges, emitCrudSideEffects, emitCrudUndoSideEffects } from "@open-mercato/shared/lib/commands/helpers";
|
|
10
|
+
import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
9
11
|
import { withAtomicFlush } from "@open-mercato/shared/lib/commands/flush";
|
|
10
|
-
import {
|
|
12
|
+
import { StaffSupportTicket, StaffTeamMember, StaffTimeTask, StaffTimeTaskStatus } from "../data/entities.js";
|
|
11
13
|
import {
|
|
12
14
|
staffTimeTaskCreateSchema,
|
|
13
15
|
staffTimeTaskStatusChangeSchema,
|
|
@@ -31,7 +33,9 @@ import {
|
|
|
31
33
|
commandInputScope,
|
|
32
34
|
ensureOrganizationScope,
|
|
33
35
|
ensureTenantScope,
|
|
36
|
+
explicitStaffCommandScope,
|
|
34
37
|
extractUndoPayload,
|
|
38
|
+
lockLiveProjectForWrite,
|
|
35
39
|
scopeForDecryption,
|
|
36
40
|
scopedStaffSnapshotWhere,
|
|
37
41
|
staffSnapshotScopeFromContext,
|
|
@@ -193,10 +197,7 @@ function referenceConflictError(translate) {
|
|
|
193
197
|
});
|
|
194
198
|
}
|
|
195
199
|
async function requireProject(em, timeProjectId, scope, translate) {
|
|
196
|
-
const project = await em
|
|
197
|
-
StaffTimeProject,
|
|
198
|
-
applyScopeToWhere({ id: timeProjectId, deletedAt: null }, scope)
|
|
199
|
-
);
|
|
200
|
+
const project = await lockLiveProjectForWrite(em, timeProjectId, scope);
|
|
200
201
|
if (!project) throw projectNotFoundError(translate);
|
|
201
202
|
return project;
|
|
202
203
|
}
|
|
@@ -407,6 +408,25 @@ const createTaskCommand = {
|
|
|
407
408
|
});
|
|
408
409
|
}
|
|
409
410
|
};
|
|
411
|
+
function emitTaskAssigneeChanged(task, previousAssigneeStaffMemberId, changeKind, actorUserId2 = null) {
|
|
412
|
+
void emitStaffEvent(
|
|
413
|
+
"staff.timesheets.time_task.assignee_changed",
|
|
414
|
+
{
|
|
415
|
+
id: task.id,
|
|
416
|
+
tenantId: task.tenantId,
|
|
417
|
+
organizationId: task.organizationId,
|
|
418
|
+
timeProjectId: task.timeProjectId,
|
|
419
|
+
previousAssigneeStaffMemberId,
|
|
420
|
+
assigneeStaffMemberId: task.assigneeStaffMemberId ?? null,
|
|
421
|
+
changeKind,
|
|
422
|
+
actorUserId: actorUserId2
|
|
423
|
+
},
|
|
424
|
+
{ persistent: true }
|
|
425
|
+
).catch((err) => {
|
|
426
|
+
logger.error("staff.timesheets.time_task.assignee_changed emit failed", { err, taskId: task.id });
|
|
427
|
+
getTelemetryRuntime()?.reportError(err, { module: "staff", code: "staff.task_assignee_changed_emit_failed" });
|
|
428
|
+
});
|
|
429
|
+
}
|
|
410
430
|
const updateTaskCommand = {
|
|
411
431
|
id: staffTimeTaskCommandIds.update,
|
|
412
432
|
async prepare(rawInput, ctx) {
|
|
@@ -433,6 +453,7 @@ const updateTaskCommand = {
|
|
|
433
453
|
let targetStatus = null;
|
|
434
454
|
let targetParentTaskId;
|
|
435
455
|
let targetAssigneeStaffMemberId;
|
|
456
|
+
const previousAssigneeStaffMemberId = task.assigneeStaffMemberId ?? null;
|
|
436
457
|
await withAtomicFlush(
|
|
437
458
|
em,
|
|
438
459
|
[
|
|
@@ -483,6 +504,10 @@ const updateTaskCommand = {
|
|
|
483
504
|
events: taskCrudEvents,
|
|
484
505
|
indexer: taskCrudIndexer
|
|
485
506
|
});
|
|
507
|
+
const nextAssigneeStaffMemberId = task.assigneeStaffMemberId ?? null;
|
|
508
|
+
if (targetAssigneeStaffMemberId !== void 0 && nextAssigneeStaffMemberId !== previousAssigneeStaffMemberId) {
|
|
509
|
+
emitTaskAssigneeChanged(task, previousAssigneeStaffMemberId, "manual", ctx.auth?.sub ?? null);
|
|
510
|
+
}
|
|
486
511
|
return { taskId: task.id };
|
|
487
512
|
},
|
|
488
513
|
buildLog: async ({ snapshots, ctx }) => {
|
|
@@ -570,6 +595,8 @@ const deleteTaskCommand = {
|
|
|
570
595
|
ensureOrganizationScope(ctx, task.organizationId);
|
|
571
596
|
let children = [];
|
|
572
597
|
let childIds = [];
|
|
598
|
+
let supportTickets = [];
|
|
599
|
+
const supportTicketIds = [];
|
|
573
600
|
await withAtomicFlush(
|
|
574
601
|
em,
|
|
575
602
|
[
|
|
@@ -578,6 +605,18 @@ const deleteTaskCommand = {
|
|
|
578
605
|
StaffTimeTask,
|
|
579
606
|
applyScopeToWhere({ parentTaskId: task.id, deletedAt: null }, scope)
|
|
580
607
|
);
|
|
608
|
+
supportTickets = await findWithDecryption(
|
|
609
|
+
em,
|
|
610
|
+
StaffSupportTicket,
|
|
611
|
+
{
|
|
612
|
+
taskId: { $in: [task.id, ...children.map((child) => child.id)] },
|
|
613
|
+
tenantId: task.tenantId,
|
|
614
|
+
organizationId: task.organizationId,
|
|
615
|
+
deletedAt: null
|
|
616
|
+
},
|
|
617
|
+
void 0,
|
|
618
|
+
{ tenantId: task.tenantId, organizationId: task.organizationId }
|
|
619
|
+
);
|
|
581
620
|
},
|
|
582
621
|
() => {
|
|
583
622
|
const plan = planTaskSoftDelete(task, children);
|
|
@@ -592,6 +631,13 @@ const deleteTaskCommand = {
|
|
|
592
631
|
child.deletedAt = now;
|
|
593
632
|
child.updatedAt = now;
|
|
594
633
|
}
|
|
634
|
+
const removedTaskIds = /* @__PURE__ */ new Set([task.id, ...childIds]);
|
|
635
|
+
for (const ticket of supportTickets) {
|
|
636
|
+
if (!removedTaskIds.has(ticket.taskId)) continue;
|
|
637
|
+
ticket.deletedAt = now;
|
|
638
|
+
ticket.updatedAt = now;
|
|
639
|
+
supportTicketIds.push(ticket.id);
|
|
640
|
+
}
|
|
595
641
|
}
|
|
596
642
|
],
|
|
597
643
|
{ transaction: true, label: staffTimeTaskCommandIds.delete }
|
|
@@ -607,9 +653,9 @@ const deleteTaskCommand = {
|
|
|
607
653
|
indexer: taskCrudIndexer
|
|
608
654
|
});
|
|
609
655
|
}
|
|
610
|
-
return { taskId: task.id, childIds };
|
|
656
|
+
return { taskId: task.id, childIds, supportTicketIds };
|
|
611
657
|
},
|
|
612
|
-
buildLog: async ({ snapshots }) => {
|
|
658
|
+
buildLog: async ({ snapshots, result }) => {
|
|
613
659
|
const prepared = snapshots.before;
|
|
614
660
|
const before = prepared?.snapshot;
|
|
615
661
|
if (!before) return null;
|
|
@@ -624,7 +670,8 @@ const deleteTaskCommand = {
|
|
|
624
670
|
payload: {
|
|
625
671
|
undo: {
|
|
626
672
|
before,
|
|
627
|
-
cascadedChildren: prepared?.cascadedChildren ?? null
|
|
673
|
+
cascadedChildren: prepared?.cascadedChildren ?? null,
|
|
674
|
+
cascadedSupportTicketIds: result?.supportTicketIds ?? []
|
|
628
675
|
}
|
|
629
676
|
}
|
|
630
677
|
};
|
|
@@ -637,21 +684,48 @@ const deleteTaskCommand = {
|
|
|
637
684
|
const em = ctx.container.resolve("em").fork();
|
|
638
685
|
const ids = [before.id, ...(payload?.cascadedChildren ?? []).map((child) => child.id)];
|
|
639
686
|
const restored = [];
|
|
687
|
+
const restoredTickets = [];
|
|
688
|
+
const ticketIds = payload?.cascadedSupportTicketIds ?? [];
|
|
689
|
+
const { translate } = await resolveTranslations();
|
|
640
690
|
await withAtomicFlush(
|
|
641
691
|
em,
|
|
642
692
|
[
|
|
643
693
|
async () => {
|
|
694
|
+
const project = await lockLiveProjectForWrite(
|
|
695
|
+
em,
|
|
696
|
+
before.timeProjectId,
|
|
697
|
+
explicitStaffCommandScope(before.tenantId, before.organizationId)
|
|
698
|
+
);
|
|
699
|
+
if (!project) throw projectNotFoundError(translate);
|
|
644
700
|
const tasks = await em.find(StaffTimeTask, {
|
|
645
701
|
id: { $in: ids },
|
|
646
702
|
...snapshotScopeWhere(snapshotScope)
|
|
647
703
|
});
|
|
648
704
|
restored.push(...tasks);
|
|
705
|
+
if (ticketIds.length === 0) return;
|
|
706
|
+
const tickets = await findWithDecryption(
|
|
707
|
+
em,
|
|
708
|
+
StaffSupportTicket,
|
|
709
|
+
{
|
|
710
|
+
id: { $in: ticketIds },
|
|
711
|
+
taskId: { $in: ids },
|
|
712
|
+
...snapshotScopeWhere(snapshotScope),
|
|
713
|
+
deletedAt: { $ne: null }
|
|
714
|
+
},
|
|
715
|
+
void 0,
|
|
716
|
+
{ tenantId: before.tenantId, organizationId: before.organizationId }
|
|
717
|
+
);
|
|
718
|
+
restoredTickets.push(...tickets);
|
|
649
719
|
},
|
|
650
720
|
() => {
|
|
651
721
|
for (const task of restored) {
|
|
652
722
|
task.deletedAt = null;
|
|
653
723
|
task.updatedAt = /* @__PURE__ */ new Date();
|
|
654
724
|
}
|
|
725
|
+
for (const ticket of restoredTickets) {
|
|
726
|
+
ticket.deletedAt = null;
|
|
727
|
+
ticket.updatedAt = /* @__PURE__ */ new Date();
|
|
728
|
+
}
|
|
655
729
|
}
|
|
656
730
|
],
|
|
657
731
|
{ transaction: true, label: `${staffTimeTaskCommandIds.delete}.undo` }
|
|
@@ -824,11 +898,19 @@ registerCommand(statusChangeTaskCommand);
|
|
|
824
898
|
export {
|
|
825
899
|
STAFF_TIME_TASK_RESOURCE_KIND,
|
|
826
900
|
TASK_DEPTH_EXCEEDED_CODE,
|
|
901
|
+
TASK_LIST_CACHE_RESOURCE,
|
|
827
902
|
TASK_PARENT_PROJECT_MISMATCH_CODE,
|
|
828
903
|
TASK_PARENT_SELF_CODE,
|
|
829
904
|
TASK_PROJECT_MOVE_UNSUPPORTED_CODE,
|
|
830
905
|
TASK_REFERENCE_CONFLICT_CODE,
|
|
831
906
|
TASK_STATUS_MISSING_CODE,
|
|
832
|
-
|
|
907
|
+
applyStatusToTask,
|
|
908
|
+
emitTaskAssigneeChanged,
|
|
909
|
+
nextTaskPosition,
|
|
910
|
+
readHighestSequenceNumber,
|
|
911
|
+
resolveTaskStatus,
|
|
912
|
+
staffTimeTaskCommandIds,
|
|
913
|
+
taskCrudEvents,
|
|
914
|
+
taskCrudIndexer
|
|
833
915
|
};
|
|
834
916
|
//# sourceMappingURL=timesheets-tasks.js.map
|