@open-mercato/core 0.10.1-develop.7717.1.2befe9c634 → 0.10.1-develop.7721.1.b5c4a00a68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +2 -2
- package/dist/generated/entities/staff_support_routing_rule/index.js +21 -0
- package/dist/generated/entities/staff_support_routing_rule/index.js.map +7 -0
- package/dist/generated/entities/staff_support_ticket/index.js +37 -0
- package/dist/generated/entities/staff_support_ticket/index.js.map +7 -0
- package/dist/generated/entities/staff_time_project/index.js +2 -0
- package/dist/generated/entities/staff_time_project/index.js.map +2 -2
- package/dist/generated/entities/staff_time_project_member/index.js +2 -0
- package/dist/generated/entities/staff_time_project_member/index.js.map +2 -2
- package/dist/generated/entities/staff_time_task/index.js +4 -0
- package/dist/generated/entities/staff_time_task/index.js.map +2 -2
- package/dist/generated/entities/staff_time_task_comment/index.js +9 -1
- package/dist/generated/entities/staff_time_task_comment/index.js.map +2 -2
- package/dist/generated/entities.ids.generated.js +2 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +38 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/auth.js +1 -0
- package/dist/helpers/integration/auth.js.map +2 -2
- package/dist/helpers/integration/communicationChannelsFixtures.js +20 -0
- package/dist/helpers/integration/communicationChannelsFixtures.js.map +2 -2
- package/dist/modules/catalog/api/bulk-delete/route.js +12 -2
- package/dist/modules/catalog/api/bulk-delete/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/test-seed/route.js +17 -3
- package/dist/modules/communication_channels/api/post/test-seed/route.js.map +2 -2
- package/dist/modules/communication_channels/lib/test-seed.js +38 -5
- package/dist/modules/communication_channels/lib/test-seed.js.map +2 -2
- package/dist/modules/messages/commands/messages.js +2 -3
- package/dist/modules/messages/commands/messages.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js.map +2 -2
- package/dist/modules/staff/api/portal/support-tickets/[id]/attachments/route.js +299 -0
- package/dist/modules/staff/api/portal/support-tickets/[id]/attachments/route.js.map +7 -0
- package/dist/modules/staff/api/portal/support-tickets/[id]/messages/route.js +91 -0
- package/dist/modules/staff/api/portal/support-tickets/[id]/messages/route.js.map +7 -0
- package/dist/modules/staff/api/portal/support-tickets/[id]/route.js +146 -0
- package/dist/modules/staff/api/portal/support-tickets/[id]/route.js.map +7 -0
- package/dist/modules/staff/api/portal/support-tickets/options/route.js +60 -0
- package/dist/modules/staff/api/portal/support-tickets/options/route.js.map +7 -0
- package/dist/modules/staff/api/portal/support-tickets/route.js +220 -0
- package/dist/modules/staff/api/portal/support-tickets/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/settings/route.js +7 -1
- package/dist/modules/staff/api/timesheets/settings/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/support-routing-rules/fallback-project/route.js +122 -0
- package/dist/modules/staff/api/timesheets/support-routing-rules/fallback-project/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/support-routing-rules/preview/route.js +180 -0
- package/dist/modules/staff/api/timesheets/support-routing-rules/preview/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/support-routing-rules/route.js +147 -0
- package/dist/modules/staff/api/timesheets/support-routing-rules/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/support-tickets/options/route.js +133 -0
- package/dist/modules/staff/api/timesheets/support-tickets/options/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tasks/[id]/claim/route.js +33 -0
- package/dist/modules/staff/api/timesheets/tasks/[id]/claim/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tasks/[id]/comments/route.js +25 -7
- package/dist/modules/staff/api/timesheets/tasks/[id]/comments/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/tasks/[id]/release/route.js +32 -0
- package/dist/modules/staff/api/timesheets/tasks/[id]/release/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tasks/route.js +94 -47
- package/dist/modules/staff/api/timesheets/tasks/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js +3 -0
- package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-projects/route.js +3 -0
- package/dist/modules/staff/api/timesheets/time-projects/route.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.js +1 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js +54 -4
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.js +21 -5
- package/dist/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/settings/page.js +319 -254
- package/dist/modules/staff/backend/staff/time-tracking/settings/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/support/page.js +363 -0
- package/dist/modules/staff/backend/staff/time-tracking/support/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/support/page.meta.js +18 -0
- package/dist/modules/staff/backend/staff/time-tracking/support/page.meta.js.map +7 -0
- package/dist/modules/staff/cli.js +27 -0
- package/dist/modules/staff/cli.js.map +2 -2
- package/dist/modules/staff/commands/index.js +3 -0
- package/dist/modules/staff/commands/index.js.map +2 -2
- package/dist/modules/staff/commands/shared.js +10 -1
- package/dist/modules/staff/commands/shared.js.map +2 -2
- package/dist/modules/staff/commands/support-routing-rules.js +505 -0
- package/dist/modules/staff/commands/support-routing-rules.js.map +7 -0
- package/dist/modules/staff/commands/support-tickets.js +513 -0
- package/dist/modules/staff/commands/support-tickets.js.map +7 -0
- package/dist/modules/staff/commands/timesheets-projects.js +72 -14
- package/dist/modules/staff/commands/timesheets-projects.js.map +2 -2
- package/dist/modules/staff/commands/timesheets-task-assignment.js +270 -0
- package/dist/modules/staff/commands/timesheets-task-assignment.js.map +7 -0
- package/dist/modules/staff/commands/timesheets-task-comments.js +58 -12
- package/dist/modules/staff/commands/timesheets-task-comments.js.map +2 -2
- package/dist/modules/staff/commands/timesheets-tasks.js +91 -9
- package/dist/modules/staff/commands/timesheets-tasks.js.map +3 -3
- package/dist/modules/staff/data/enrichers.js +141 -0
- package/dist/modules/staff/data/enrichers.js.map +2 -2
- package/dist/modules/staff/data/entities.js +143 -2
- package/dist/modules/staff/data/entities.js.map +2 -2
- package/dist/modules/staff/data/extensions.js +8 -0
- package/dist/modules/staff/data/extensions.js.map +2 -2
- package/dist/modules/staff/data/validators.js +34 -5
- package/dist/modules/staff/data/validators.js.map +2 -2
- package/dist/modules/staff/encryption.js +6 -0
- package/dist/modules/staff/encryption.js.map +2 -2
- package/dist/modules/staff/events.js +25 -0
- package/dist/modules/staff/events.js.map +2 -2
- package/dist/modules/staff/events.payloads.js +54 -0
- package/dist/modules/staff/events.payloads.js.map +2 -2
- package/dist/modules/staff/extension-points.js +1 -0
- package/dist/modules/staff/extension-points.js.map +2 -2
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.js +468 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.meta.js +12 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.meta.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/new/page.js +316 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/new/page.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/new/page.meta.js +12 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/new/page.meta.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/page.js +190 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/page.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/page.meta.js +19 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/support/page.meta.js.map +7 -0
- package/dist/modules/staff/index.js +1 -1
- package/dist/modules/staff/index.js.map +2 -2
- package/dist/modules/staff/lib/crud.js +2 -0
- package/dist/modules/staff/lib/crud.js.map +2 -2
- package/dist/modules/staff/lib/support/attachments.js +36 -0
- package/dist/modules/staff/lib/support/attachments.js.map +7 -0
- package/dist/modules/staff/lib/support/dictionaries.js +87 -0
- package/dist/modules/staff/lib/support/dictionaries.js.map +7 -0
- package/dist/modules/staff/lib/support/notifications.js +111 -0
- package/dist/modules/staff/lib/support/notifications.js.map +7 -0
- package/dist/modules/staff/lib/support/portalAttachments.js +100 -0
- package/dist/modules/staff/lib/support/portalAttachments.js.map +7 -0
- package/dist/modules/staff/lib/support/portalBroadcast.js +117 -0
- package/dist/modules/staff/lib/support/portalBroadcast.js.map +7 -0
- package/dist/modules/staff/lib/support/portalRoute.js +99 -0
- package/dist/modules/staff/lib/support/portalRoute.js.map +7 -0
- package/dist/modules/staff/lib/support/portalStatus.js +12 -0
- package/dist/modules/staff/lib/support/portalStatus.js.map +7 -0
- package/dist/modules/staff/lib/support/portalTickets.js +148 -0
- package/dist/modules/staff/lib/support/portalTickets.js.map +7 -0
- package/dist/modules/staff/lib/support/routing.js +81 -0
- package/dist/modules/staff/lib/support/routing.js.map +7 -0
- package/dist/modules/staff/lib/support/routingRuleDraft.js +25 -0
- package/dist/modules/staff/lib/support/routingRuleDraft.js.map +7 -0
- package/dist/modules/staff/lib/support/seedDefaults.js +114 -0
- package/dist/modules/staff/lib/support/seedDefaults.js.map +7 -0
- package/dist/modules/staff/lib/support/supportActivity.js +26 -0
- package/dist/modules/staff/lib/support/supportActivity.js.map +7 -0
- package/dist/modules/staff/lib/support-ui/PortalSupportNotLinked.js +23 -0
- package/dist/modules/staff/lib/support-ui/PortalSupportNotLinked.js.map +7 -0
- package/dist/modules/staff/lib/support-ui/portalAttachmentValidation.js +112 -0
- package/dist/modules/staff/lib/support-ui/portalAttachmentValidation.js.map +7 -0
- package/dist/modules/staff/lib/support-ui/portalPendingUploads.js +45 -0
- package/dist/modules/staff/lib/support-ui/portalPendingUploads.js.map +7 -0
- package/dist/modules/staff/lib/support-ui/portalSupport.js +51 -0
- package/dist/modules/staff/lib/support-ui/portalSupport.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/access.js +3 -39
- package/dist/modules/staff/lib/time-tracking/access.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking/listProjectAccess.js +54 -0
- package/dist/modules/staff/lib/time-tracking/listProjectAccess.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/portalRecipients.js +92 -2
- package/dist/modules/staff/lib/time-tracking/portalRecipients.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking/projectAssignment.js +112 -0
- package/dist/modules/staff/lib/time-tracking/projectAssignment.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/settingKeys.js +14 -0
- package/dist/modules/staff/lib/time-tracking/settingKeys.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking/settings.js +28 -1
- package/dist/modules/staff/lib/time-tracking/settings.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking/taskAssignmentRoute.js +143 -0
- package/dist/modules/staff/lib/time-tracking/taskAssignmentRoute.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/workRecipients.js +92 -0
- package/dist/modules/staff/lib/time-tracking/workRecipients.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/BoardFilterChips.js +32 -3
- package/dist/modules/staff/lib/time-tracking-ui/BoardFilterChips.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/KanbanBoard.js +29 -0
- package/dist/modules/staff/lib/time-tracking-ui/KanbanBoard.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/KanbanCard.js +4 -0
- package/dist/modules/staff/lib/time-tracking-ui/KanbanCard.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/ProjectFormSections.js +141 -1
- package/dist/modules/staff/lib/time-tracking-ui/ProjectFormSections.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/SupportAttachmentsSection.js +153 -0
- package/dist/modules/staff/lib/time-tracking-ui/SupportAttachmentsSection.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/SupportRequestPanel.js +67 -0
- package/dist/modules/staff/lib/time-tracking-ui/SupportRequestPanel.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskBoardScreen.js +3 -1
- package/dist/modules/staff/lib/time-tracking-ui/TaskBoardScreen.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/TaskCommentThread.js +80 -17
- package/dist/modules/staff/lib/time-tracking-ui/TaskCommentThread.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/TaskDrawer.js +230 -41
- package/dist/modules/staff/lib/time-tracking-ui/TaskDrawer.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/boardDirectory.js +21 -1
- package/dist/modules/staff/lib/time-tracking-ui/boardDirectory.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/boardFilters.js +16 -4
- package/dist/modules/staff/lib/time-tracking-ui/boardFilters.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/kanbanBoardData.js +3 -1
- package/dist/modules/staff/lib/time-tracking-ui/kanbanBoardData.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/projectDefaultAssignee.js +68 -0
- package/dist/modules/staff/lib/time-tracking-ui/projectDefaultAssignee.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/settingsTabs.js +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/settingsTabs.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/supportTicketData.js +50 -0
- package/dist/modules/staff/lib/time-tracking-ui/supportTicketData.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/taskDrawerData.js +13 -1
- package/dist/modules/staff/lib/time-tracking-ui/taskDrawerData.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.js +36 -2
- package/dist/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.js.map +2 -2
- package/dist/modules/staff/migrations/Migration20261009180543_staff.js +42 -0
- package/dist/modules/staff/migrations/Migration20261009180543_staff.js.map +7 -0
- package/dist/modules/staff/notifications.js +92 -0
- package/dist/modules/staff/notifications.js.map +2 -2
- package/dist/modules/staff/setup.js +6 -2
- package/dist/modules/staff/setup.js.map +2 -2
- package/dist/modules/staff/subscribers/support-portal-comment-created.js +26 -0
- package/dist/modules/staff/subscribers/support-portal-comment-created.js.map +7 -0
- package/dist/modules/staff/subscribers/support-portal-comment-deleted.js +26 -0
- package/dist/modules/staff/subscribers/support-portal-comment-deleted.js.map +7 -0
- package/dist/modules/staff/subscribers/support-portal-comment-updated.js +26 -0
- package/dist/modules/staff/subscribers/support-portal-comment-updated.js.map +7 -0
- package/dist/modules/staff/subscribers/support-portal-status-column-updated.js +24 -0
- package/dist/modules/staff/subscribers/support-portal-status-column-updated.js.map +7 -0
- package/dist/modules/staff/subscribers/support-portal-task-status-changed.js +25 -0
- package/dist/modules/staff/subscribers/support-portal-task-status-changed.js.map +7 -0
- package/dist/modules/staff/subscribers/support-portal-task-updated.js +25 -0
- package/dist/modules/staff/subscribers/support-portal-task-updated.js.map +7 -0
- package/dist/modules/staff/subscribers/support-ticket-created-notification.js +25 -0
- package/dist/modules/staff/subscribers/support-ticket-created-notification.js.map +7 -0
- package/dist/modules/staff/subscribers/support-ticket-customer-replied-notification.js +25 -0
- package/dist/modules/staff/subscribers/support-ticket-customer-replied-notification.js.map +7 -0
- package/dist/modules/staff/subscribers/support-ticket-unnotified-clear.js +26 -0
- package/dist/modules/staff/subscribers/support-ticket-unnotified-clear.js.map +7 -0
- package/dist/modules/staff/subscribers/time-task-assignee-notification.js +83 -0
- package/dist/modules/staff/subscribers/time-task-assignee-notification.js.map +7 -0
- package/dist/modules/staff/widgets/injection/portal-support-tickets/widget.client.js +112 -0
- package/dist/modules/staff/widgets/injection/portal-support-tickets/widget.client.js.map +7 -0
- package/dist/modules/staff/widgets/injection/portal-support-tickets/widget.js +17 -0
- package/dist/modules/staff/widgets/injection/portal-support-tickets/widget.js.map +7 -0
- package/dist/modules/staff/widgets/injection/time-tracking-settings-support/widget.client.js +870 -0
- package/dist/modules/staff/widgets/injection/time-tracking-settings-support/widget.client.js.map +7 -0
- package/dist/modules/staff/widgets/injection/time-tracking-settings-support/widget.js +17 -0
- package/dist/modules/staff/widgets/injection/time-tracking-settings-support/widget.js.map +7 -0
- package/dist/modules/staff/widgets/injection-table.js +16 -1
- package/dist/modules/staff/widgets/injection-table.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsLocationDetailPage.js +4 -18
- package/dist/modules/wms/components/backend/WmsLocationDetailPage.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsLotDetailPage.js +4 -18
- package/dist/modules/wms/components/backend/WmsLotDetailPage.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsSkuDetailPage.js +4 -18
- package/dist/modules/wms/components/backend/WmsSkuDetailPage.js.map +2 -2
- package/dist/modules/wms/lib/inventoryCsvExport.js +19 -0
- package/dist/modules/wms/lib/inventoryCsvExport.js.map +7 -0
- package/generated/entities/staff_support_routing_rule/index.ts +9 -0
- package/generated/entities/staff_support_ticket/index.ts +17 -0
- package/generated/entities/staff_time_project/index.ts +1 -0
- package/generated/entities/staff_time_project_member/index.ts +1 -0
- package/generated/entities/staff_time_task/index.ts +2 -0
- package/generated/entities/staff_time_task_comment/index.ts +4 -0
- package/generated/entities.ids.generated.ts +2 -0
- package/generated/entity-fields-registry.ts +38 -0
- package/package.json +8 -8
- package/src/helpers/integration/auth.ts +1 -1
- package/src/helpers/integration/communicationChannelsFixtures.ts +60 -2
- package/src/modules/catalog/api/bulk-delete/route.ts +12 -2
- package/src/modules/communication_channels/api/post/test-seed/route.ts +29 -12
- package/src/modules/communication_channels/lib/test-seed.ts +49 -10
- package/src/modules/messages/commands/messages.ts +8 -10
- package/src/modules/portal/frontend/[orgSlug]/portal/dashboard/page.tsx +2 -2
- package/src/modules/staff/AGENTS.md +22 -6
- package/src/modules/staff/api/portal/support-tickets/[id]/attachments/route.ts +327 -0
- package/src/modules/staff/api/portal/support-tickets/[id]/messages/route.ts +94 -0
- package/src/modules/staff/api/portal/support-tickets/[id]/route.ts +160 -0
- package/src/modules/staff/api/portal/support-tickets/options/route.ts +60 -0
- package/src/modules/staff/api/portal/support-tickets/route.ts +253 -0
- package/src/modules/staff/api/timesheets/settings/route.ts +7 -1
- package/src/modules/staff/api/timesheets/support-routing-rules/fallback-project/route.ts +131 -0
- package/src/modules/staff/api/timesheets/support-routing-rules/preview/route.ts +248 -0
- package/src/modules/staff/api/timesheets/support-routing-rules/route.ts +150 -0
- package/src/modules/staff/api/timesheets/support-tickets/options/route.ts +181 -0
- package/src/modules/staff/api/timesheets/tasks/[id]/claim/route.ts +31 -0
- package/src/modules/staff/api/timesheets/tasks/[id]/comments/route.ts +25 -7
- package/src/modules/staff/api/timesheets/tasks/[id]/release/route.ts +30 -0
- package/src/modules/staff/api/timesheets/tasks/route.ts +119 -67
- package/src/modules/staff/api/timesheets/time-projects/[id]/employees/route.ts +3 -0
- package/src/modules/staff/api/timesheets/time-projects/route.ts +3 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.tsx +1 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx +67 -2
- package/src/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.ts +22 -3
- package/src/modules/staff/backend/staff/time-tracking/settings/page.tsx +405 -308
- package/src/modules/staff/backend/staff/time-tracking/support/page.meta.ts +14 -0
- package/src/modules/staff/backend/staff/time-tracking/support/page.tsx +428 -0
- package/src/modules/staff/cli.ts +28 -0
- package/src/modules/staff/commands/index.ts +3 -0
- package/src/modules/staff/commands/shared.ts +22 -1
- package/src/modules/staff/commands/support-routing-rules.ts +595 -0
- package/src/modules/staff/commands/support-tickets.ts +580 -0
- package/src/modules/staff/commands/timesheets-projects.ts +84 -13
- package/src/modules/staff/commands/timesheets-task-assignment.ts +319 -0
- package/src/modules/staff/commands/timesheets-task-comments.ts +70 -15
- package/src/modules/staff/commands/timesheets-tasks.ts +106 -15
- package/src/modules/staff/data/enrichers.ts +203 -0
- package/src/modules/staff/data/entities.ts +133 -2
- package/src/modules/staff/data/extensions.ts +9 -1
- package/src/modules/staff/data/validators.ts +36 -0
- package/src/modules/staff/encryption.ts +6 -0
- package/src/modules/staff/events.payloads.ts +87 -1
- package/src/modules/staff/events.ts +26 -0
- package/src/modules/staff/extension-points.ts +1 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.meta.ts +10 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.tsx +553 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/support/new/page.meta.ts +10 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/support/new/page.tsx +346 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/support/page.meta.ts +17 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/support/page.tsx +253 -0
- package/src/modules/staff/i18n/de.json +291 -0
- package/src/modules/staff/i18n/en.json +291 -0
- package/src/modules/staff/i18n/es.json +291 -0
- package/src/modules/staff/i18n/ko.json +291 -0
- package/src/modules/staff/i18n/pl.json +291 -0
- package/src/modules/staff/index.ts +1 -1
- package/src/modules/staff/lib/crud.ts +2 -1
- package/src/modules/staff/lib/support/attachments.ts +43 -0
- package/src/modules/staff/lib/support/dictionaries.ts +120 -0
- package/src/modules/staff/lib/support/notifications.ts +138 -0
- package/src/modules/staff/lib/support/portalAttachments.ts +140 -0
- package/src/modules/staff/lib/support/portalBroadcast.ts +161 -0
- package/src/modules/staff/lib/support/portalRoute.ts +108 -0
- package/src/modules/staff/lib/support/portalStatus.ts +15 -0
- package/src/modules/staff/lib/support/portalTickets.ts +233 -0
- package/src/modules/staff/lib/support/routing.ts +117 -0
- package/src/modules/staff/lib/support/routingRuleDraft.ts +45 -0
- package/src/modules/staff/lib/support/seedDefaults.ts +151 -0
- package/src/modules/staff/lib/support/supportActivity.ts +39 -0
- package/src/modules/staff/lib/support-ui/PortalSupportNotLinked.tsx +20 -0
- package/src/modules/staff/lib/support-ui/portalAttachmentValidation.ts +125 -0
- package/src/modules/staff/lib/support-ui/portalPendingUploads.ts +49 -0
- package/src/modules/staff/lib/support-ui/portalSupport.ts +116 -0
- package/src/modules/staff/lib/time-tracking/access.ts +2 -62
- package/src/modules/staff/lib/time-tracking/listProjectAccess.ts +78 -0
- package/src/modules/staff/lib/time-tracking/portalRecipients.ts +173 -5
- package/src/modules/staff/lib/time-tracking/projectAssignment.ts +172 -0
- package/src/modules/staff/lib/time-tracking/settingKeys.ts +11 -0
- package/src/modules/staff/lib/time-tracking/settings.ts +46 -1
- package/src/modules/staff/lib/time-tracking/taskAssignmentRoute.ts +165 -0
- package/src/modules/staff/lib/time-tracking/workRecipients.ts +183 -0
- package/src/modules/staff/lib/time-tracking-ui/BoardFilterChips.tsx +36 -1
- package/src/modules/staff/lib/time-tracking-ui/KanbanBoard.tsx +30 -0
- package/src/modules/staff/lib/time-tracking-ui/KanbanCard.tsx +9 -0
- package/src/modules/staff/lib/time-tracking-ui/ProjectFormSections.tsx +185 -6
- package/src/modules/staff/lib/time-tracking-ui/SupportAttachmentsSection.tsx +188 -0
- package/src/modules/staff/lib/time-tracking-ui/SupportRequestPanel.tsx +102 -0
- package/src/modules/staff/lib/time-tracking-ui/TaskBoardScreen.tsx +2 -0
- package/src/modules/staff/lib/time-tracking-ui/TaskCommentThread.tsx +105 -22
- package/src/modules/staff/lib/time-tracking-ui/TaskDrawer.tsx +278 -43
- package/src/modules/staff/lib/time-tracking-ui/boardDirectory.ts +21 -0
- package/src/modules/staff/lib/time-tracking-ui/boardFilters.ts +16 -2
- package/src/modules/staff/lib/time-tracking-ui/kanbanBoardData.ts +4 -0
- package/src/modules/staff/lib/time-tracking-ui/projectDefaultAssignee.ts +117 -0
- package/src/modules/staff/lib/time-tracking-ui/settingsTabs.ts +17 -0
- package/src/modules/staff/lib/time-tracking-ui/supportTicketData.ts +71 -0
- package/src/modules/staff/lib/time-tracking-ui/taskDrawerData.ts +15 -0
- package/src/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.ts +48 -1
- package/src/modules/staff/migrations/.snapshot-open-mercato.json +714 -0
- package/src/modules/staff/migrations/Migration20261009180543_staff.ts +50 -0
- package/src/modules/staff/notifications.ts +92 -0
- package/src/modules/staff/setup.ts +6 -2
- package/src/modules/staff/subscribers/support-portal-comment-created.ts +35 -0
- package/src/modules/staff/subscribers/support-portal-comment-deleted.ts +35 -0
- package/src/modules/staff/subscribers/support-portal-comment-updated.ts +35 -0
- package/src/modules/staff/subscribers/support-portal-status-column-updated.ts +36 -0
- package/src/modules/staff/subscribers/support-portal-task-status-changed.ts +34 -0
- package/src/modules/staff/subscribers/support-portal-task-updated.ts +34 -0
- package/src/modules/staff/subscribers/support-ticket-created-notification.ts +27 -0
- package/src/modules/staff/subscribers/support-ticket-customer-replied-notification.ts +27 -0
- package/src/modules/staff/subscribers/support-ticket-unnotified-clear.ts +29 -0
- package/src/modules/staff/subscribers/time-task-assignee-notification.ts +102 -0
- package/src/modules/staff/widgets/injection/portal-support-tickets/widget.client.tsx +151 -0
- package/src/modules/staff/widgets/injection/portal-support-tickets/widget.ts +16 -0
- package/src/modules/staff/widgets/injection/time-tracking-settings-support/widget.client.tsx +1058 -0
- package/src/modules/staff/widgets/injection/time-tracking-settings-support/widget.ts +17 -0
- package/src/modules/staff/widgets/injection-table.ts +15 -0
- package/src/modules/wms/components/backend/WmsLocationDetailPage.tsx +4 -20
- package/src/modules/wms/components/backend/WmsLotDetailPage.tsx +4 -20
- package/src/modules/wms/components/backend/WmsSkuDetailPage.tsx +4 -19
- package/src/modules/wms/lib/inventoryCsvExport.ts +18 -0
|
@@ -265,8 +265,9 @@ export async function seedInboundMessage(
|
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
/**
|
|
268
|
-
* Best-effort hard delete of the rows one
|
|
269
|
-
* (thread mapping, channel link,
|
|
268
|
+
* Best-effort hard delete of the rows one seeded or test-ingested inbound message
|
|
269
|
+
* created (thread mapping, channel link, external message, conversation and
|
|
270
|
+
* platform message). Run it before
|
|
270
271
|
* {@link deleteChannelIfExists}: the purge is authorized against the live channel,
|
|
271
272
|
* and disconnecting a channel deliberately retains its conversations and messages.
|
|
272
273
|
* Safe to call with null ids in `finally`.
|
|
@@ -341,6 +342,63 @@ export async function ingestInboundChatMessage(
|
|
|
341
342
|
};
|
|
342
343
|
}
|
|
343
344
|
|
|
345
|
+
/**
|
|
346
|
+
* Ingest an inbound email through the real `ingest_inbound_message` command on a
|
|
347
|
+
* channel connected with the default `email` flavor. The stub adapter turns the
|
|
348
|
+
* frame into an email-shaped inbound message whose sender is `senderAddress`, so
|
|
349
|
+
* the hub derives `externalEmail` from it and composes the platform message via
|
|
350
|
+
* `messages.messages.compose` exactly as a real IMAP/Gmail/Graph adapter would.
|
|
351
|
+
*
|
|
352
|
+
* Use this — not {@link seedInboundMessage}, which bypasses compose — for any
|
|
353
|
+
* assertion about what the hub does with an inbound email after ingest (for
|
|
354
|
+
* example that it is not echoed back to its sender, #6089). Prefix the supplied
|
|
355
|
+
* external conversation id with `inbound-seed:` when the test will use
|
|
356
|
+
* {@link purgeSeededInboundMessage} for teardown.
|
|
357
|
+
*/
|
|
358
|
+
export async function ingestInboundEmailMessage(
|
|
359
|
+
request: APIRequestContext,
|
|
360
|
+
token: string,
|
|
361
|
+
input: {
|
|
362
|
+
channelId: string;
|
|
363
|
+
senderAddress: string;
|
|
364
|
+
senderDisplayName?: string;
|
|
365
|
+
subject?: string;
|
|
366
|
+
body?: string;
|
|
367
|
+
externalMessageId: string;
|
|
368
|
+
externalConversationId: string;
|
|
369
|
+
},
|
|
370
|
+
): Promise<{
|
|
371
|
+
status: string;
|
|
372
|
+
messageId: string | null;
|
|
373
|
+
conversationId: string | null;
|
|
374
|
+
channelLinkId: string | null;
|
|
375
|
+
channelType: string | null;
|
|
376
|
+
}> {
|
|
377
|
+
const { senderAddress, ...rest } = input;
|
|
378
|
+
const response = await apiRequest(request, 'POST', TEST_SEED_PATH, {
|
|
379
|
+
token,
|
|
380
|
+
data: { action: 'ingest-inbound', senderIdentifier: senderAddress, ...rest },
|
|
381
|
+
});
|
|
382
|
+
expect(
|
|
383
|
+
response.status(),
|
|
384
|
+
'POST /api/communication_channels/test-seed (ingest-inbound, email) should return 201',
|
|
385
|
+
).toBe(201);
|
|
386
|
+
const body = await readJsonSafe<{
|
|
387
|
+
status?: string;
|
|
388
|
+
messageId?: string | null;
|
|
389
|
+
conversationId?: string | null;
|
|
390
|
+
channelLinkId?: string | null;
|
|
391
|
+
channelType?: string | null;
|
|
392
|
+
}>(response);
|
|
393
|
+
return {
|
|
394
|
+
status: body?.status ?? 'unknown',
|
|
395
|
+
messageId: body?.messageId ?? null,
|
|
396
|
+
conversationId: body?.conversationId ?? null,
|
|
397
|
+
channelLinkId: body?.channelLinkId ?? null,
|
|
398
|
+
channelType: body?.channelType ?? null,
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
344
402
|
export type InspectedMessageLinks = {
|
|
345
403
|
links: Array<{
|
|
346
404
|
id: string;
|
|
@@ -2,6 +2,7 @@ import { NextResponse } from 'next/server'
|
|
|
2
2
|
import { z } from 'zod'
|
|
3
3
|
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
4
4
|
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
5
|
+
import { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'
|
|
5
6
|
import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
|
|
6
7
|
import type { ProgressService } from '../../../progress/lib/progressService'
|
|
7
8
|
import {
|
|
@@ -52,6 +53,15 @@ export async function POST(req: Request) {
|
|
|
52
53
|
const ids = Array.from(new Set(parsed.data.ids))
|
|
53
54
|
const container = await createRequestContainer()
|
|
54
55
|
const progressService = container.resolve('progressService') as ProgressService
|
|
56
|
+
const orgScope = await resolveOrganizationScopeForRequest({ container, auth, request: req })
|
|
57
|
+
if (orgScope?.selectionRejected) {
|
|
58
|
+
return NextResponse.json(responseSchema.parse({
|
|
59
|
+
ok: false,
|
|
60
|
+
progressJobId: null,
|
|
61
|
+
message: 'Your selected organization is no longer available. Please re-select an organization and try again.',
|
|
62
|
+
}), { status: 422 })
|
|
63
|
+
}
|
|
64
|
+
const organizationId = orgScope?.selectedId ?? auth.orgId
|
|
55
65
|
|
|
56
66
|
const progressJob = await progressService.createJob(
|
|
57
67
|
{
|
|
@@ -69,7 +79,7 @@ export async function POST(req: Request) {
|
|
|
69
79
|
},
|
|
70
80
|
{
|
|
71
81
|
tenantId: auth.tenantId,
|
|
72
|
-
organizationId
|
|
82
|
+
organizationId,
|
|
73
83
|
userId: auth.sub,
|
|
74
84
|
},
|
|
75
85
|
)
|
|
@@ -79,7 +89,7 @@ export async function POST(req: Request) {
|
|
|
79
89
|
progressJobId: progressJob.id,
|
|
80
90
|
ids,
|
|
81
91
|
scope: {
|
|
82
|
-
organizationId
|
|
92
|
+
organizationId,
|
|
83
93
|
tenantId: auth.tenantId,
|
|
84
94
|
userId: auth.sub,
|
|
85
95
|
},
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
ChannelThreadMapping,
|
|
14
14
|
CommunicationChannel,
|
|
15
15
|
ExternalConversation,
|
|
16
|
+
ExternalMessage,
|
|
16
17
|
MessageChannelLink,
|
|
17
18
|
} from '../../../data/entities'
|
|
18
19
|
import { ChannelAccessDeniedError, assertCanManageChannel } from '../../../lib/access-control'
|
|
@@ -64,8 +65,8 @@ import {
|
|
|
64
65
|
* the inbound auto-link tests (TC-CRM-EMAIL-002..005). NOTE: this action
|
|
65
66
|
* deliberately bypasses `messages.messages.compose` — it can never prove that
|
|
66
67
|
* the hub accepts a message, only that downstream subscribers fire.
|
|
67
|
-
* - `purge-inbound`: hard-delete the rows one
|
|
68
|
-
* integration teardown.
|
|
68
|
+
* - `purge-inbound`: hard-delete the rows one test-seeded or test-ingested
|
|
69
|
+
* inbound message created, for integration teardown.
|
|
69
70
|
*/
|
|
70
71
|
type RbacServiceLike = {
|
|
71
72
|
loadAcl: (
|
|
@@ -137,9 +138,15 @@ const connectChannelSchema = z.object({
|
|
|
137
138
|
const ingestInboundSchema = z.object({
|
|
138
139
|
action: z.literal('ingest-inbound'),
|
|
139
140
|
channelId: z.string().uuid(),
|
|
140
|
-
/**
|
|
141
|
+
/**
|
|
142
|
+
* Opaque sender handle — a Discord snowflake, a Slack member id, etc. For a
|
|
143
|
+
* channel connected with the default `email` flavor this is the sender's
|
|
144
|
+
* address, and the email stub normalizes it into an email-shaped frame.
|
|
145
|
+
*/
|
|
141
146
|
senderIdentifier: z.string().min(1).max(255),
|
|
142
147
|
senderDisplayName: z.string().max(255).optional(),
|
|
148
|
+
/** Honored by the email stub only; chat frames carry no subject. */
|
|
149
|
+
subject: z.string().max(500).optional(),
|
|
143
150
|
body: z.string().max(50_000).optional(),
|
|
144
151
|
externalMessageId: z.string().min(1).max(255),
|
|
145
152
|
externalConversationId: z.string().min(1).max(255),
|
|
@@ -197,11 +204,11 @@ const emitInboundSchema = z.object({
|
|
|
197
204
|
})
|
|
198
205
|
|
|
199
206
|
/**
|
|
200
|
-
* Teardown for
|
|
201
|
-
* mapping, channel link, synthetic conversation and
|
|
202
|
-
* to the caller's channel. Disconnecting a channel
|
|
203
|
-
*
|
|
204
|
-
* integration database.
|
|
207
|
+
* Teardown for test-seeded or test-ingested inbound records: hard-delete the
|
|
208
|
+
* thread mapping, channel link, external message, synthetic conversation and
|
|
209
|
+
* platform message, scoped to the caller's channel. Disconnecting a channel
|
|
210
|
+
* deliberately retains these records, so without this every test leaks into the
|
|
211
|
+
* shared integration database.
|
|
205
212
|
*/
|
|
206
213
|
const purgeInboundSchema = z.object({
|
|
207
214
|
action: z.literal('purge-inbound'),
|
|
@@ -529,8 +536,8 @@ export async function POST(req: Request): Promise<Response> {
|
|
|
529
536
|
|
|
530
537
|
if (body.action === 'purge-inbound') {
|
|
531
538
|
const rowScope = { tenantId, organizationId }
|
|
532
|
-
// Only rows hanging off a
|
|
533
|
-
//
|
|
539
|
+
// Only rows hanging off a test-prefixed conversation are purgeable, so a
|
|
540
|
+
// caller cannot use this action to hard-delete real channel traffic.
|
|
534
541
|
const seededConversation = await findOneWithDecryption(
|
|
535
542
|
em,
|
|
536
543
|
ExternalConversation,
|
|
@@ -556,6 +563,11 @@ export async function POST(req: Request): Promise<Response> {
|
|
|
556
563
|
externalConversationId: body.conversationId,
|
|
557
564
|
...rowScope,
|
|
558
565
|
})
|
|
566
|
+
await em.nativeDelete(ExternalMessage, {
|
|
567
|
+
conversationId: body.conversationId,
|
|
568
|
+
channelId: body.channelId,
|
|
569
|
+
...rowScope,
|
|
570
|
+
})
|
|
559
571
|
await em.nativeDelete(ExternalConversation, {
|
|
560
572
|
id: body.conversationId,
|
|
561
573
|
channelId: body.channelId,
|
|
@@ -564,6 +576,7 @@ export async function POST(req: Request): Promise<Response> {
|
|
|
564
576
|
await deleteTestSeedPlatformMessage(em, {
|
|
565
577
|
messageId: body.messageId,
|
|
566
578
|
channelId: body.channelId,
|
|
579
|
+
conversationId: body.conversationId,
|
|
567
580
|
...rowScope,
|
|
568
581
|
})
|
|
569
582
|
return NextResponse.json({ ok: true })
|
|
@@ -579,11 +592,14 @@ export async function POST(req: Request): Promise<Response> {
|
|
|
579
592
|
// check that `providerKey` matches the channel it names, so passing a
|
|
580
593
|
// different one would silently stamp the wrong provider onto the link.
|
|
581
594
|
const channelProviderKey = ownedChannel.providerKey
|
|
582
|
-
if (
|
|
595
|
+
if (
|
|
596
|
+
channelProviderKey !== TEST_SEED_CHAT_PROVIDER_KEY
|
|
597
|
+
&& channelProviderKey !== TEST_SEED_PROVIDER_KEY
|
|
598
|
+
) {
|
|
583
599
|
return NextResponse.json(
|
|
584
600
|
{
|
|
585
601
|
error:
|
|
586
|
-
'ingest-inbound requires a channel connected
|
|
602
|
+
'ingest-inbound requires a channel connected through the test-seed stub; ' +
|
|
587
603
|
`channel ${body.channelId} is '${channelProviderKey}'`,
|
|
588
604
|
},
|
|
589
605
|
{ status: 422 },
|
|
@@ -610,6 +626,7 @@ export async function POST(req: Request): Promise<Response> {
|
|
|
610
626
|
externalConversationId: body.externalConversationId,
|
|
611
627
|
senderIdentifier: body.senderIdentifier,
|
|
612
628
|
senderDisplayName: body.senderDisplayName,
|
|
629
|
+
...(body.subject !== undefined ? { subject: body.subject } : {}),
|
|
613
630
|
body: body.body ?? '',
|
|
614
631
|
},
|
|
615
632
|
eventType: 'message',
|
|
@@ -143,15 +143,16 @@ export async function createTestSeedPlatformMessage(
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
/**
|
|
146
|
-
* Hard-delete a
|
|
147
|
-
*
|
|
148
|
-
*
|
|
146
|
+
* Hard-delete a platform message created by the test-seed harness. Matches only
|
|
147
|
+
* the direct seed stamp or a test-prefixed conversation already verified by the
|
|
148
|
+
* test-seed route, so integration teardown cannot remove real traffic.
|
|
149
149
|
*/
|
|
150
150
|
export async function deleteTestSeedPlatformMessage(
|
|
151
151
|
em: EntityManager,
|
|
152
152
|
input: {
|
|
153
153
|
messageId: string
|
|
154
154
|
channelId: string
|
|
155
|
+
conversationId: string
|
|
155
156
|
tenantId: string
|
|
156
157
|
organizationId: string | null
|
|
157
158
|
},
|
|
@@ -159,11 +160,20 @@ export async function deleteTestSeedPlatformMessage(
|
|
|
159
160
|
await em.getConnection().execute(
|
|
160
161
|
`DELETE FROM messages
|
|
161
162
|
WHERE id = ?
|
|
162
|
-
AND
|
|
163
|
-
|
|
163
|
+
AND (
|
|
164
|
+
(source_entity_type = 'communication_channels.test_seed_inbound' AND source_entity_id = ?)
|
|
165
|
+
OR
|
|
166
|
+
(source_entity_type = 'communication_channels.external_conversation' AND source_entity_id = ?)
|
|
167
|
+
)
|
|
164
168
|
AND tenant_id = ?
|
|
165
169
|
AND organization_id IS NOT DISTINCT FROM ?`,
|
|
166
|
-
[
|
|
170
|
+
[
|
|
171
|
+
input.messageId,
|
|
172
|
+
input.channelId,
|
|
173
|
+
input.conversationId,
|
|
174
|
+
input.tenantId,
|
|
175
|
+
input.organizationId,
|
|
176
|
+
],
|
|
167
177
|
)
|
|
168
178
|
}
|
|
169
179
|
|
|
@@ -341,10 +351,39 @@ class TestSeedChannelAdapter implements ChannelAdapter {
|
|
|
341
351
|
}
|
|
342
352
|
}
|
|
343
353
|
|
|
344
|
-
async normalizeInbound(
|
|
345
|
-
//
|
|
346
|
-
//
|
|
347
|
-
|
|
354
|
+
async normalizeInbound(raw: InboundMessage): Promise<NormalizedInboundMessage> {
|
|
355
|
+
// Email-shaped frame for the test-seed `ingest-inbound` action: the sender is
|
|
356
|
+
// an address, so the message travels the REAL ingest path for an email
|
|
357
|
+
// channel — `ingest_inbound_message` derives `externalEmail` from it and
|
|
358
|
+
// composes through `messages.messages.compose` with `sendViaEmail: false`.
|
|
359
|
+
// That is the only way a test can prove the hub does not echo inbound mail
|
|
360
|
+
// back to its sender (#6089); `emit-inbound` bypasses compose entirely.
|
|
361
|
+
const frame = (raw.raw ?? {}) as Record<string, unknown>
|
|
362
|
+
const senderIdentifier = String(frame.senderIdentifier ?? '').trim()
|
|
363
|
+
if (!senderIdentifier.includes('@')) {
|
|
364
|
+
throw new Error('[internal] TestSeedChannelAdapter requires an email-shaped senderIdentifier')
|
|
365
|
+
}
|
|
366
|
+
const subject = typeof frame.subject === 'string' ? frame.subject : undefined
|
|
367
|
+
const body = typeof frame.body === 'string' ? frame.body : ''
|
|
368
|
+
const senderDisplayName =
|
|
369
|
+
typeof frame.senderDisplayName === 'string' ? frame.senderDisplayName : undefined
|
|
370
|
+
return {
|
|
371
|
+
externalMessageId: String(frame.externalMessageId ?? ''),
|
|
372
|
+
externalConversationId: String(frame.externalConversationId ?? ''),
|
|
373
|
+
senderIdentifier,
|
|
374
|
+
senderDisplayName,
|
|
375
|
+
subject,
|
|
376
|
+
body,
|
|
377
|
+
bodyFormat: 'text',
|
|
378
|
+
timestamp: new Date(),
|
|
379
|
+
channelPayload: {
|
|
380
|
+
from: { address: senderIdentifier, ...(senderDisplayName ? { name: senderDisplayName } : {}) },
|
|
381
|
+
...(subject !== undefined ? { subject } : {}),
|
|
382
|
+
text: body,
|
|
383
|
+
},
|
|
384
|
+
channelContentType: 'text/plain',
|
|
385
|
+
channelMetadata: {},
|
|
386
|
+
}
|
|
348
387
|
}
|
|
349
388
|
|
|
350
389
|
async validateCredentials(_input: ValidateCredentialsInput): Promise<ValidateCredentialsResult> {
|
|
@@ -322,14 +322,13 @@ const composeMessageCommand: CommandHandler<unknown, { id: string; threadId: str
|
|
|
322
322
|
)?.threadId ?? input.parentMessageId
|
|
323
323
|
: undefined
|
|
324
324
|
|
|
325
|
-
|
|
326
|
-
//
|
|
327
|
-
//
|
|
328
|
-
//
|
|
329
|
-
//
|
|
330
|
-
//
|
|
331
|
-
|
|
332
|
-
const sendViaEmail = isPublicVisibility && !input.inboundFromChannel ? true : input.sendViaEmail
|
|
325
|
+
// Honor the caller's explicit `sendViaEmail` (#6089, #6090). The messages API
|
|
326
|
+
// route forces email delivery for public messages a user composes; internal
|
|
327
|
+
// callers (communication_channels inbound ingest, send-as-user) compose
|
|
328
|
+
// `visibility: 'public'` with `sendViaEmail: false` on purpose. Forcing it
|
|
329
|
+
// here echoed every inbound email back to its sender through the tenant
|
|
330
|
+
// system email channel and made send-as-user deliver twice.
|
|
331
|
+
const sendViaEmail = input.sendViaEmail
|
|
333
332
|
const message = trx.create(Message, {
|
|
334
333
|
type: input.type,
|
|
335
334
|
visibility: input.visibility ?? null,
|
|
@@ -435,8 +434,7 @@ const composeMessageCommand: CommandHandler<unknown, { id: string; threadId: str
|
|
|
435
434
|
messageId,
|
|
436
435
|
senderUserId: input.userId,
|
|
437
436
|
recipientUserIds: input.recipients.map((recipient) => recipient.userId),
|
|
438
|
-
sendViaEmail:
|
|
439
|
-
input.visibility === 'public' && !input.inboundFromChannel ? true : input.sendViaEmail,
|
|
437
|
+
sendViaEmail: input.sendViaEmail,
|
|
440
438
|
inboundFromChannel: input.inboundFromChannel === true,
|
|
441
439
|
externalEmail: responseExternalEmail,
|
|
442
440
|
tenantId: input.tenantId,
|
|
@@ -121,7 +121,7 @@ export default function PortalDashboardPage({ params }: Props) {
|
|
|
121
121
|
const isHidden = hiddenWidgets.has(widget.metadata.id)
|
|
122
122
|
return (
|
|
123
123
|
<Button key={widget.metadata.id} type="button" variant={isHidden ? 'outline' : 'default'} size="sm" className="rounded-lg text-sm" onClick={() => toggleWidget(widget.metadata.id)}>
|
|
124
|
-
{widget.metadata.title
|
|
124
|
+
{widget.metadata.title ? t(widget.metadata.title, widget.metadata.title) : widget.metadata.id}
|
|
125
125
|
</Button>
|
|
126
126
|
)
|
|
127
127
|
})}
|
|
@@ -136,7 +136,7 @@ export default function PortalDashboardPage({ params }: Props) {
|
|
|
136
136
|
if (!WidgetComponent) return null
|
|
137
137
|
return (
|
|
138
138
|
<PortalCard key={widget.metadata.id}>
|
|
139
|
-
<PortalCardHeader title={widget.metadata.title
|
|
139
|
+
<PortalCardHeader title={widget.metadata.title ? t(widget.metadata.title, widget.metadata.title) : widget.metadata.id} />
|
|
140
140
|
<WidgetComponent context={{ orgSlug: params.orgSlug, user, roles: auth.roles, resolvedFeatures: auth.resolvedFeatures }} />
|
|
141
141
|
</PortalCard>
|
|
142
142
|
)
|
|
@@ -407,7 +407,8 @@ Pinned by `lib/time-tracking-ui/__tests__/TimeEntryDialog.test.tsx` →
|
|
|
407
407
|
| `staff.time_task.board:column-header` | `lib/time-tracking-ui/KanbanColumn.tsx` | `{ statusId, statusName, isDone, total, loggedMinutes }` |
|
|
408
408
|
| `staff.time_task.board:{card-badges,card-footer}` | `lib/time-tracking-ui/KanbanCard.tsx` | `{ entityId, recordId, taskId, timeProjectId, taskStatusId, timerRunning }` |
|
|
409
409
|
| `staff.my_work:{before-sections,after-sections}` | `backend/staff/time-tracking/page.tsx` | `{ staffMemberId, today, projectIds, retryLastMutation }` |
|
|
410
|
-
| `staff.time_tracking.settings:sections` | `backend/staff/time-tracking/settings/page.tsx` | `{ moduleId, canManage }` |
|
|
410
|
+
| `staff.time_tracking.settings:sections` | `backend/staff/time-tracking/settings/page.tsx` | `{ moduleId, canManage, keys, values, setValue }` |
|
|
411
|
+
| `staff.time_tracking.settings:tabs` | `backend/staff/time-tracking/settings/page.tsx` | `{ moduleId, canManage, settings, onSettingsSaved }` — `placement: { kind: 'tab' }`; a tab owns its saves (writes built from `context.settings`, reported via `onSettingsSaved`) |
|
|
411
412
|
| `staff.timesheets.timer-bar:actions` | `lib/timesheets-ui/TimerBar.tsx` | `{ staffMemberId, retryLastMutation }` |
|
|
412
413
|
|
|
413
414
|
No context carries a rate, cost or amount; contributions that need money must ask for it
|
|
@@ -646,9 +647,10 @@ columns time tracking already carried:
|
|
|
646
647
|
the query engine only ever looks a link up by `base`. **These are strings, never
|
|
647
648
|
imports.** Staff is slated for extraction into a standalone package, so
|
|
648
649
|
`data/extensions.ts` may not import a `customers` or `sales` symbol and
|
|
649
|
-
`requires
|
|
650
|
-
`
|
|
651
|
-
|
|
650
|
+
`requires` in [`index.ts`](./index.ts) (`['planner', 'resources', 'dictionaries']`;
|
|
651
|
+
`dictionaries` backs the support-ticket categories) must never gain `customers`, `sales` or
|
|
652
|
+
`customer_accounts`; `__tests__/entityExtensions.test.ts` fails if either happens, and pins
|
|
653
|
+
every table and column against the migration snapshots.
|
|
652
654
|
|
|
653
655
|
**Declaration-only, exactly as in `apps/mercato/src/modules/example/data/extensions.ts`.**
|
|
654
656
|
Nothing in the platform passes `includeExtensions` to a query, the basic engine's
|
|
@@ -820,6 +822,20 @@ The CLI enqueues onto the same queue with the same `ProgressJob` the settings
|
|
|
820
822
|
screen uses; it does not do the work in process. An unknown `--hook` id refuses to
|
|
821
823
|
start rather than silently doing nothing.
|
|
822
824
|
|
|
825
|
+
## Customer support tickets
|
|
826
|
+
|
|
827
|
+
Design of record: [`.ai/specs/2026-10-06-staff-helpdesk-support-tickets.md`](../../../../../.ai/specs/2026-10-06-staff-helpdesk-support-tickets.md).
|
|
828
|
+
|
|
829
|
+
- **A ticket is a task.** `staff_time_tasks.source = 'portal'` plus `customer_id` (the ticket owner — never the project's customer), and a 1:1 `staff_support_tickets` row (same-module FK `task_id`) holding every helpdesk-only field. The customer's original request lives in the encrypted `customer_request_snapshot`; the portal reads it and NEVER the staff-editable task title/description.
|
|
830
|
+
- **Routing** (`lib/support/routing.ts`): per-category `staff_support_routing_rules` override the organization fallback rule (`category_value IS NULL`, exactly one, cannot be deleted or paused). Paused, unusable (completed/deleted/no `isDefault` status) or orphaned overrides fall back. Categories are the per-org dictionary `staff.support_ticket_category` (exact org, no ancestors). `seedDefaults` seeds categories, the "Customer support" project and the fallback rule; existing tenants run `mercato staff seed-support-defaults`.
|
|
831
|
+
- **Settings UI**: categories, routing and the reference prefix are the "Support" tab (`widgets/injection/time-tracking-settings-support`) of `/backend/staff/time-tracking/settings`, mounted on the `staff.time_tracking.settings:tabs` spot. A tab saves on its own; when it writes the settings record it must build the body from `context.settings` and call `context.onSettingsSaved(response)` so the page's draft is rebased, not overwritten.
|
|
832
|
+
- **Backoffice reads for agents**: `GET /api/staff/timesheets/support-tickets/options` (`staff.timesheets.tasks.view`) serves the Support Inbox filters (categories, project-scoped assignees); tasks carry `_staffAssignment: { canClaim, canRelease }`, computed with the claim command's predicate. Never derive claimability client-side.
|
|
833
|
+
- **Portal routes** `/api/staff/portal/support-tickets/*` (options, list, create, detail, messages, attachments) resolve identity via dynamic import (no static `customer_accounts` edge) and load every row WITH `portalOwnedTaskWhere` (`lib/support/portalTickets.ts`). Features `portal.support_tickets.view|manage` are customer features granted via `defaultCustomerRoleFeatures`, not `acl.ts`. Attachments load the attachments module dynamically; customer uploads carry `customer-visible` + `customer-upload:<customerUserId>` tags and only the uploader may delete.
|
|
834
|
+
- **Conversation**: comment `visibility` defaults to `internal`; `customer` is refused (422) on non-portal tasks; `author_kind = 'customer'` comments are immutable for staff, `manage_all` included. Comment CRUD events carry `portalVisible`.
|
|
835
|
+
- **Events**: `staff.support_ticket.created|customer_replied` are server-side only (they name the customer); `activity_recorded` is the id-only client broadcast (also emitted with `kind: attachment_added|attachment_removed` plus the changed `attachmentId` — which keeps rapid uploads distinct for the browser bridge's 500 ms duplicate filter — by the portal attachments route via `lib/support/supportActivity.ts`, so an open TaskDrawer refetches "Ticket files", whose list is a drawer query invalidated with task/comments); `portal_updated` is emitted only by `lib/support/portalBroadcast.ts` with pinned `recipientUserIds` and never with an empty list. `staff.timesheets.time_task.assignee_changed` (client broadcast, ids only, `changeKind`) is emitted by claim/release/manual edits and the portal default assignee.
|
|
836
|
+
- **Assigners and default assignee**: `staff_time_project_members.is_assigner` (requires a linked user) grants claim (`POST /api/staff/timesheets/tasks/{id}/claim`, conditional update, 409 on a lost race) and release. `staff_time_projects.default_assignee_staff_member_id` applies to portal tickets ONLY — staff task creation keeps US-C1 (omitted or `null` → creator).
|
|
837
|
+
- **Notification chain** (`lib/time-tracking/workRecipients.ts`): type audience → active assigners → owner → `createForFeature('staff.timesheets.projects.manage', restrictRecipientsToOrganization: true)`. The actor is excluded in steps 1–3 only. A support notification that reaches nobody sets `staff_support_tickets.unnotified_at` (cleared on assignment).
|
|
838
|
+
|
|
823
839
|
## Internal-Only Surfaces (NOT public contract)
|
|
824
840
|
|
|
825
841
|
These are subject to change without deprecation; do not import them from non-staff code:
|
|
@@ -836,9 +852,9 @@ If you need data from staff in another core module, the correct path is:
|
|
|
836
852
|
|
|
837
853
|
## Dependencies
|
|
838
854
|
|
|
839
|
-
Staff currently declares `requires: ['planner', 'resources']` in [`index.ts`](./index.ts). The dependency direction is intentional and asymmetric:
|
|
855
|
+
Staff currently declares `requires: ['planner', 'resources', 'dictionaries']` in [`index.ts`](./index.ts). The dependency direction is intentional and asymmetric:
|
|
840
856
|
|
|
841
|
-
- Staff depends on planner + resources (hard requirement at load time).
|
|
857
|
+
- Staff depends on planner + resources (hard requirement at load time), and on dictionaries for the support-ticket categories.
|
|
842
858
|
- Planner soft-resolves `availabilityAccessResolver` via DI with `allowUnregistered: true` (graceful degradation when staff is absent).
|
|
843
859
|
|
|
844
860
|
This asymmetry will be reconciled in the Phase 2/3 follow-up when staff becomes its own npm package; for now, planner is the only consumer that must work without staff registered.
|