@open-mercato/core 0.10.1-develop.7717.1.2befe9c634 → 0.10.1-develop.7722.1.901c72b9e9
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
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
DrawerHeader,
|
|
14
14
|
DrawerTitle,
|
|
15
15
|
} from '@open-mercato/ui/primitives/drawer'
|
|
16
|
+
import { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'
|
|
16
17
|
import { Avatar } from '@open-mercato/ui/primitives/avatar'
|
|
17
18
|
import { Badge } from '@open-mercato/ui/primitives/badge'
|
|
18
19
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
@@ -28,7 +29,11 @@ import {
|
|
|
28
29
|
} from '@open-mercato/ui/primitives/select'
|
|
29
30
|
import { ErrorMessage, LoadingMessage } from '@open-mercato/ui/backend/detail'
|
|
30
31
|
import { apiCall, apiCallOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
|
|
31
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
buildOptimisticLockHeader,
|
|
34
|
+
extractOptimisticLockConflict,
|
|
35
|
+
extractRecordLockConflict,
|
|
36
|
+
} from '@open-mercato/ui/backend/utils/optimisticLock'
|
|
32
37
|
import { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'
|
|
33
38
|
import { useBackendChrome } from '@open-mercato/ui/backend/BackendChromeProvider'
|
|
34
39
|
import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
|
|
@@ -41,7 +46,11 @@ import { hasFeature } from '@open-mercato/shared/security/features'
|
|
|
41
46
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
42
47
|
import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
|
|
43
48
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
44
|
-
import { TaskCommentThread } from './TaskCommentThread'
|
|
49
|
+
import { TaskCommentThread, type CommentVisibility } from './TaskCommentThread'
|
|
50
|
+
import { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'
|
|
51
|
+
import { SupportRequestPanel } from './SupportRequestPanel'
|
|
52
|
+
import { SupportAttachmentsSection } from './SupportAttachmentsSection'
|
|
53
|
+
import { readSupportTicket, SUPPORT_ACTIVITY_EVENT, type SupportTicketView } from './supportTicketData'
|
|
45
54
|
import { TaskQuickLog, type QuickLogSubmission } from './TaskQuickLog'
|
|
46
55
|
import { TagPicker, tagChipStyle } from './TagPicker'
|
|
47
56
|
import { slugifyProjectName } from '../time-tracking/projectCode'
|
|
@@ -70,6 +79,9 @@ import {
|
|
|
70
79
|
toDrawerEntry,
|
|
71
80
|
todayIsoDate,
|
|
72
81
|
buildEntryClocks,
|
|
82
|
+
isAssignmentEventForTask,
|
|
83
|
+
TASK_ASSIGNMENT_CHANGED_EVENT,
|
|
84
|
+
TASK_DRAWER_QUERY_ROOT,
|
|
73
85
|
type DrawerComment,
|
|
74
86
|
type DrawerEntry,
|
|
75
87
|
} from './taskDrawerData'
|
|
@@ -87,11 +99,32 @@ type TaskDetailInjectionContext = {
|
|
|
87
99
|
}
|
|
88
100
|
|
|
89
101
|
export const TASK_DRAWER_MUTATION_CONTEXT_ID = 'staff.time_tracking.task_drawer'
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The drawer's writes carry no `resourceKind`/`resourceId`, so the enterprise
|
|
105
|
+
* record-lock widget leaves them alone: every property saves on its own (the
|
|
106
|
+
* spec's per-field saves), and the `updated_at` version each save sends is what
|
|
107
|
+
* guards concurrent edits. A lock taken per save, never released and never
|
|
108
|
+
* resolvable from here, could only block the user (#8045, #8101). The `formId`
|
|
109
|
+
* is still scoped to the task so no guarded-mutation state crosses tasks.
|
|
110
|
+
*/
|
|
111
|
+
function taskDrawerFormId(taskId: string): string {
|
|
112
|
+
return `${TASK_DRAWER_MUTATION_CONTEXT_ID}:${taskId}`
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const ASSIGNMENT_RACE_CODES = new Set(['task_already_claimed', 'task_assignment_changed'])
|
|
116
|
+
|
|
117
|
+
function isAssignmentRace(error: unknown): boolean {
|
|
118
|
+
if (extractOptimisticLockConflict(error) || extractRecordLockConflict(error)) return true
|
|
119
|
+
if (!error || typeof error !== 'object') return false
|
|
120
|
+
const code = (error as { code?: unknown }).code
|
|
121
|
+
return typeof code === 'string' && ASSIGNMENT_RACE_CODES.has(code)
|
|
122
|
+
}
|
|
90
123
|
export const RATES_FEATURE = 'staff.timesheets.rates.view'
|
|
91
124
|
export const TASKS_MANAGE_FEATURE = 'staff.timesheets.tasks.manage'
|
|
92
125
|
export const ENTRIES_LIST_PATH = '/backend/staff/time-tracking/entries'
|
|
93
126
|
|
|
94
|
-
const DRAWER_QUERY_ROOT =
|
|
127
|
+
const DRAWER_QUERY_ROOT = TASK_DRAWER_QUERY_ROOT
|
|
95
128
|
const DIRECTORY_PAGE_SIZE = 100
|
|
96
129
|
const CHILD_PAGE_SIZE = 100
|
|
97
130
|
const COMMENT_PAGE_SIZE = 50
|
|
@@ -113,7 +146,21 @@ export type TaskDrawerProps = {
|
|
|
113
146
|
onChanged?: () => void
|
|
114
147
|
}
|
|
115
148
|
|
|
116
|
-
type
|
|
149
|
+
type TaskAssignmentAbility = { canClaim: boolean; canRelease: boolean }
|
|
150
|
+
|
|
151
|
+
type LoadedTask = {
|
|
152
|
+
task: BoardTask
|
|
153
|
+
createdAt: string | null
|
|
154
|
+
support: SupportTicketView | null
|
|
155
|
+
assignment: TaskAssignmentAbility
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function readTaskAssignmentAbility(row: BoardApiRow): TaskAssignmentAbility {
|
|
159
|
+
const block = (row as Record<string, unknown>)._staffAssignment
|
|
160
|
+
if (!block || typeof block !== 'object') return { canClaim: false, canRelease: false }
|
|
161
|
+
const ability = block as Record<string, unknown>
|
|
162
|
+
return { canClaim: ability.canClaim === true, canRelease: ability.canRelease === true }
|
|
163
|
+
}
|
|
117
164
|
|
|
118
165
|
type RunningTimer = { entryId: string | null; taskId: string | null; startedAt: string | null }
|
|
119
166
|
|
|
@@ -197,16 +244,22 @@ export function TaskDrawer({
|
|
|
197
244
|
* with the token the first blur just invalidated.
|
|
198
245
|
*/
|
|
199
246
|
const versionRef = React.useRef<string | null>(null)
|
|
247
|
+
const [notice, setNotice] = React.useState<{ status: 'warning' | 'error'; message: string } | null>(null)
|
|
200
248
|
|
|
201
|
-
const { runMutation, retryLastMutation } = useGuardedMutation<{
|
|
249
|
+
const { runMutation: runGuardedMutation, retryLastMutation } = useGuardedMutation<{
|
|
202
250
|
formId: string
|
|
203
|
-
resourceKind: string
|
|
204
|
-
resourceId: string
|
|
205
251
|
retryLastMutation: () => Promise<boolean>
|
|
206
252
|
}>({
|
|
207
253
|
contextId: TASK_DRAWER_MUTATION_CONTEXT_ID,
|
|
208
254
|
blockedMessage: t('staff.time_tracking.taskDrawer.properties.saveError', 'Could not save the change.'),
|
|
209
255
|
})
|
|
256
|
+
const runMutation = React.useCallback<typeof runGuardedMutation>(
|
|
257
|
+
(input) => {
|
|
258
|
+
setNotice(null)
|
|
259
|
+
return runGuardedMutation(input)
|
|
260
|
+
},
|
|
261
|
+
[runGuardedMutation],
|
|
262
|
+
)
|
|
210
263
|
|
|
211
264
|
const taskQuery = useQuery<LoadedTask | null>({
|
|
212
265
|
queryKey: [...DRAWER_QUERY_ROOT, 'task', `scope:${scopeVersion}`, taskId],
|
|
@@ -220,12 +273,20 @@ export function TaskDrawer({
|
|
|
220
273
|
const row = readItems(call.result)[0]
|
|
221
274
|
if (!row) return null
|
|
222
275
|
const parsed = toBoardTask(row)
|
|
223
|
-
return parsed
|
|
276
|
+
return parsed
|
|
277
|
+
? {
|
|
278
|
+
task: parsed,
|
|
279
|
+
createdAt: readString(row, 'created_at', 'createdAt'),
|
|
280
|
+
support: readSupportTicket(row),
|
|
281
|
+
assignment: readTaskAssignmentAbility(row),
|
|
282
|
+
}
|
|
283
|
+
: null
|
|
224
284
|
},
|
|
225
285
|
})
|
|
226
286
|
|
|
227
287
|
const task = taskQuery.data?.task ?? null
|
|
228
288
|
const createdAt = taskQuery.data?.createdAt ?? null
|
|
289
|
+
const support = taskQuery.data?.support ?? null
|
|
229
290
|
const projectId = task?.timeProjectId ?? null
|
|
230
291
|
|
|
231
292
|
const taskInjectionContext = React.useMemo<TaskDetailInjectionContext>(
|
|
@@ -246,6 +307,10 @@ export function TaskDrawer({
|
|
|
246
307
|
setTagIds(task.tagIds)
|
|
247
308
|
}, [task])
|
|
248
309
|
|
|
310
|
+
React.useEffect(() => {
|
|
311
|
+
setNotice(null)
|
|
312
|
+
}, [taskId])
|
|
313
|
+
|
|
249
314
|
const statusesQuery = useQuery<BoardStatus[]>({
|
|
250
315
|
queryKey: [...DRAWER_QUERY_ROOT, 'statuses', `scope:${scopeVersion}`, projectId ?? 'none'],
|
|
251
316
|
enabled: open && !!projectId,
|
|
@@ -435,7 +500,7 @@ export function TaskDrawer({
|
|
|
435
500
|
)
|
|
436
501
|
|
|
437
502
|
const invalidate = React.useCallback(
|
|
438
|
-
(part: 'task' | 'children' | 'entries' | 'comments') => {
|
|
503
|
+
(part: 'task' | 'children' | 'entries' | 'comments' | 'attachments') => {
|
|
439
504
|
queryClient.invalidateQueries({ queryKey: [...DRAWER_QUERY_ROOT, part] }).catch(() => {})
|
|
440
505
|
},
|
|
441
506
|
[queryClient],
|
|
@@ -447,17 +512,36 @@ export function TaskDrawer({
|
|
|
447
512
|
if (updatedAt) versionRef.current = updatedAt
|
|
448
513
|
}, [taskQuery])
|
|
449
514
|
|
|
515
|
+
/**
|
|
516
|
+
* A failed save is reported inside the drawer, which covers the page-level
|
|
517
|
+
* conflict bar. It is a conflict when the server says so, or when the task
|
|
518
|
+
* moved underneath the save (a guard can refuse it before any request leaves);
|
|
519
|
+
* either way the drawer refetches, so the next attempt starts from what it shows.
|
|
520
|
+
*/
|
|
450
521
|
const reportError = React.useCallback(
|
|
451
522
|
(error: unknown, fallback: string) => {
|
|
523
|
+
const conflictNotice = {
|
|
524
|
+
status: 'warning' as const,
|
|
525
|
+
message: t(
|
|
526
|
+
'staff.time_tracking.taskDrawer.conflict',
|
|
527
|
+
'Someone else changed this task. It now shows the latest version. Check it and try again.',
|
|
528
|
+
),
|
|
529
|
+
}
|
|
530
|
+
const seenVersion = versionRef.current
|
|
531
|
+
invalidate('children')
|
|
452
532
|
if (surfaceRecordConflict(error, t)) {
|
|
533
|
+
setNotice(conflictNotice)
|
|
453
534
|
invalidate('task')
|
|
454
|
-
invalidate('children')
|
|
455
535
|
return
|
|
456
536
|
}
|
|
457
537
|
logger.error('staff.time_tracking task drawer mutation failed', { err: error })
|
|
458
|
-
|
|
538
|
+
setNotice({ status: 'error', message: error instanceof Error && error.message ? error.message : fallback })
|
|
539
|
+
void taskQuery.refetch().then((refreshed) => {
|
|
540
|
+
const currentVersion = refreshed.data?.task.updatedAt ?? null
|
|
541
|
+
if (seenVersion && currentVersion && currentVersion !== seenVersion) setNotice(conflictNotice)
|
|
542
|
+
})
|
|
459
543
|
},
|
|
460
|
-
[invalidate, t],
|
|
544
|
+
[invalidate, t, taskQuery],
|
|
461
545
|
)
|
|
462
546
|
|
|
463
547
|
const handleQuickLog = React.useCallback(
|
|
@@ -503,9 +587,7 @@ export function TaskDrawer({
|
|
|
503
587
|
{ errorMessage: t('staff.time_tracking.taskDrawer.quickLog.error', 'Could not save the time entry.') },
|
|
504
588
|
),
|
|
505
589
|
context: {
|
|
506
|
-
formId:
|
|
507
|
-
resourceKind: 'staff.timesheets.time_entry',
|
|
508
|
-
resourceId: taskId,
|
|
590
|
+
formId: taskDrawerFormId(taskId),
|
|
509
591
|
retryLastMutation,
|
|
510
592
|
},
|
|
511
593
|
})
|
|
@@ -534,14 +616,12 @@ export function TaskDrawer({
|
|
|
534
616
|
{ errorMessage: t('staff.time_tracking.board.timer.stopError', 'Could not stop the timer.') },
|
|
535
617
|
),
|
|
536
618
|
context: {
|
|
537
|
-
formId:
|
|
538
|
-
resourceKind: 'staff.timesheets.time_entry',
|
|
539
|
-
resourceId: entryId,
|
|
619
|
+
formId: taskDrawerFormId(taskId),
|
|
540
620
|
retryLastMutation,
|
|
541
621
|
},
|
|
542
622
|
})
|
|
543
623
|
},
|
|
544
|
-
[retryLastMutation, runMutation, t],
|
|
624
|
+
[retryLastMutation, runMutation, t, taskId],
|
|
545
625
|
)
|
|
546
626
|
|
|
547
627
|
const handleStopTimer = React.useCallback(async () => {
|
|
@@ -596,9 +676,7 @@ export function TaskDrawer({
|
|
|
596
676
|
{ errorMessage: t('staff.time_tracking.board.timer.startError', 'Could not start the timer.') },
|
|
597
677
|
),
|
|
598
678
|
context: {
|
|
599
|
-
formId:
|
|
600
|
-
resourceKind: 'staff.timesheets.time_entry',
|
|
601
|
-
resourceId: taskId,
|
|
679
|
+
formId: taskDrawerFormId(taskId),
|
|
602
680
|
retryLastMutation,
|
|
603
681
|
},
|
|
604
682
|
})
|
|
@@ -681,9 +759,7 @@ export function TaskDrawer({
|
|
|
681
759
|
),
|
|
682
760
|
),
|
|
683
761
|
context: {
|
|
684
|
-
formId:
|
|
685
|
-
resourceKind: 'staff.timesheets.task',
|
|
686
|
-
resourceId: taskId,
|
|
762
|
+
formId: taskDrawerFormId(taskId),
|
|
687
763
|
retryLastMutation,
|
|
688
764
|
},
|
|
689
765
|
})
|
|
@@ -721,9 +797,7 @@ export function TaskDrawer({
|
|
|
721
797
|
),
|
|
722
798
|
),
|
|
723
799
|
context: {
|
|
724
|
-
formId:
|
|
725
|
-
resourceKind: 'staff.timesheets.task',
|
|
726
|
-
resourceId: taskId,
|
|
800
|
+
formId: taskDrawerFormId(taskId),
|
|
727
801
|
retryLastMutation,
|
|
728
802
|
},
|
|
729
803
|
})
|
|
@@ -762,6 +836,85 @@ export function TaskDrawer({
|
|
|
762
836
|
[saveTaskField],
|
|
763
837
|
)
|
|
764
838
|
|
|
839
|
+
const canClaim = taskQuery.data?.assignment.canClaim === true
|
|
840
|
+
const canRelease = taskQuery.data?.assignment.canRelease === true
|
|
841
|
+
|
|
842
|
+
const runAssignmentAction = React.useCallback(
|
|
843
|
+
(action: 'claim' | 'release') => {
|
|
844
|
+
if (!task) return
|
|
845
|
+
const errorMessage =
|
|
846
|
+
action === 'claim'
|
|
847
|
+
? t('staff.time_tracking.taskDrawer.assignment.claimError', 'Could not claim the task.')
|
|
848
|
+
: t('staff.time_tracking.taskDrawer.assignment.releaseError', 'Could not release the task.')
|
|
849
|
+
setBusy(true)
|
|
850
|
+
void runSerialized(async () => {
|
|
851
|
+
try {
|
|
852
|
+
// No optimistic-lock header: claim and release are atomic conditional
|
|
853
|
+
// updates on the assignee, so a lost race answers with its own reason
|
|
854
|
+
// instead of a generic "record changed".
|
|
855
|
+
const done = await runMutation({
|
|
856
|
+
operation: () =>
|
|
857
|
+
apiCallOrThrow<Record<string, unknown>>(
|
|
858
|
+
`/api/staff/timesheets/tasks/${encodeURIComponent(taskId)}/${action}`,
|
|
859
|
+
{ method: 'POST' },
|
|
860
|
+
{ errorMessage },
|
|
861
|
+
),
|
|
862
|
+
context: {
|
|
863
|
+
formId: taskDrawerFormId(taskId),
|
|
864
|
+
retryLastMutation,
|
|
865
|
+
},
|
|
866
|
+
})
|
|
867
|
+
const updatedAt = typeof done?.result?.updatedAt === 'string' ? done.result.updatedAt : null
|
|
868
|
+
if (updatedAt) versionRef.current = updatedAt
|
|
869
|
+
else await refreshVersion()
|
|
870
|
+
invalidate('task')
|
|
871
|
+
flash(
|
|
872
|
+
action === 'claim'
|
|
873
|
+
? t('staff.time_tracking.taskDrawer.assignment.claimed', 'Task claimed.')
|
|
874
|
+
: t('staff.time_tracking.taskDrawer.assignment.released', 'Task released.'),
|
|
875
|
+
'success',
|
|
876
|
+
)
|
|
877
|
+
onChanged?.()
|
|
878
|
+
} catch (error) {
|
|
879
|
+
if (!isAssignmentRace(error)) {
|
|
880
|
+
reportError(error, errorMessage)
|
|
881
|
+
return
|
|
882
|
+
}
|
|
883
|
+
const refreshed = await taskQuery.refetch()
|
|
884
|
+
const assigneeId = refreshed.data?.task.assigneeStaffMemberId ?? null
|
|
885
|
+
const takenByOther = action === 'claim' && assigneeId !== null && assigneeId !== selfStaffMemberId
|
|
886
|
+
setNotice({
|
|
887
|
+
status: 'warning',
|
|
888
|
+
message: takenByOther
|
|
889
|
+
? t('staff.timesheets.errors.taskAlreadyClaimed', 'Someone else already took this task.')
|
|
890
|
+
: t(
|
|
891
|
+
'staff.time_tracking.taskDrawer.conflict',
|
|
892
|
+
'Someone else changed this task. It now shows the latest version. Check it and try again.',
|
|
893
|
+
),
|
|
894
|
+
})
|
|
895
|
+
invalidate('task')
|
|
896
|
+
onChanged?.()
|
|
897
|
+
} finally {
|
|
898
|
+
setBusy(false)
|
|
899
|
+
}
|
|
900
|
+
})
|
|
901
|
+
},
|
|
902
|
+
[
|
|
903
|
+
invalidate,
|
|
904
|
+
onChanged,
|
|
905
|
+
refreshVersion,
|
|
906
|
+
reportError,
|
|
907
|
+
retryLastMutation,
|
|
908
|
+
runMutation,
|
|
909
|
+
runSerialized,
|
|
910
|
+
selfStaffMemberId,
|
|
911
|
+
t,
|
|
912
|
+
task,
|
|
913
|
+
taskId,
|
|
914
|
+
taskQuery,
|
|
915
|
+
],
|
|
916
|
+
)
|
|
917
|
+
|
|
765
918
|
const handleToggleSubtask = React.useCallback(
|
|
766
919
|
async (child: BoardTask, done: boolean) => {
|
|
767
920
|
const targetStatusId = done ? resolveDoneStatusId(statuses) : resolveDefaultStatusId(statuses)
|
|
@@ -789,9 +942,7 @@ export function TaskDrawer({
|
|
|
789
942
|
),
|
|
790
943
|
),
|
|
791
944
|
context: {
|
|
792
|
-
formId:
|
|
793
|
-
resourceKind: 'staff.timesheets.task',
|
|
794
|
-
resourceId: child.id,
|
|
945
|
+
formId: taskDrawerFormId(taskId),
|
|
795
946
|
retryLastMutation,
|
|
796
947
|
},
|
|
797
948
|
})
|
|
@@ -807,7 +958,7 @@ export function TaskDrawer({
|
|
|
807
958
|
setBusy(false)
|
|
808
959
|
}
|
|
809
960
|
},
|
|
810
|
-
[invalidate, onChanged, reportError, retryLastMutation, runMutation, statuses, t],
|
|
961
|
+
[invalidate, onChanged, reportError, retryLastMutation, runMutation, statuses, t, taskId],
|
|
811
962
|
)
|
|
812
963
|
|
|
813
964
|
const handleAddSubtask = React.useCallback(async () => {
|
|
@@ -838,9 +989,7 @@ export function TaskDrawer({
|
|
|
838
989
|
},
|
|
839
990
|
),
|
|
840
991
|
context: {
|
|
841
|
-
formId:
|
|
842
|
-
resourceKind: 'staff.timesheets.task',
|
|
843
|
-
resourceId: taskId,
|
|
992
|
+
formId: taskDrawerFormId(taskId),
|
|
844
993
|
retryLastMutation,
|
|
845
994
|
},
|
|
846
995
|
})
|
|
@@ -867,8 +1016,65 @@ export function TaskDrawer({
|
|
|
867
1016
|
taskId,
|
|
868
1017
|
])
|
|
869
1018
|
|
|
1019
|
+
useAppEvent(
|
|
1020
|
+
SUPPORT_ACTIVITY_EVENT,
|
|
1021
|
+
(event) => {
|
|
1022
|
+
const eventTaskId = (event.payload as { taskId?: unknown } | undefined)?.taskId
|
|
1023
|
+
if (!open || eventTaskId !== taskId) return
|
|
1024
|
+
invalidate('task')
|
|
1025
|
+
invalidate('comments')
|
|
1026
|
+
invalidate('attachments')
|
|
1027
|
+
},
|
|
1028
|
+
[invalidate, open, taskId],
|
|
1029
|
+
)
|
|
1030
|
+
|
|
1031
|
+
useAppEvent(
|
|
1032
|
+
TASK_ASSIGNMENT_CHANGED_EVENT,
|
|
1033
|
+
(event) => {
|
|
1034
|
+
if (!open || !isAssignmentEventForTask(event.payload, taskId)) return
|
|
1035
|
+
invalidate('task')
|
|
1036
|
+
},
|
|
1037
|
+
[invalidate, open, taskId],
|
|
1038
|
+
)
|
|
1039
|
+
|
|
1040
|
+
const handleMakeInternal = React.useCallback(
|
|
1041
|
+
async (comment: DrawerComment): Promise<void> => {
|
|
1042
|
+
setBusy(true)
|
|
1043
|
+
try {
|
|
1044
|
+
await runMutation({
|
|
1045
|
+
operation: () =>
|
|
1046
|
+
withScopedApiRequestHeaders(buildOptimisticLockHeader(comment.updatedAt), () =>
|
|
1047
|
+
apiCallOrThrow(
|
|
1048
|
+
`/api/staff/timesheets/tasks/${encodeURIComponent(taskId)}/comments`,
|
|
1049
|
+
{
|
|
1050
|
+
method: 'PUT',
|
|
1051
|
+
headers: { 'content-type': 'application/json' },
|
|
1052
|
+
body: JSON.stringify({ id: comment.id, visibility: 'internal' }),
|
|
1053
|
+
},
|
|
1054
|
+
{ errorMessage: t('staff.time_tracking.taskComments.makeInternalError', 'Could not change the reply.') },
|
|
1055
|
+
),
|
|
1056
|
+
),
|
|
1057
|
+
context: {
|
|
1058
|
+
formId: taskDrawerFormId(taskId),
|
|
1059
|
+
retryLastMutation,
|
|
1060
|
+
},
|
|
1061
|
+
})
|
|
1062
|
+
invalidate('comments')
|
|
1063
|
+
} catch (error) {
|
|
1064
|
+
if (surfaceRecordConflict(error, t)) {
|
|
1065
|
+
invalidate('comments')
|
|
1066
|
+
return
|
|
1067
|
+
}
|
|
1068
|
+
reportError(error, t('staff.time_tracking.taskComments.makeInternalError', 'Could not change the reply.'))
|
|
1069
|
+
} finally {
|
|
1070
|
+
setBusy(false)
|
|
1071
|
+
}
|
|
1072
|
+
},
|
|
1073
|
+
[invalidate, reportError, retryLastMutation, runMutation, t, taskId],
|
|
1074
|
+
)
|
|
1075
|
+
|
|
870
1076
|
const handlePostComment = React.useCallback(
|
|
871
|
-
async (body: string): Promise<boolean> => {
|
|
1077
|
+
async (body: string, visibility: CommentVisibility = 'internal'): Promise<boolean> => {
|
|
872
1078
|
setBusy(true)
|
|
873
1079
|
try {
|
|
874
1080
|
await runMutation({
|
|
@@ -878,14 +1084,12 @@ export function TaskDrawer({
|
|
|
878
1084
|
{
|
|
879
1085
|
method: 'POST',
|
|
880
1086
|
headers: { 'content-type': 'application/json' },
|
|
881
|
-
body: JSON.stringify({ body }),
|
|
1087
|
+
body: JSON.stringify({ body, visibility }),
|
|
882
1088
|
},
|
|
883
1089
|
{ errorMessage: t('staff.time_tracking.taskDrawer.comments.error', 'Could not post the comment.') },
|
|
884
1090
|
),
|
|
885
1091
|
context: {
|
|
886
|
-
formId:
|
|
887
|
-
resourceKind: 'staff.timesheets.task_comment',
|
|
888
|
-
resourceId: taskId,
|
|
1092
|
+
formId: taskDrawerFormId(taskId),
|
|
889
1093
|
retryLastMutation,
|
|
890
1094
|
},
|
|
891
1095
|
})
|
|
@@ -921,9 +1125,7 @@ export function TaskDrawer({
|
|
|
921
1125
|
{ errorMessage: t('staff.time_tracking.taskDrawer.properties.tagError', 'Could not save the tags.') },
|
|
922
1126
|
),
|
|
923
1127
|
context: {
|
|
924
|
-
formId:
|
|
925
|
-
resourceKind: 'staff.timesheets.task_tag',
|
|
926
|
-
resourceId: taskId,
|
|
1128
|
+
formId: taskDrawerFormId(taskId),
|
|
927
1129
|
retryLastMutation,
|
|
928
1130
|
},
|
|
929
1131
|
})
|
|
@@ -1012,6 +1214,13 @@ export function TaskDrawer({
|
|
|
1012
1214
|
|
|
1013
1215
|
return (
|
|
1014
1216
|
<div className="flex flex-col gap-6">
|
|
1217
|
+
{notice ? (
|
|
1218
|
+
<Alert status={notice.status} data-testid="task-drawer-notice">
|
|
1219
|
+
<AlertDescription>{notice.message}</AlertDescription>
|
|
1220
|
+
</Alert>
|
|
1221
|
+
) : null}
|
|
1222
|
+
{support ? <SupportRequestPanel ticket={support} /> : null}
|
|
1223
|
+
{support ? <SupportAttachmentsSection taskId={taskId} /> : null}
|
|
1015
1224
|
<TaskQuickLog
|
|
1016
1225
|
onLog={handleQuickLog}
|
|
1017
1226
|
logging={busy}
|
|
@@ -1221,6 +1430,8 @@ export function TaskDrawer({
|
|
|
1221
1430
|
posting={busy}
|
|
1222
1431
|
canComment={canManage}
|
|
1223
1432
|
onPost={handlePostComment}
|
|
1433
|
+
mode={support ? 'support' : 'internal'}
|
|
1434
|
+
onMakeInternal={support ? handleMakeInternal : undefined}
|
|
1224
1435
|
/>
|
|
1225
1436
|
|
|
1226
1437
|
<section className="flex flex-col gap-3" data-testid="task-drawer-properties">
|
|
@@ -1277,6 +1488,30 @@ export function TaskDrawer({
|
|
|
1277
1488
|
))}
|
|
1278
1489
|
</SelectContent>
|
|
1279
1490
|
</Select>
|
|
1491
|
+
{canClaim ? (
|
|
1492
|
+
<Button
|
|
1493
|
+
type="button"
|
|
1494
|
+
size="sm"
|
|
1495
|
+
variant="outline"
|
|
1496
|
+
disabled={busy}
|
|
1497
|
+
onClick={() => runAssignmentAction('claim')}
|
|
1498
|
+
data-testid="task-drawer-claim"
|
|
1499
|
+
>
|
|
1500
|
+
{t('staff.time_tracking.taskDrawer.assignment.claim', 'Claim')}
|
|
1501
|
+
</Button>
|
|
1502
|
+
) : null}
|
|
1503
|
+
{canRelease ? (
|
|
1504
|
+
<Button
|
|
1505
|
+
type="button"
|
|
1506
|
+
size="sm"
|
|
1507
|
+
variant="ghost"
|
|
1508
|
+
disabled={busy}
|
|
1509
|
+
onClick={() => runAssignmentAction('release')}
|
|
1510
|
+
data-testid="task-drawer-release"
|
|
1511
|
+
>
|
|
1512
|
+
{t('staff.time_tracking.taskDrawer.assignment.release', 'Release')}
|
|
1513
|
+
</Button>
|
|
1514
|
+
) : null}
|
|
1280
1515
|
</div>
|
|
1281
1516
|
</div>
|
|
1282
1517
|
|
|
@@ -229,3 +229,24 @@ export function useStaffMemberDirectory(enabled: boolean) {
|
|
|
229
229
|
},
|
|
230
230
|
})
|
|
231
231
|
}
|
|
232
|
+
|
|
233
|
+
export function boardUnassignedCountQueryKey(scopeVersion: number, timeProjectId: string) {
|
|
234
|
+
return [...BOARD_QUERY_ROOT, 'unassigned-count', `scope:${scopeVersion}`, `project:${timeProjectId}`]
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** How many top-level tasks in the project have no assignee — the claimable work the chip counts. */
|
|
238
|
+
export function useUnassignedTaskCount(timeProjectId: string | null | undefined) {
|
|
239
|
+
const scopeVersion = useOrganizationScopeVersion()
|
|
240
|
+
return useQuery<number>({
|
|
241
|
+
queryKey: boardUnassignedCountQueryKey(scopeVersion, timeProjectId ?? ''),
|
|
242
|
+
enabled: !!timeProjectId,
|
|
243
|
+
staleTime: 15_000,
|
|
244
|
+
queryFn: async () => {
|
|
245
|
+
const call = await apiCall<Record<string, unknown>>(
|
|
246
|
+
`/api/staff/timesheets/tasks?timeProjectId=${encodeURIComponent(timeProjectId ?? '')}&unassigned=true&topLevelOnly=true&page=1&pageSize=1`,
|
|
247
|
+
)
|
|
248
|
+
if (!call.ok) throw new Error('[internal] unassigned task count request failed')
|
|
249
|
+
return readTotal(call.result, 0)
|
|
250
|
+
},
|
|
251
|
+
})
|
|
252
|
+
}
|
|
@@ -25,12 +25,15 @@ export type BoardFilters = {
|
|
|
25
25
|
assigneeStaffMemberId: string | null
|
|
26
26
|
taskStatusId: string | null
|
|
27
27
|
tagIds: string[]
|
|
28
|
+
/** Only tasks with no assignee — the claimable work. Mutually exclusive with the assignee filter. */
|
|
29
|
+
unassigned: boolean
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
export const EMPTY_BOARD_FILTERS: BoardFilters = {
|
|
31
33
|
assigneeStaffMemberId: null,
|
|
32
34
|
taskStatusId: null,
|
|
33
35
|
tagIds: [],
|
|
36
|
+
unassigned: false,
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
function readOptionalId(value: unknown): string | null {
|
|
@@ -50,16 +53,19 @@ export function normalizeBoardFilters(raw: unknown): BoardFilters {
|
|
|
50
53
|
),
|
|
51
54
|
)
|
|
52
55
|
: []
|
|
56
|
+
const assigneeStaffMemberId = readOptionalId(source.assigneeStaffMemberId)
|
|
53
57
|
return {
|
|
54
|
-
assigneeStaffMemberId
|
|
58
|
+
assigneeStaffMemberId,
|
|
55
59
|
taskStatusId: readOptionalId(source.taskStatusId),
|
|
56
60
|
tagIds,
|
|
61
|
+
unassigned: assigneeStaffMemberId === null && source.unassigned === true,
|
|
57
62
|
}
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
export function countActiveBoardFilters(filters: BoardFilters): number {
|
|
61
66
|
let count = 0
|
|
62
67
|
if (filters.assigneeStaffMemberId) count += 1
|
|
68
|
+
if (filters.unassigned) count += 1
|
|
63
69
|
if (filters.taskStatusId) count += 1
|
|
64
70
|
return count + filters.tagIds.length
|
|
65
71
|
}
|
|
@@ -80,6 +86,7 @@ export type BoardFilterScope = {
|
|
|
80
86
|
*/
|
|
81
87
|
export function boardServerFilterKey(filters: BoardFilters, scope: BoardFilterScope = {}): string {
|
|
82
88
|
const parts = [`assignee:${filters.assigneeStaffMemberId ?? ''}`]
|
|
89
|
+
if (filters.unassigned) parts.push('unassigned')
|
|
83
90
|
if (scope.includeStatus) parts.push(`status:${filters.taskStatusId ?? ''}`)
|
|
84
91
|
parts.push(`tags:${[...filters.tagIds].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)).join(',')}`)
|
|
85
92
|
return parts.join('|')
|
|
@@ -90,6 +97,8 @@ export function boardServerFilterParams(filters: BoardFilters, scope: BoardFilte
|
|
|
90
97
|
let params = ''
|
|
91
98
|
if (filters.assigneeStaffMemberId) {
|
|
92
99
|
params += `&assigneeStaffMemberId=${encodeURIComponent(filters.assigneeStaffMemberId)}`
|
|
100
|
+
} else if (filters.unassigned) {
|
|
101
|
+
params += '&unassigned=true'
|
|
93
102
|
}
|
|
94
103
|
if (scope.includeStatus && filters.taskStatusId) {
|
|
95
104
|
params += `&taskStatusId=${encodeURIComponent(filters.taskStatusId)}`
|
|
@@ -110,7 +119,12 @@ export function visibleBoardStatusIds<T extends { id: string }>(
|
|
|
110
119
|
}
|
|
111
120
|
|
|
112
121
|
export function withAssigneeFilter(filters: BoardFilters, staffMemberId: string | null): BoardFilters {
|
|
113
|
-
|
|
122
|
+
const assigneeStaffMemberId = readOptionalId(staffMemberId)
|
|
123
|
+
return { ...filters, assigneeStaffMemberId, unassigned: assigneeStaffMemberId ? false : filters.unassigned }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function withUnassignedFilter(filters: BoardFilters, unassigned: boolean): BoardFilters {
|
|
127
|
+
return { ...filters, unassigned, assigneeStaffMemberId: unassigned ? null : filters.assigneeStaffMemberId }
|
|
114
128
|
}
|
|
115
129
|
|
|
116
130
|
export function withStatusFilter(filters: BoardFilters, taskStatusId: string | null): BoardFilters {
|
|
@@ -16,6 +16,7 @@ import { formatDuration } from '../time-tracking/duration'
|
|
|
16
16
|
import { sumTaskLoggedMinutes } from '../timesheets-tasks/taskHoursTotals'
|
|
17
17
|
import { STATUS_POSITION_GAP } from '../timesheets-tasks/statusPositions'
|
|
18
18
|
import { resolveProjectColorHex } from '../timesheets-ui/colors'
|
|
19
|
+
import { readSupportTicket } from './supportTicketData'
|
|
19
20
|
|
|
20
21
|
export type BoardApiRow = Record<string, unknown>
|
|
21
22
|
|
|
@@ -44,6 +45,8 @@ export type BoardTask = {
|
|
|
44
45
|
closedAt: string | null
|
|
45
46
|
updatedAt: string | null
|
|
46
47
|
tagIds: string[]
|
|
48
|
+
/** Set when the task is a customer portal support ticket. */
|
|
49
|
+
supportReference?: string | null
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
export type SubtaskProgress = {
|
|
@@ -123,6 +126,7 @@ export function toBoardTask(row: BoardApiRow): BoardTask | null {
|
|
|
123
126
|
closedAt: readString(row, 'closed_at', 'closedAt'),
|
|
124
127
|
updatedAt: readString(row, 'updated_at', 'updatedAt'),
|
|
125
128
|
tagIds: readStringArray(row, 'tagIds', 'tag_ids'),
|
|
129
|
+
supportReference: readSupportTicket(row)?.supportReference ?? null,
|
|
126
130
|
}
|
|
127
131
|
}
|
|
128
132
|
|