@open-mercato/core 0.10.1-develop.7716.1.cefe7975df → 0.10.1-develop.7721.1.b5c4a00a68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +2 -2
- package/dist/generated/entities/staff_support_routing_rule/index.js +21 -0
- package/dist/generated/entities/staff_support_routing_rule/index.js.map +7 -0
- package/dist/generated/entities/staff_support_ticket/index.js +37 -0
- package/dist/generated/entities/staff_support_ticket/index.js.map +7 -0
- package/dist/generated/entities/staff_time_project/index.js +2 -0
- package/dist/generated/entities/staff_time_project/index.js.map +2 -2
- package/dist/generated/entities/staff_time_project_member/index.js +2 -0
- package/dist/generated/entities/staff_time_project_member/index.js.map +2 -2
- package/dist/generated/entities/staff_time_task/index.js +4 -0
- package/dist/generated/entities/staff_time_task/index.js.map +2 -2
- package/dist/generated/entities/staff_time_task_comment/index.js +9 -1
- package/dist/generated/entities/staff_time_task_comment/index.js.map +2 -2
- package/dist/generated/entities.ids.generated.js +2 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +38 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/auth.js +1 -0
- package/dist/helpers/integration/auth.js.map +2 -2
- package/dist/helpers/integration/communicationChannelsFixtures.js +20 -0
- package/dist/helpers/integration/communicationChannelsFixtures.js.map +2 -2
- package/dist/modules/catalog/api/bulk-delete/route.js +12 -2
- package/dist/modules/catalog/api/bulk-delete/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/test-seed/route.js +17 -3
- package/dist/modules/communication_channels/api/post/test-seed/route.js.map +2 -2
- package/dist/modules/communication_channels/lib/test-seed.js +38 -5
- package/dist/modules/communication_channels/lib/test-seed.js.map +2 -2
- package/dist/modules/messages/commands/messages.js +2 -3
- package/dist/modules/messages/commands/messages.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js.map +2 -2
- package/dist/modules/staff/api/portal/support-tickets/[id]/attachments/route.js +299 -0
- package/dist/modules/staff/api/portal/support-tickets/[id]/attachments/route.js.map +7 -0
- package/dist/modules/staff/api/portal/support-tickets/[id]/messages/route.js +91 -0
- package/dist/modules/staff/api/portal/support-tickets/[id]/messages/route.js.map +7 -0
- package/dist/modules/staff/api/portal/support-tickets/[id]/route.js +146 -0
- package/dist/modules/staff/api/portal/support-tickets/[id]/route.js.map +7 -0
- package/dist/modules/staff/api/portal/support-tickets/options/route.js +60 -0
- package/dist/modules/staff/api/portal/support-tickets/options/route.js.map +7 -0
- package/dist/modules/staff/api/portal/support-tickets/route.js +220 -0
- package/dist/modules/staff/api/portal/support-tickets/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/settings/route.js +7 -1
- package/dist/modules/staff/api/timesheets/settings/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/support-routing-rules/fallback-project/route.js +122 -0
- package/dist/modules/staff/api/timesheets/support-routing-rules/fallback-project/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/support-routing-rules/preview/route.js +180 -0
- package/dist/modules/staff/api/timesheets/support-routing-rules/preview/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/support-routing-rules/route.js +147 -0
- package/dist/modules/staff/api/timesheets/support-routing-rules/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/support-tickets/options/route.js +133 -0
- package/dist/modules/staff/api/timesheets/support-tickets/options/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tasks/[id]/claim/route.js +33 -0
- package/dist/modules/staff/api/timesheets/tasks/[id]/claim/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tasks/[id]/comments/route.js +25 -7
- package/dist/modules/staff/api/timesheets/tasks/[id]/comments/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/tasks/[id]/release/route.js +32 -0
- package/dist/modules/staff/api/timesheets/tasks/[id]/release/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tasks/route.js +94 -47
- package/dist/modules/staff/api/timesheets/tasks/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js +3 -0
- package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-projects/route.js +3 -0
- package/dist/modules/staff/api/timesheets/time-projects/route.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.js +1 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js +54 -4
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.js +21 -5
- package/dist/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/settings/page.js +319 -254
- package/dist/modules/staff/backend/staff/time-tracking/settings/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/support/page.js +363 -0
- package/dist/modules/staff/backend/staff/time-tracking/support/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/support/page.meta.js +18 -0
- package/dist/modules/staff/backend/staff/time-tracking/support/page.meta.js.map +7 -0
- package/dist/modules/staff/cli.js +27 -0
- package/dist/modules/staff/cli.js.map +2 -2
- package/dist/modules/staff/commands/index.js +3 -0
- package/dist/modules/staff/commands/index.js.map +2 -2
- package/dist/modules/staff/commands/shared.js +10 -1
- package/dist/modules/staff/commands/shared.js.map +2 -2
- package/dist/modules/staff/commands/support-routing-rules.js +505 -0
- package/dist/modules/staff/commands/support-routing-rules.js.map +7 -0
- package/dist/modules/staff/commands/support-tickets.js +513 -0
- package/dist/modules/staff/commands/support-tickets.js.map +7 -0
- package/dist/modules/staff/commands/timesheets-projects.js +72 -14
- package/dist/modules/staff/commands/timesheets-projects.js.map +2 -2
- package/dist/modules/staff/commands/timesheets-task-assignment.js +270 -0
- package/dist/modules/staff/commands/timesheets-task-assignment.js.map +7 -0
- package/dist/modules/staff/commands/timesheets-task-comments.js +58 -12
- package/dist/modules/staff/commands/timesheets-task-comments.js.map +2 -2
- package/dist/modules/staff/commands/timesheets-tasks.js +91 -9
- package/dist/modules/staff/commands/timesheets-tasks.js.map +3 -3
- package/dist/modules/staff/data/enrichers.js +141 -0
- package/dist/modules/staff/data/enrichers.js.map +2 -2
- package/dist/modules/staff/data/entities.js +143 -2
- package/dist/modules/staff/data/entities.js.map +2 -2
- package/dist/modules/staff/data/extensions.js +8 -0
- package/dist/modules/staff/data/extensions.js.map +2 -2
- package/dist/modules/staff/data/validators.js +34 -5
- package/dist/modules/staff/data/validators.js.map +2 -2
- package/dist/modules/staff/encryption.js +6 -0
- package/dist/modules/staff/encryption.js.map +2 -2
- package/dist/modules/staff/events.js +25 -0
- package/dist/modules/staff/events.js.map +2 -2
- package/dist/modules/staff/events.payloads.js +54 -0
- package/dist/modules/staff/events.payloads.js.map +2 -2
- package/dist/modules/staff/extension-points.js +1 -0
- package/dist/modules/staff/extension-points.js.map +2 -2
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.js +468 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.meta.js +12 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.meta.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/new/page.js +316 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/new/page.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/new/page.meta.js +12 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/new/page.meta.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/page.js +190 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/page.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/page.meta.js +19 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/page.meta.js.map +7 -0
- package/dist/modules/staff/index.js +1 -1
- package/dist/modules/staff/index.js.map +2 -2
- package/dist/modules/staff/lib/crud.js +2 -0
- package/dist/modules/staff/lib/crud.js.map +2 -2
- package/dist/modules/staff/lib/support/attachments.js +36 -0
- package/dist/modules/staff/lib/support/attachments.js.map +7 -0
- package/dist/modules/staff/lib/support/dictionaries.js +87 -0
- package/dist/modules/staff/lib/support/dictionaries.js.map +7 -0
- package/dist/modules/staff/lib/support/notifications.js +111 -0
- package/dist/modules/staff/lib/support/notifications.js.map +7 -0
- package/dist/modules/staff/lib/support/portalAttachments.js +100 -0
- package/dist/modules/staff/lib/support/portalAttachments.js.map +7 -0
- package/dist/modules/staff/lib/support/portalBroadcast.js +117 -0
- package/dist/modules/staff/lib/support/portalBroadcast.js.map +7 -0
- package/dist/modules/staff/lib/support/portalRoute.js +99 -0
- package/dist/modules/staff/lib/support/portalRoute.js.map +7 -0
- package/dist/modules/staff/lib/support/portalStatus.js +12 -0
- package/dist/modules/staff/lib/support/portalStatus.js.map +7 -0
- package/dist/modules/staff/lib/support/portalTickets.js +148 -0
- package/dist/modules/staff/lib/support/portalTickets.js.map +7 -0
- package/dist/modules/staff/lib/support/routing.js +81 -0
- package/dist/modules/staff/lib/support/routing.js.map +7 -0
- package/dist/modules/staff/lib/support/routingRuleDraft.js +25 -0
- package/dist/modules/staff/lib/support/routingRuleDraft.js.map +7 -0
- package/dist/modules/staff/lib/support/seedDefaults.js +114 -0
- package/dist/modules/staff/lib/support/seedDefaults.js.map +7 -0
- package/dist/modules/staff/lib/support/supportActivity.js +26 -0
- package/dist/modules/staff/lib/support/supportActivity.js.map +7 -0
- package/dist/modules/staff/lib/support-ui/PortalSupportNotLinked.js +23 -0
- package/dist/modules/staff/lib/support-ui/PortalSupportNotLinked.js.map +7 -0
- package/dist/modules/staff/lib/support-ui/portalAttachmentValidation.js +112 -0
- package/dist/modules/staff/lib/support-ui/portalAttachmentValidation.js.map +7 -0
- package/dist/modules/staff/lib/support-ui/portalPendingUploads.js +45 -0
- package/dist/modules/staff/lib/support-ui/portalPendingUploads.js.map +7 -0
- package/dist/modules/staff/lib/support-ui/portalSupport.js +51 -0
- package/dist/modules/staff/lib/support-ui/portalSupport.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/access.js +3 -39
- package/dist/modules/staff/lib/time-tracking/access.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking/listProjectAccess.js +54 -0
- package/dist/modules/staff/lib/time-tracking/listProjectAccess.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/portalRecipients.js +92 -2
- package/dist/modules/staff/lib/time-tracking/portalRecipients.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking/projectAssignment.js +112 -0
- package/dist/modules/staff/lib/time-tracking/projectAssignment.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/settingKeys.js +14 -0
- package/dist/modules/staff/lib/time-tracking/settingKeys.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking/settings.js +28 -1
- package/dist/modules/staff/lib/time-tracking/settings.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking/taskAssignmentRoute.js +143 -0
- package/dist/modules/staff/lib/time-tracking/taskAssignmentRoute.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/workRecipients.js +92 -0
- package/dist/modules/staff/lib/time-tracking/workRecipients.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/BoardFilterChips.js +32 -3
- package/dist/modules/staff/lib/time-tracking-ui/BoardFilterChips.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/KanbanBoard.js +29 -0
- package/dist/modules/staff/lib/time-tracking-ui/KanbanBoard.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/KanbanCard.js +4 -0
- package/dist/modules/staff/lib/time-tracking-ui/KanbanCard.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/ProjectFormSections.js +141 -1
- package/dist/modules/staff/lib/time-tracking-ui/ProjectFormSections.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/SupportAttachmentsSection.js +153 -0
- package/dist/modules/staff/lib/time-tracking-ui/SupportAttachmentsSection.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/SupportRequestPanel.js +67 -0
- package/dist/modules/staff/lib/time-tracking-ui/SupportRequestPanel.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskBoardScreen.js +3 -1
- package/dist/modules/staff/lib/time-tracking-ui/TaskBoardScreen.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/TaskCommentThread.js +80 -17
- package/dist/modules/staff/lib/time-tracking-ui/TaskCommentThread.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/TaskDrawer.js +230 -41
- package/dist/modules/staff/lib/time-tracking-ui/TaskDrawer.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/boardDirectory.js +21 -1
- package/dist/modules/staff/lib/time-tracking-ui/boardDirectory.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/boardFilters.js +16 -4
- package/dist/modules/staff/lib/time-tracking-ui/boardFilters.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/kanbanBoardData.js +3 -1
- package/dist/modules/staff/lib/time-tracking-ui/kanbanBoardData.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/projectDefaultAssignee.js +68 -0
- package/dist/modules/staff/lib/time-tracking-ui/projectDefaultAssignee.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/settingsTabs.js +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/settingsTabs.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/supportTicketData.js +50 -0
- package/dist/modules/staff/lib/time-tracking-ui/supportTicketData.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/taskDrawerData.js +13 -1
- package/dist/modules/staff/lib/time-tracking-ui/taskDrawerData.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.js +36 -2
- package/dist/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.js.map +2 -2
- package/dist/modules/staff/migrations/Migration20261009180543_staff.js +42 -0
- package/dist/modules/staff/migrations/Migration20261009180543_staff.js.map +7 -0
- package/dist/modules/staff/notifications.js +92 -0
- package/dist/modules/staff/notifications.js.map +2 -2
- package/dist/modules/staff/setup.js +6 -2
- package/dist/modules/staff/setup.js.map +2 -2
- package/dist/modules/staff/subscribers/support-portal-comment-created.js +26 -0
- package/dist/modules/staff/subscribers/support-portal-comment-created.js.map +7 -0
- package/dist/modules/staff/subscribers/support-portal-comment-deleted.js +26 -0
- package/dist/modules/staff/subscribers/support-portal-comment-deleted.js.map +7 -0
- package/dist/modules/staff/subscribers/support-portal-comment-updated.js +26 -0
- package/dist/modules/staff/subscribers/support-portal-comment-updated.js.map +7 -0
- package/dist/modules/staff/subscribers/support-portal-status-column-updated.js +24 -0
- package/dist/modules/staff/subscribers/support-portal-status-column-updated.js.map +7 -0
- package/dist/modules/staff/subscribers/support-portal-task-status-changed.js +25 -0
- package/dist/modules/staff/subscribers/support-portal-task-status-changed.js.map +7 -0
- package/dist/modules/staff/subscribers/support-portal-task-updated.js +25 -0
- package/dist/modules/staff/subscribers/support-portal-task-updated.js.map +7 -0
- package/dist/modules/staff/subscribers/support-ticket-created-notification.js +25 -0
- package/dist/modules/staff/subscribers/support-ticket-created-notification.js.map +7 -0
- package/dist/modules/staff/subscribers/support-ticket-customer-replied-notification.js +25 -0
- package/dist/modules/staff/subscribers/support-ticket-customer-replied-notification.js.map +7 -0
- package/dist/modules/staff/subscribers/support-ticket-unnotified-clear.js +26 -0
- package/dist/modules/staff/subscribers/support-ticket-unnotified-clear.js.map +7 -0
- package/dist/modules/staff/subscribers/time-task-assignee-notification.js +83 -0
- package/dist/modules/staff/subscribers/time-task-assignee-notification.js.map +7 -0
- package/dist/modules/staff/widgets/injection/portal-support-tickets/widget.client.js +112 -0
- package/dist/modules/staff/widgets/injection/portal-support-tickets/widget.client.js.map +7 -0
- package/dist/modules/staff/widgets/injection/portal-support-tickets/widget.js +17 -0
- package/dist/modules/staff/widgets/injection/portal-support-tickets/widget.js.map +7 -0
- package/dist/modules/staff/widgets/injection/time-tracking-settings-support/widget.client.js +870 -0
- package/dist/modules/staff/widgets/injection/time-tracking-settings-support/widget.client.js.map +7 -0
- package/dist/modules/staff/widgets/injection/time-tracking-settings-support/widget.js +17 -0
- package/dist/modules/staff/widgets/injection/time-tracking-settings-support/widget.js.map +7 -0
- package/dist/modules/staff/widgets/injection-table.js +16 -1
- package/dist/modules/staff/widgets/injection-table.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsLocationDetailPage.js +4 -18
- package/dist/modules/wms/components/backend/WmsLocationDetailPage.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsLotDetailPage.js +4 -18
- package/dist/modules/wms/components/backend/WmsLotDetailPage.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsSkuDetailPage.js +4 -18
- package/dist/modules/wms/components/backend/WmsSkuDetailPage.js.map +2 -2
- package/dist/modules/wms/lib/inventoryCsvExport.js +19 -0
- package/dist/modules/wms/lib/inventoryCsvExport.js.map +7 -0
- package/generated/entities/staff_support_routing_rule/index.ts +9 -0
- package/generated/entities/staff_support_ticket/index.ts +17 -0
- package/generated/entities/staff_time_project/index.ts +1 -0
- package/generated/entities/staff_time_project_member/index.ts +1 -0
- package/generated/entities/staff_time_task/index.ts +2 -0
- package/generated/entities/staff_time_task_comment/index.ts +4 -0
- package/generated/entities.ids.generated.ts +2 -0
- package/generated/entity-fields-registry.ts +38 -0
- package/package.json +8 -8
- package/src/helpers/integration/auth.ts +1 -1
- package/src/helpers/integration/communicationChannelsFixtures.ts +60 -2
- package/src/modules/catalog/api/bulk-delete/route.ts +12 -2
- package/src/modules/communication_channels/api/post/test-seed/route.ts +29 -12
- package/src/modules/communication_channels/lib/test-seed.ts +49 -10
- package/src/modules/messages/commands/messages.ts +8 -10
- package/src/modules/portal/frontend/[orgSlug]/portal/dashboard/page.tsx +2 -2
- package/src/modules/staff/AGENTS.md +22 -6
- package/src/modules/staff/api/portal/support-tickets/[id]/attachments/route.ts +327 -0
- package/src/modules/staff/api/portal/support-tickets/[id]/messages/route.ts +94 -0
- package/src/modules/staff/api/portal/support-tickets/[id]/route.ts +160 -0
- package/src/modules/staff/api/portal/support-tickets/options/route.ts +60 -0
- package/src/modules/staff/api/portal/support-tickets/route.ts +253 -0
- package/src/modules/staff/api/timesheets/settings/route.ts +7 -1
- package/src/modules/staff/api/timesheets/support-routing-rules/fallback-project/route.ts +131 -0
- package/src/modules/staff/api/timesheets/support-routing-rules/preview/route.ts +248 -0
- package/src/modules/staff/api/timesheets/support-routing-rules/route.ts +150 -0
- package/src/modules/staff/api/timesheets/support-tickets/options/route.ts +181 -0
- package/src/modules/staff/api/timesheets/tasks/[id]/claim/route.ts +31 -0
- package/src/modules/staff/api/timesheets/tasks/[id]/comments/route.ts +25 -7
- package/src/modules/staff/api/timesheets/tasks/[id]/release/route.ts +30 -0
- package/src/modules/staff/api/timesheets/tasks/route.ts +119 -67
- package/src/modules/staff/api/timesheets/time-projects/[id]/employees/route.ts +3 -0
- package/src/modules/staff/api/timesheets/time-projects/route.ts +3 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.tsx +1 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx +67 -2
- package/src/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.ts +22 -3
- package/src/modules/staff/backend/staff/time-tracking/settings/page.tsx +405 -308
- package/src/modules/staff/backend/staff/time-tracking/support/page.meta.ts +14 -0
- package/src/modules/staff/backend/staff/time-tracking/support/page.tsx +428 -0
- package/src/modules/staff/cli.ts +28 -0
- package/src/modules/staff/commands/index.ts +3 -0
- package/src/modules/staff/commands/shared.ts +22 -1
- package/src/modules/staff/commands/support-routing-rules.ts +595 -0
- package/src/modules/staff/commands/support-tickets.ts +580 -0
- package/src/modules/staff/commands/timesheets-projects.ts +84 -13
- package/src/modules/staff/commands/timesheets-task-assignment.ts +319 -0
- package/src/modules/staff/commands/timesheets-task-comments.ts +70 -15
- package/src/modules/staff/commands/timesheets-tasks.ts +106 -15
- package/src/modules/staff/data/enrichers.ts +203 -0
- package/src/modules/staff/data/entities.ts +133 -2
- package/src/modules/staff/data/extensions.ts +9 -1
- package/src/modules/staff/data/validators.ts +36 -0
- package/src/modules/staff/encryption.ts +6 -0
- package/src/modules/staff/events.payloads.ts +87 -1
- package/src/modules/staff/events.ts +26 -0
- package/src/modules/staff/extension-points.ts +1 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.meta.ts +10 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.tsx +553 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/support/new/page.meta.ts +10 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/support/new/page.tsx +346 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/support/page.meta.ts +17 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/support/page.tsx +253 -0
- package/src/modules/staff/i18n/de.json +291 -0
- package/src/modules/staff/i18n/en.json +291 -0
- package/src/modules/staff/i18n/es.json +291 -0
- package/src/modules/staff/i18n/ko.json +291 -0
- package/src/modules/staff/i18n/pl.json +291 -0
- package/src/modules/staff/index.ts +1 -1
- package/src/modules/staff/lib/crud.ts +2 -1
- package/src/modules/staff/lib/support/attachments.ts +43 -0
- package/src/modules/staff/lib/support/dictionaries.ts +120 -0
- package/src/modules/staff/lib/support/notifications.ts +138 -0
- package/src/modules/staff/lib/support/portalAttachments.ts +140 -0
- package/src/modules/staff/lib/support/portalBroadcast.ts +161 -0
- package/src/modules/staff/lib/support/portalRoute.ts +108 -0
- package/src/modules/staff/lib/support/portalStatus.ts +15 -0
- package/src/modules/staff/lib/support/portalTickets.ts +233 -0
- package/src/modules/staff/lib/support/routing.ts +117 -0
- package/src/modules/staff/lib/support/routingRuleDraft.ts +45 -0
- package/src/modules/staff/lib/support/seedDefaults.ts +151 -0
- package/src/modules/staff/lib/support/supportActivity.ts +39 -0
- package/src/modules/staff/lib/support-ui/PortalSupportNotLinked.tsx +20 -0
- package/src/modules/staff/lib/support-ui/portalAttachmentValidation.ts +125 -0
- package/src/modules/staff/lib/support-ui/portalPendingUploads.ts +49 -0
- package/src/modules/staff/lib/support-ui/portalSupport.ts +116 -0
- package/src/modules/staff/lib/time-tracking/access.ts +2 -62
- package/src/modules/staff/lib/time-tracking/listProjectAccess.ts +78 -0
- package/src/modules/staff/lib/time-tracking/portalRecipients.ts +173 -5
- package/src/modules/staff/lib/time-tracking/projectAssignment.ts +172 -0
- package/src/modules/staff/lib/time-tracking/settingKeys.ts +11 -0
- package/src/modules/staff/lib/time-tracking/settings.ts +46 -1
- package/src/modules/staff/lib/time-tracking/taskAssignmentRoute.ts +165 -0
- package/src/modules/staff/lib/time-tracking/workRecipients.ts +183 -0
- package/src/modules/staff/lib/time-tracking-ui/BoardFilterChips.tsx +36 -1
- package/src/modules/staff/lib/time-tracking-ui/KanbanBoard.tsx +30 -0
- package/src/modules/staff/lib/time-tracking-ui/KanbanCard.tsx +9 -0
- package/src/modules/staff/lib/time-tracking-ui/ProjectFormSections.tsx +185 -6
- package/src/modules/staff/lib/time-tracking-ui/SupportAttachmentsSection.tsx +188 -0
- package/src/modules/staff/lib/time-tracking-ui/SupportRequestPanel.tsx +102 -0
- package/src/modules/staff/lib/time-tracking-ui/TaskBoardScreen.tsx +2 -0
- package/src/modules/staff/lib/time-tracking-ui/TaskCommentThread.tsx +105 -22
- package/src/modules/staff/lib/time-tracking-ui/TaskDrawer.tsx +278 -43
- package/src/modules/staff/lib/time-tracking-ui/boardDirectory.ts +21 -0
- package/src/modules/staff/lib/time-tracking-ui/boardFilters.ts +16 -2
- package/src/modules/staff/lib/time-tracking-ui/kanbanBoardData.ts +4 -0
- package/src/modules/staff/lib/time-tracking-ui/projectDefaultAssignee.ts +117 -0
- package/src/modules/staff/lib/time-tracking-ui/settingsTabs.ts +17 -0
- package/src/modules/staff/lib/time-tracking-ui/supportTicketData.ts +71 -0
- package/src/modules/staff/lib/time-tracking-ui/taskDrawerData.ts +15 -0
- package/src/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.ts +48 -1
- package/src/modules/staff/migrations/.snapshot-open-mercato.json +714 -0
- package/src/modules/staff/migrations/Migration20261009180543_staff.ts +50 -0
- package/src/modules/staff/notifications.ts +92 -0
- package/src/modules/staff/setup.ts +6 -2
- package/src/modules/staff/subscribers/support-portal-comment-created.ts +35 -0
- package/src/modules/staff/subscribers/support-portal-comment-deleted.ts +35 -0
- package/src/modules/staff/subscribers/support-portal-comment-updated.ts +35 -0
- package/src/modules/staff/subscribers/support-portal-status-column-updated.ts +36 -0
- package/src/modules/staff/subscribers/support-portal-task-status-changed.ts +34 -0
- package/src/modules/staff/subscribers/support-portal-task-updated.ts +34 -0
- package/src/modules/staff/subscribers/support-ticket-created-notification.ts +27 -0
- package/src/modules/staff/subscribers/support-ticket-customer-replied-notification.ts +27 -0
- package/src/modules/staff/subscribers/support-ticket-unnotified-clear.ts +29 -0
- package/src/modules/staff/subscribers/time-task-assignee-notification.ts +102 -0
- package/src/modules/staff/widgets/injection/portal-support-tickets/widget.client.tsx +151 -0
- package/src/modules/staff/widgets/injection/portal-support-tickets/widget.ts +16 -0
- package/src/modules/staff/widgets/injection/time-tracking-settings-support/widget.client.tsx +1058 -0
- package/src/modules/staff/widgets/injection/time-tracking-settings-support/widget.ts +17 -0
- package/src/modules/staff/widgets/injection-table.ts +15 -0
- package/src/modules/wms/components/backend/WmsLocationDetailPage.tsx +4 -20
- package/src/modules/wms/components/backend/WmsLotDetailPage.tsx +4 -20
- package/src/modules/wms/components/backend/WmsSkuDetailPage.tsx +4 -19
- package/src/modules/wms/lib/inventoryCsvExport.ts +18 -0
|
@@ -30,13 +30,12 @@ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
|
30
30
|
import { resolveCrudRecordId, parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'
|
|
31
31
|
import { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'
|
|
32
32
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
33
|
-
import
|
|
34
|
-
import {
|
|
33
|
+
import { getTelemetryRuntime } from '@open-mercato/shared/lib/telemetry/runtime'
|
|
34
|
+
import { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
35
|
+
import { StaffSupportTicket, StaffTimeTask, StaffTimeTaskStatus, StaffTimeTaskTag } from '../../../data/entities'
|
|
35
36
|
import { staffTimeTaskCreateSchema, staffTimeTaskUpdateSchema } from '../../../data/validators'
|
|
36
37
|
import { staffTimeTaskCommandIds } from '../../../commands/timesheets-tasks'
|
|
37
|
-
import {
|
|
38
|
-
import { MANAGE_PROJECTS_FEATURE, resolveProjectAccess, type ProjectAccess } from '../../../lib/time-tracking/access'
|
|
39
|
-
import { readTimeTrackingSettings } from '../../../lib/time-tracking/settings'
|
|
38
|
+
import { resolveListProjectAccess } from '../../../lib/time-tracking/listProjectAccess'
|
|
40
39
|
import { sanitizeSearchTerm, parseBooleanFlag } from '../../helpers'
|
|
41
40
|
import { createStaffCrudOpenApi, createPagedListResponseSchema, defaultOkResponseSchema } from '../../openapi'
|
|
42
41
|
|
|
@@ -57,6 +56,8 @@ const F = {
|
|
|
57
56
|
position: 'position',
|
|
58
57
|
created_by_user_id: 'created_by_user_id',
|
|
59
58
|
closed_at: 'closed_at',
|
|
59
|
+
source: 'source',
|
|
60
|
+
customer_id: 'customer_id',
|
|
60
61
|
created_at: 'created_at',
|
|
61
62
|
updated_at: 'updated_at',
|
|
62
63
|
} as const
|
|
@@ -87,6 +88,13 @@ const listSchema = z
|
|
|
87
88
|
parentTaskId: z.string().uuid().optional(),
|
|
88
89
|
tagIds: z.string().optional(),
|
|
89
90
|
topLevelOnly: z.string().optional(),
|
|
91
|
+
unassigned: z.string().optional(),
|
|
92
|
+
source: z.enum(['internal', 'portal']).optional(),
|
|
93
|
+
customerId: z.string().uuid().optional(),
|
|
94
|
+
supportCategory: z.string().optional(),
|
|
95
|
+
supportReference: z.string().optional(),
|
|
96
|
+
portalStatus: z.enum(['received', 'in_progress', 'resolved']).optional(),
|
|
97
|
+
supportUnnotified: z.string().optional(),
|
|
90
98
|
sortField: z.string().optional(),
|
|
91
99
|
sortDir: z.enum(['asc', 'desc']).optional(),
|
|
92
100
|
})
|
|
@@ -100,71 +108,30 @@ type TaskListQuery = z.infer<typeof listSchema>
|
|
|
100
108
|
*/
|
|
101
109
|
const IMPOSSIBLE_ID = '00000000-0000-0000-0000-000000000000'
|
|
102
110
|
|
|
103
|
-
|
|
111
|
+
export { resolveListProjectAccess }
|
|
104
112
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
type ContainerLike = { resolve: (name: string) => unknown }
|
|
113
|
+
/**
|
|
114
|
+
* Upper bound on the task ids the support filters materialize into the `id IN (...)`
|
|
115
|
+
* narrowing. The newest matching tickets win; an older match beyond the cap drops out
|
|
116
|
+
* of the filtered list instead of growing an unbounded parameter list.
|
|
117
|
+
*/
|
|
118
|
+
export const SUPPORT_FILTER_MAX_TASK_IDS = 1000
|
|
113
119
|
|
|
114
|
-
/**
|
|
115
|
-
|
|
120
|
+
/**
|
|
121
|
+
* Requests whose support-filter narrowing hit `SUPPORT_FILTER_MAX_TASK_IDS`. The list's
|
|
122
|
+
* `afterList` hook reports them as `totalIsCapped`, so a truncated support filter is
|
|
123
|
+
* never presented as the complete result.
|
|
124
|
+
*/
|
|
125
|
+
const supportFilterTruncatedRequests = new WeakSet<Request>()
|
|
116
126
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const configService = container.resolve('moduleConfigService') as ModuleConfigService
|
|
120
|
-
const settings = await readTimeTrackingSettings(configService, { tenantId })
|
|
121
|
-
return settings.access.assignmentGraceDays
|
|
122
|
-
} catch {
|
|
123
|
-
// Fail safe to the documented default rather than widening the window.
|
|
124
|
-
return null
|
|
125
|
-
}
|
|
127
|
+
export function markTaskListTotalCapped(payload: Record<string, unknown>, ctx: { request?: Request }): void {
|
|
128
|
+
if (ctx.request && supportFilterTruncatedRequests.has(ctx.request)) payload.totalIsCapped = true
|
|
126
129
|
}
|
|
127
130
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
organizationId: string | null,
|
|
133
|
-
): Promise<ProjectAccess> {
|
|
134
|
-
if (!tenantId || !organizationId) return { ...DENIED_ACCESS }
|
|
135
|
-
const scope = { tenantId, organizationId }
|
|
136
|
-
try {
|
|
137
|
-
// One lookup, one authority, and a failure that says so. The previous
|
|
138
|
-
// `catch → []` could not tell "denied" from "could not ask", so an RBAC
|
|
139
|
-
// hiccup silently demoted a manager to their own memberships.
|
|
140
|
-
const access = await resolveFeatureAccess(container, userId, [MANAGE_PROJECTS_FEATURE], scope)
|
|
141
|
-
const em = container.resolve('em') as EntityManager
|
|
142
|
-
return await resolveProjectAccess({
|
|
143
|
-
em: em.fork(),
|
|
144
|
-
userId,
|
|
145
|
-
tenantId,
|
|
146
|
-
organizationId,
|
|
147
|
-
canManageAll: access.allowed,
|
|
148
|
-
userFeatures: access.grantedFeatures,
|
|
149
|
-
assignmentGraceDays: await resolveAssignmentGraceDays(container, tenantId),
|
|
150
|
-
})
|
|
151
|
-
} catch (err) {
|
|
152
|
-
logger.error('staff.timesheets.tasks access resolution failed', { err })
|
|
153
|
-
return { ...DENIED_ACCESS }
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export async function resolveListProjectAccess(ctx: CrudCtx): Promise<ProjectAccess> {
|
|
158
|
-
const tenantId = ctx.auth?.tenantId ?? null
|
|
159
|
-
const organizationId = ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null
|
|
160
|
-
const load = () => loadProjectAccess(ctx.container, ctx.auth?.sub ?? null, tenantId, organizationId)
|
|
161
|
-
const request = ctx.request
|
|
162
|
-
if (!request) return load()
|
|
163
|
-
const cached = accessByRequest.get(request)
|
|
164
|
-
if (cached) return cached
|
|
165
|
-
const pending = load()
|
|
166
|
-
accessByRequest.set(request, pending)
|
|
167
|
-
return pending
|
|
131
|
+
function intersectIds(current: string[] | null, next: string[]): string[] {
|
|
132
|
+
if (current === null) return next
|
|
133
|
+
const allowed = new Set(next)
|
|
134
|
+
return current.filter((id) => allowed.has(id))
|
|
168
135
|
}
|
|
169
136
|
|
|
170
137
|
function splitIdList(value: unknown): string[] | null {
|
|
@@ -210,6 +177,65 @@ async function resolveTaskIdsForTags(ctx: CrudCtx, tagIds: string[]): Promise<st
|
|
|
210
177
|
return [...seen.entries()].filter(([, tags]) => tags.size >= required).map(([taskId]) => taskId)
|
|
211
178
|
} catch (err) {
|
|
212
179
|
logger.error('staff.timesheets.tasks tag filter resolution failed', { err })
|
|
180
|
+
getTelemetryRuntime()?.reportError(err, { module: 'staff', code: 'staff.task_tag_filter_failed' })
|
|
181
|
+
return []
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
type SupportFilterCriteria = {
|
|
186
|
+
supportCategory?: string
|
|
187
|
+
supportReference?: string
|
|
188
|
+
supportUnnotified?: boolean
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Task ids whose support ticket row matches the requested support criteria. Like the
|
|
193
|
+
* tag lookup, a failed lookup narrows to nothing rather than widening the page.
|
|
194
|
+
*/
|
|
195
|
+
async function resolveTaskIdsForSupport(ctx: CrudCtx, criteria: SupportFilterCriteria): Promise<string[]> {
|
|
196
|
+
const tenantId = ctx.auth?.tenantId ?? null
|
|
197
|
+
const organizationId = ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null
|
|
198
|
+
if (!tenantId || !organizationId) return []
|
|
199
|
+
try {
|
|
200
|
+
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
201
|
+
const where: Record<string, unknown> = { tenantId, organizationId, deletedAt: null }
|
|
202
|
+
if (criteria.supportCategory) where.categoryValue = criteria.supportCategory
|
|
203
|
+
if (criteria.supportReference) where.supportReference = { $ilike: `${escapeLikePattern(criteria.supportReference)}%` }
|
|
204
|
+
if (criteria.supportUnnotified) where.unnotifiedAt = { $ne: null }
|
|
205
|
+
const rows = await findWithDecryption(
|
|
206
|
+
em,
|
|
207
|
+
StaffSupportTicket,
|
|
208
|
+
where,
|
|
209
|
+
{ fields: ['taskId'], limit: SUPPORT_FILTER_MAX_TASK_IDS + 1, orderBy: { createdAt: 'desc' } },
|
|
210
|
+
{ tenantId, organizationId },
|
|
211
|
+
)
|
|
212
|
+
if (rows.length > SUPPORT_FILTER_MAX_TASK_IDS && ctx.request) supportFilterTruncatedRequests.add(ctx.request)
|
|
213
|
+
return Array.from(new Set(rows.slice(0, SUPPORT_FILTER_MAX_TASK_IDS).map((row) => row.taskId)))
|
|
214
|
+
} catch (err) {
|
|
215
|
+
logger.error('staff.timesheets.tasks support filter resolution failed', { err })
|
|
216
|
+
getTelemetryRuntime()?.reportError(err, { module: 'staff', code: 'staff.task_support_filter_failed' })
|
|
217
|
+
return []
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async function resolveStatusIdsForPortalStatus(
|
|
222
|
+
ctx: CrudCtx,
|
|
223
|
+
portalStatus: 'received' | 'in_progress' | 'resolved',
|
|
224
|
+
): Promise<string[]> {
|
|
225
|
+
const tenantId = ctx.auth?.tenantId ?? null
|
|
226
|
+
const organizationId = ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null
|
|
227
|
+
if (!tenantId || !organizationId) return []
|
|
228
|
+
try {
|
|
229
|
+
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
230
|
+
const where: Record<string, unknown> = { tenantId, organizationId, deletedAt: null }
|
|
231
|
+
if (portalStatus === 'resolved') where.isDone = true
|
|
232
|
+
else if (portalStatus === 'received') Object.assign(where, { isDone: false, isDefault: true })
|
|
233
|
+
else Object.assign(where, { isDone: false, isDefault: false })
|
|
234
|
+
const rows = await em.find(StaffTimeTaskStatus, where, { fields: ['id'] })
|
|
235
|
+
return rows.map((row) => row.id)
|
|
236
|
+
} catch (err) {
|
|
237
|
+
logger.error('staff.timesheets.tasks portal status filter resolution failed', { err })
|
|
238
|
+
getTelemetryRuntime()?.reportError(err, { module: 'staff', code: 'staff.task_portal_status_filter_failed' })
|
|
213
239
|
return []
|
|
214
240
|
}
|
|
215
241
|
}
|
|
@@ -228,13 +254,25 @@ export async function buildTaskListFilters(
|
|
|
228
254
|
for (const raw of [query.ids, query.id]) {
|
|
229
255
|
const parsed = splitIdList(raw)
|
|
230
256
|
if (parsed === null) continue
|
|
231
|
-
narrowIds = narrowIds
|
|
257
|
+
narrowIds = intersectIds(narrowIds, parsed)
|
|
232
258
|
}
|
|
233
259
|
|
|
234
260
|
const requestedTagIds = splitIdList(query.tagIds)
|
|
235
261
|
if (requestedTagIds !== null) {
|
|
236
262
|
const tagged = requestedTagIds.length === 0 ? [] : await resolveTaskIdsForTags(ctx, requestedTagIds)
|
|
237
|
-
narrowIds = narrowIds
|
|
263
|
+
narrowIds = intersectIds(narrowIds, tagged)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const supportCategory = typeof query.supportCategory === 'string' ? query.supportCategory.trim() : ''
|
|
267
|
+
const supportReference = sanitizeSearchTerm(query.supportReference) ?? ''
|
|
268
|
+
const supportUnnotified = parseBooleanFlag(query.supportUnnotified)
|
|
269
|
+
if (supportCategory || supportReference || supportUnnotified) {
|
|
270
|
+
const supportTaskIds = await resolveTaskIdsForSupport(ctx, {
|
|
271
|
+
supportCategory: supportCategory || undefined,
|
|
272
|
+
supportReference: supportReference || undefined,
|
|
273
|
+
supportUnnotified,
|
|
274
|
+
})
|
|
275
|
+
narrowIds = intersectIds(narrowIds, supportTaskIds)
|
|
238
276
|
}
|
|
239
277
|
|
|
240
278
|
const access = await resolveListProjectAccess(ctx)
|
|
@@ -266,10 +304,17 @@ export async function buildTaskListFilters(
|
|
|
266
304
|
|
|
267
305
|
if (typeof query.taskStatusId === 'string' && query.taskStatusId.trim().length > 0) {
|
|
268
306
|
filters[F.task_status_id] = query.taskStatusId.trim()
|
|
307
|
+
} else if (query.portalStatus) {
|
|
308
|
+
const statusIds = await resolveStatusIdsForPortalStatus(ctx, query.portalStatus)
|
|
309
|
+
filters[F.task_status_id] = { $in: statusIds.length > 0 ? statusIds : [IMPOSSIBLE_ID] }
|
|
269
310
|
}
|
|
270
311
|
if (typeof query.assigneeStaffMemberId === 'string' && query.assigneeStaffMemberId.trim().length > 0) {
|
|
271
312
|
filters[F.assignee_staff_member_id] = query.assigneeStaffMemberId.trim()
|
|
313
|
+
} else if (parseBooleanFlag(query.unassigned)) {
|
|
314
|
+
filters[F.assignee_staff_member_id] = { $eq: null }
|
|
272
315
|
}
|
|
316
|
+
if (query.source) filters[F.source] = query.source
|
|
317
|
+
if (query.customerId) filters[F.customer_id] = query.customerId
|
|
273
318
|
if (typeof query.parentTaskId === 'string' && query.parentTaskId.trim().length > 0) {
|
|
274
319
|
// The drawer's checklist: one parent's children, in position order.
|
|
275
320
|
filters[F.parent_task_id] = query.parentTaskId.trim()
|
|
@@ -320,6 +365,8 @@ export const taskListFields = [
|
|
|
320
365
|
F.position,
|
|
321
366
|
F.created_by_user_id,
|
|
322
367
|
F.closed_at,
|
|
368
|
+
F.source,
|
|
369
|
+
F.customer_id,
|
|
323
370
|
F.created_at,
|
|
324
371
|
F.updated_at,
|
|
325
372
|
] as const
|
|
@@ -355,6 +402,9 @@ const crud = makeCrudRoute({
|
|
|
355
402
|
buildFilters: buildTaskListFilters,
|
|
356
403
|
},
|
|
357
404
|
enrichers: { entityId: 'staff:staff_time_task' },
|
|
405
|
+
hooks: {
|
|
406
|
+
afterList: (payload, ctx) => markTaskListTotalCapped(payload, ctx),
|
|
407
|
+
},
|
|
358
408
|
actions: {
|
|
359
409
|
create: {
|
|
360
410
|
commandId: staffTimeTaskCommandIds.create,
|
|
@@ -408,6 +458,8 @@ export const taskListItemSchema = z.object({
|
|
|
408
458
|
position: z.number().int().nullable().optional(),
|
|
409
459
|
created_by_user_id: z.string().uuid().nullable().optional(),
|
|
410
460
|
closed_at: z.string().nullable().optional(),
|
|
461
|
+
source: z.enum(['internal', 'portal']).nullable().optional(),
|
|
462
|
+
customer_id: z.string().uuid().nullable().optional(),
|
|
411
463
|
created_at: z.string().nullable().optional(),
|
|
412
464
|
updated_at: z.string().nullable().optional(),
|
|
413
465
|
/** This task's own entries, in raw minutes. */
|
|
@@ -31,6 +31,7 @@ const F = {
|
|
|
31
31
|
status: 'status',
|
|
32
32
|
assigned_start_date: 'assigned_start_date',
|
|
33
33
|
assigned_end_date: 'assigned_end_date',
|
|
34
|
+
is_assigner: 'is_assigner',
|
|
34
35
|
created_at: 'created_at',
|
|
35
36
|
updated_at: 'updated_at',
|
|
36
37
|
deleted_at: 'deleted_at',
|
|
@@ -90,6 +91,7 @@ const crud = makeCrudRoute({
|
|
|
90
91
|
F.status,
|
|
91
92
|
F.assigned_start_date,
|
|
92
93
|
F.assigned_end_date,
|
|
94
|
+
F.is_assigner,
|
|
93
95
|
F.created_at,
|
|
94
96
|
F.updated_at,
|
|
95
97
|
],
|
|
@@ -201,6 +203,7 @@ const projectMemberListItemSchema = z.object({
|
|
|
201
203
|
status: z.string().nullable().optional(),
|
|
202
204
|
assigned_start_date: z.string().nullable().optional(),
|
|
203
205
|
assigned_end_date: z.string().nullable().optional(),
|
|
206
|
+
is_assigner: z.boolean().nullable().optional(),
|
|
204
207
|
created_at: z.string().nullable().optional(),
|
|
205
208
|
updated_at: z.string().nullable().optional(),
|
|
206
209
|
})
|
|
@@ -47,6 +47,7 @@ const F = {
|
|
|
47
47
|
budget_kind: 'budget_kind',
|
|
48
48
|
budget_value: 'budget_value',
|
|
49
49
|
budget_warn_at_percent: 'budget_warn_at_percent',
|
|
50
|
+
default_assignee_staff_member_id: 'default_assignee_staff_member_id',
|
|
50
51
|
created_at: 'created_at',
|
|
51
52
|
updated_at: 'updated_at',
|
|
52
53
|
deleted_at: 'deleted_at',
|
|
@@ -338,6 +339,7 @@ export const timeProjectListFields = [
|
|
|
338
339
|
F.budget_kind,
|
|
339
340
|
F.budget_value,
|
|
340
341
|
F.budget_warn_at_percent,
|
|
342
|
+
F.default_assignee_staff_member_id,
|
|
341
343
|
F.created_at,
|
|
342
344
|
F.updated_at,
|
|
343
345
|
] as const
|
|
@@ -563,6 +565,7 @@ export const timeProjectListItemSchema = z.object({
|
|
|
563
565
|
budget_kind: z.string().nullable().optional(),
|
|
564
566
|
budget_value: z.union([z.string(), z.number()]).nullable().optional(),
|
|
565
567
|
budget_warn_at_percent: z.number().int().nullable().optional(),
|
|
568
|
+
default_assignee_staff_member_id: z.string().uuid().nullable().optional(),
|
|
566
569
|
created_at: z.string().nullable().optional(),
|
|
567
570
|
updated_at: z.string().nullable().optional(),
|
|
568
571
|
entryCount: z.number().int().optional(),
|
|
@@ -89,6 +89,7 @@ function toValues(record: ProjectRecord): ProjectFormValues {
|
|
|
89
89
|
budgetValue: readString(record, 'budgetValue', 'budget_value'),
|
|
90
90
|
budgetWarnAtPercent:
|
|
91
91
|
readString(record, 'budgetWarnAtPercent', 'budget_warn_at_percent') ?? String(DEFAULT_BUDGET_WARN_AT_PERCENT),
|
|
92
|
+
defaultAssigneeStaffMemberId: readString(record, 'defaultAssigneeStaffMemberId', 'default_assignee_staff_member_id') ?? null,
|
|
92
93
|
projectType: readString(record, 'projectType', 'project_type'),
|
|
93
94
|
color: readString(record, 'color'),
|
|
94
95
|
startDate: readString(record, 'startDate', 'start_date'),
|
|
@@ -11,8 +11,10 @@ import { Input } from '@open-mercato/ui/primitives/input'
|
|
|
11
11
|
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'
|
|
12
12
|
import { LookupSelect } from '@open-mercato/ui/backend/inputs/LookupSelect'
|
|
13
13
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@open-mercato/ui/primitives/tabs'
|
|
14
|
-
import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
|
|
15
|
-
import { deleteCrud, createCrud } from '@open-mercato/ui/backend/utils/crud'
|
|
14
|
+
import { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
|
|
15
|
+
import { deleteCrud, createCrud, updateCrud } from '@open-mercato/ui/backend/utils/crud'
|
|
16
|
+
import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
|
|
17
|
+
import { Switch } from '@open-mercato/ui/primitives/switch'
|
|
16
18
|
import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
|
|
17
19
|
import { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'
|
|
18
20
|
import { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'
|
|
@@ -102,6 +104,9 @@ type EmployeeAssignment = {
|
|
|
102
104
|
assignedEndDate: string | null
|
|
103
105
|
displayName: string | null
|
|
104
106
|
teamName: string | null
|
|
107
|
+
isAssigner: boolean
|
|
108
|
+
hasUserAccount: boolean | null
|
|
109
|
+
updatedAt: string | null
|
|
105
110
|
}
|
|
106
111
|
|
|
107
112
|
type EmployeesResponse = {
|
|
@@ -113,6 +118,8 @@ type StaffMemberRecord = {
|
|
|
113
118
|
id: string
|
|
114
119
|
display_name?: string
|
|
115
120
|
displayName?: string
|
|
121
|
+
user_id?: string | null
|
|
122
|
+
userId?: string | null
|
|
116
123
|
team?: { id: string; name: string } | null
|
|
117
124
|
}
|
|
118
125
|
|
|
@@ -475,6 +482,9 @@ export default function TimesheetProjectDetailPage({ params }: { params?: { id?:
|
|
|
475
482
|
: null,
|
|
476
483
|
displayName: staff?.display_name ?? staff?.displayName ?? null,
|
|
477
484
|
teamName: staff?.team?.name ?? null,
|
|
485
|
+
isAssigner: item.is_assigner === true || item.isAssigner === true,
|
|
486
|
+
hasUserAccount: staff ? Boolean(staff.user_id ?? staff.userId) : null,
|
|
487
|
+
updatedAt: typeof (item.updated_at ?? item.updatedAt) === 'string' ? String(item.updated_at ?? item.updatedAt) : null,
|
|
478
488
|
}
|
|
479
489
|
})
|
|
480
490
|
|
|
@@ -562,6 +572,37 @@ export default function TimesheetProjectDetailPage({ params }: { params?: { id?:
|
|
|
562
572
|
}
|
|
563
573
|
}, [projectId, confirm, employeeMutationContext, runMutation, t])
|
|
564
574
|
|
|
575
|
+
const handleToggleAssigner = React.useCallback(async (emp: EmployeeAssignment, nextValue: boolean) => {
|
|
576
|
+
const payload = { id: emp.id, isAssigner: nextValue }
|
|
577
|
+
try {
|
|
578
|
+
await runMutation({
|
|
579
|
+
operation: () =>
|
|
580
|
+
withScopedApiRequestHeaders(buildOptimisticLockHeader(emp.updatedAt), () =>
|
|
581
|
+
updateCrud(`staff/timesheets/time-projects/${projectId}/employees`, payload, {
|
|
582
|
+
errorMessage: t('staff.time_tracking.assigners.saveError', 'Could not change the assigner setting.'),
|
|
583
|
+
}),
|
|
584
|
+
),
|
|
585
|
+
context: employeeMutationContext(emp.id),
|
|
586
|
+
mutationPayload: payload,
|
|
587
|
+
})
|
|
588
|
+
flash(
|
|
589
|
+
nextValue
|
|
590
|
+
? t('staff.time_tracking.assigners.enabled', 'Member is now an assigner.')
|
|
591
|
+
: t('staff.time_tracking.assigners.disabled', 'Member is no longer an assigner.'),
|
|
592
|
+
'success',
|
|
593
|
+
)
|
|
594
|
+
setReloadToken((token) => token + 1)
|
|
595
|
+
} catch (toggleError) {
|
|
596
|
+
if (surfaceRecordConflict(toggleError, t, { onRefresh: () => setReloadToken((token) => token + 1) })) return
|
|
597
|
+
flash(
|
|
598
|
+
toggleError instanceof Error && toggleError.message
|
|
599
|
+
? toggleError.message
|
|
600
|
+
: t('staff.time_tracking.assigners.saveError', 'Could not change the assigner setting.'),
|
|
601
|
+
'error',
|
|
602
|
+
)
|
|
603
|
+
}
|
|
604
|
+
}, [projectId, employeeMutationContext, runMutation, t])
|
|
605
|
+
|
|
565
606
|
// --- Add employee dialog ---
|
|
566
607
|
const openAddDialog = React.useCallback(() => {
|
|
567
608
|
setAddStaffMemberId(null)
|
|
@@ -1104,6 +1145,11 @@ export default function TimesheetProjectDetailPage({ params }: { params?: { id?:
|
|
|
1104
1145
|
</div>
|
|
1105
1146
|
</div>
|
|
1106
1147
|
<div className="flex items-center gap-3 shrink-0">
|
|
1148
|
+
{emp.isAssigner ? (
|
|
1149
|
+
<Badge variant="info" data-testid={`project-member-assigner-badge-${emp.id}`}>
|
|
1150
|
+
{t('staff.time_tracking.assigners.badge', 'Assigner')}
|
|
1151
|
+
</Badge>
|
|
1152
|
+
) : null}
|
|
1107
1153
|
<Badge
|
|
1108
1154
|
variant={(emp.status ?? 'active') === 'active' ? 'success' : 'neutral'}
|
|
1109
1155
|
className="capitalize"
|
|
@@ -1155,6 +1201,25 @@ export default function TimesheetProjectDetailPage({ params }: { params?: { id?:
|
|
|
1155
1201
|
<dd>{emp.teamName}</dd>
|
|
1156
1202
|
</div>
|
|
1157
1203
|
) : null}
|
|
1204
|
+
<div className="col-span-2">
|
|
1205
|
+
<dt className="font-medium text-muted-foreground">
|
|
1206
|
+
{t('staff.time_tracking.assigners.label', 'Assigner')}
|
|
1207
|
+
</dt>
|
|
1208
|
+
<dd className="flex items-center gap-2 pt-1">
|
|
1209
|
+
<Switch
|
|
1210
|
+
checked={emp.isAssigner}
|
|
1211
|
+
disabled={!canManageProjects || emp.hasUserAccount === false}
|
|
1212
|
+
onCheckedChange={(checked) => { void handleToggleAssigner(emp, checked === true) }}
|
|
1213
|
+
aria-label={t('staff.time_tracking.assigners.label', 'Assigner')}
|
|
1214
|
+
data-testid={`project-member-assigner-switch-${emp.id}`}
|
|
1215
|
+
/>
|
|
1216
|
+
<span className="text-xs text-muted-foreground">
|
|
1217
|
+
{emp.hasUserAccount === false
|
|
1218
|
+
? t('staff.time_tracking.assigners.needsUser', 'Only members with a user account can be assigners.')
|
|
1219
|
+
: t('staff.time_tracking.assigners.help', 'Assigners are notified about new and released work and can claim unassigned tasks.')}
|
|
1220
|
+
</span>
|
|
1221
|
+
</dd>
|
|
1222
|
+
</div>
|
|
1158
1223
|
</dl>
|
|
1159
1224
|
{canManageProjects && (
|
|
1160
1225
|
<div className="flex justify-end">
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
ProjectBudgetSection,
|
|
16
16
|
ProjectRoundingNotice,
|
|
17
17
|
ProjectTeamSection,
|
|
18
|
+
ProjectDefaultAssigneeField,
|
|
18
19
|
type ProjectBudgetKind,
|
|
19
20
|
} from '../../../../lib/time-tracking-ui/ProjectFormSections'
|
|
20
21
|
|
|
@@ -43,6 +44,7 @@ export type ProjectFormValues = {
|
|
|
43
44
|
startDate?: string | null
|
|
44
45
|
costCenter?: string | null
|
|
45
46
|
status?: string
|
|
47
|
+
defaultAssigneeStaffMemberId?: string | null
|
|
46
48
|
/**
|
|
47
49
|
* Custom-field values rendered by `CrudForm` from `entityIds`. They are keyed
|
|
48
50
|
* `cf_<key>` / `cf:<key>` and are opaque here: this form neither declares nor
|
|
@@ -112,6 +114,7 @@ export function createProjectFormSchema() {
|
|
|
112
114
|
startDate: z.string().optional().nullable(),
|
|
113
115
|
costCenter: z.string().max(100).optional().nullable(),
|
|
114
116
|
status: z.enum(['active', 'on_hold', 'completed']).optional(),
|
|
117
|
+
defaultAssigneeStaffMemberId: z.string().optional().nullable(),
|
|
115
118
|
})
|
|
116
119
|
}
|
|
117
120
|
|
|
@@ -381,9 +384,22 @@ export function createProjectFormGroups(
|
|
|
381
384
|
id: 'team',
|
|
382
385
|
column: 2,
|
|
383
386
|
title: t('staff.time_tracking.projects.form.groupTeam', 'Team'),
|
|
384
|
-
component: (ctx) =>
|
|
385
|
-
projectId
|
|
386
|
-
|
|
387
|
+
component: (ctx) => {
|
|
388
|
+
const projectId = typeof ctx.values.id === 'string' ? ctx.values.id : null
|
|
389
|
+
const defaultAssignee = ctx.values.defaultAssigneeStaffMemberId
|
|
390
|
+
return React.createElement(
|
|
391
|
+
React.Fragment,
|
|
392
|
+
null,
|
|
393
|
+
React.createElement(ProjectTeamSection, { projectId }),
|
|
394
|
+
projectId
|
|
395
|
+
? React.createElement(ProjectDefaultAssigneeField, {
|
|
396
|
+
projectId,
|
|
397
|
+
value: typeof defaultAssignee === 'string' && defaultAssignee.length > 0 ? defaultAssignee : null,
|
|
398
|
+
onChange: (next: string | null) => ctx.setValue('defaultAssigneeStaffMemberId', next),
|
|
399
|
+
})
|
|
400
|
+
: null,
|
|
401
|
+
)
|
|
402
|
+
},
|
|
387
403
|
},
|
|
388
404
|
{
|
|
389
405
|
id: 'rounding',
|
|
@@ -456,6 +472,9 @@ export function buildProjectPayload(values: ProjectFormValues): Record<string, u
|
|
|
456
472
|
payload.currencyCode = trimmedOrNull(values.currencyCode)
|
|
457
473
|
} else {
|
|
458
474
|
payload.id = values.id
|
|
475
|
+
if (values.defaultAssigneeStaffMemberId !== undefined) {
|
|
476
|
+
payload.defaultAssigneeStaffMemberId = values.defaultAssigneeStaffMemberId || null
|
|
477
|
+
}
|
|
459
478
|
}
|
|
460
479
|
if (values.status) payload.status = values.status
|
|
461
480
|
// The form renders custom-field inputs whenever `entityIds` is passed, but this
|