@hed-hog/operations 0.0.330 → 0.0.332
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/README.md +5 -5
- package/dist/controllers/operations-collaborators.controller.d.ts +58 -213
- package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -1
- package/dist/controllers/operations-collaborators.controller.js +100 -0
- package/dist/controllers/operations-collaborators.controller.js.map +1 -1
- package/dist/controllers/operations-contracts.controller.d.ts +6 -6
- package/dist/controllers/operations-projects.controller.d.ts +25 -0
- package/dist/controllers/operations-projects.controller.d.ts.map +1 -1
- package/dist/controllers/operations-projects.controller.js +48 -0
- package/dist/controllers/operations-projects.controller.js.map +1 -1
- package/dist/controllers/operations-reports.controller.d.ts +1 -1
- package/dist/controllers/operations-tasks.controller.d.ts +34 -9
- package/dist/controllers/operations-tasks.controller.d.ts.map +1 -1
- package/dist/controllers/operations-tasks.controller.js +43 -32
- package/dist/controllers/operations-tasks.controller.js.map +1 -1
- package/dist/controllers/operations-timesheets.controller.d.ts +9 -9
- package/dist/dashboard/components/DashboardLayout.d.ts +30 -0
- package/dist/dashboard/components/DashboardLayout.d.ts.map +1 -0
- package/dist/dashboard/components/DashboardLayout.js +87 -0
- package/dist/dashboard/components/DashboardLayout.js.map +1 -0
- package/dist/dashboard/components/widget-registry.d.ts +23 -0
- package/dist/dashboard/components/widget-registry.d.ts.map +1 -0
- package/dist/dashboard/components/widget-registry.js +245 -0
- package/dist/dashboard/components/widget-registry.js.map +1 -0
- package/dist/dashboard/hooks/useDashboardData.d.ts +20 -0
- package/dist/dashboard/hooks/useDashboardData.d.ts.map +1 -0
- package/dist/dashboard/hooks/useDashboardData.js +24 -0
- package/dist/dashboard/hooks/useDashboardData.js.map +1 -0
- package/dist/dashboard/types/widgets.types.d.ts +233 -0
- package/dist/dashboard/types/widgets.types.d.ts.map +1 -0
- package/dist/dashboard/types/widgets.types.js +6 -0
- package/dist/dashboard/types/widgets.types.js.map +1 -0
- package/dist/dashboard/widgets/CapacityDistribution.d.ts +23 -0
- package/dist/dashboard/widgets/CapacityDistribution.d.ts.map +1 -0
- package/dist/dashboard/widgets/CapacityDistribution.js +11 -0
- package/dist/dashboard/widgets/CapacityDistribution.js.map +1 -0
- package/dist/dashboard/widgets/EffortByProject.d.ts +22 -0
- package/dist/dashboard/widgets/EffortByProject.d.ts.map +1 -0
- package/dist/dashboard/widgets/EffortByProject.js +11 -0
- package/dist/dashboard/widgets/EffortByProject.js.map +1 -0
- package/dist/dashboard/widgets/HeadcountByArea.d.ts +24 -0
- package/dist/dashboard/widgets/HeadcountByArea.d.ts.map +1 -0
- package/dist/dashboard/widgets/HeadcountByArea.js +11 -0
- package/dist/dashboard/widgets/HeadcountByArea.js.map +1 -0
- package/dist/dashboard/widgets/ManagedProjectsStatus.d.ts +18 -0
- package/dist/dashboard/widgets/ManagedProjectsStatus.d.ts.map +1 -0
- package/dist/dashboard/widgets/ManagedProjectsStatus.js +12 -0
- package/dist/dashboard/widgets/ManagedProjectsStatus.js.map +1 -0
- package/dist/dashboard/widgets/MyHoursPeriodKpi.d.ts +22 -0
- package/dist/dashboard/widgets/MyHoursPeriodKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/MyHoursPeriodKpi.js +12 -0
- package/dist/dashboard/widgets/MyHoursPeriodKpi.js.map +1 -0
- package/dist/dashboard/widgets/MyOpenRequestsKpi.d.ts +19 -0
- package/dist/dashboard/widgets/MyOpenRequestsKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/MyOpenRequestsKpi.js +17 -0
- package/dist/dashboard/widgets/MyOpenRequestsKpi.js.map +1 -0
- package/dist/dashboard/widgets/MyPendingRequestsList.d.ts +23 -0
- package/dist/dashboard/widgets/MyPendingRequestsList.d.ts.map +1 -0
- package/dist/dashboard/widgets/MyPendingRequestsList.js +14 -0
- package/dist/dashboard/widgets/MyPendingRequestsList.js.map +1 -0
- package/dist/dashboard/widgets/MyProjectAllocationsKpi.d.ts +22 -0
- package/dist/dashboard/widgets/MyProjectAllocationsKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/MyProjectAllocationsKpi.js +11 -0
- package/dist/dashboard/widgets/MyProjectAllocationsKpi.js.map +1 -0
- package/dist/dashboard/widgets/MyQuickActions.d.ts +23 -0
- package/dist/dashboard/widgets/MyQuickActions.d.ts.map +1 -0
- package/dist/dashboard/widgets/MyQuickActions.js +18 -0
- package/dist/dashboard/widgets/MyQuickActions.js.map +1 -0
- package/dist/dashboard/widgets/MyRelevantDeadlines.d.ts +23 -0
- package/dist/dashboard/widgets/MyRelevantDeadlines.d.ts.map +1 -0
- package/dist/dashboard/widgets/MyRelevantDeadlines.js +22 -0
- package/dist/dashboard/widgets/MyRelevantDeadlines.js.map +1 -0
- package/dist/dashboard/widgets/MyTimesheetStatusKpi.d.ts +17 -0
- package/dist/dashboard/widgets/MyTimesheetStatusKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/MyTimesheetStatusKpi.js +11 -0
- package/dist/dashboard/widgets/MyTimesheetStatusKpi.js.map +1 -0
- package/dist/dashboard/widgets/MyWeeklyJourney.d.ts +21 -0
- package/dist/dashboard/widgets/MyWeeklyJourney.d.ts.map +1 -0
- package/dist/dashboard/widgets/MyWeeklyJourney.js +19 -0
- package/dist/dashboard/widgets/MyWeeklyJourney.js.map +1 -0
- package/dist/dashboard/widgets/PortfolioCostsKpi.d.ts +19 -0
- package/dist/dashboard/widgets/PortfolioCostsKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/PortfolioCostsKpi.js +12 -0
- package/dist/dashboard/widgets/PortfolioCostsKpi.js.map +1 -0
- package/dist/dashboard/widgets/PortfolioEffortKpi.d.ts +18 -0
- package/dist/dashboard/widgets/PortfolioEffortKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/PortfolioEffortKpi.js +8 -0
- package/dist/dashboard/widgets/PortfolioEffortKpi.js.map +1 -0
- package/dist/dashboard/widgets/PortfolioProjectsKpi.d.ts +22 -0
- package/dist/dashboard/widgets/PortfolioProjectsKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/PortfolioProjectsKpi.js +56 -0
- package/dist/dashboard/widgets/PortfolioProjectsKpi.js.map +1 -0
- package/dist/dashboard/widgets/PortfolioRiskKpi.d.ts +19 -0
- package/dist/dashboard/widgets/PortfolioRiskKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/PortfolioRiskKpi.js +11 -0
- package/dist/dashboard/widgets/PortfolioRiskKpi.js.map +1 -0
- package/dist/dashboard/widgets/ProjectStatusOverview.d.ts +19 -0
- package/dist/dashboard/widgets/ProjectStatusOverview.d.ts.map +1 -0
- package/dist/dashboard/widgets/ProjectStatusOverview.js +18 -0
- package/dist/dashboard/widgets/ProjectStatusOverview.js.map +1 -0
- package/dist/dashboard/widgets/StrategicDeadlines.d.ts +24 -0
- package/dist/dashboard/widgets/StrategicDeadlines.d.ts.map +1 -0
- package/dist/dashboard/widgets/StrategicDeadlines.js +22 -0
- package/dist/dashboard/widgets/StrategicDeadlines.js.map +1 -0
- package/dist/dashboard/widgets/TeamApprovalQueue.d.ts +24 -0
- package/dist/dashboard/widgets/TeamApprovalQueue.d.ts.map +1 -0
- package/dist/dashboard/widgets/TeamApprovalQueue.js +12 -0
- package/dist/dashboard/widgets/TeamApprovalQueue.js.map +1 -0
- package/dist/dashboard/widgets/TeamCapacityKpi.d.ts +18 -0
- package/dist/dashboard/widgets/TeamCapacityKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/TeamCapacityKpi.js +19 -0
- package/dist/dashboard/widgets/TeamCapacityKpi.js.map +1 -0
- package/dist/dashboard/widgets/TeamHeadcountKpi.d.ts +22 -0
- package/dist/dashboard/widgets/TeamHeadcountKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/TeamHeadcountKpi.js +56 -0
- package/dist/dashboard/widgets/TeamHeadcountKpi.js.map +1 -0
- package/dist/dashboard/widgets/TeamHoursKpi.d.ts +19 -0
- package/dist/dashboard/widgets/TeamHoursKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/TeamHoursKpi.js +13 -0
- package/dist/dashboard/widgets/TeamHoursKpi.js.map +1 -0
- package/dist/dashboard/widgets/TeamPendingApprovalsKpi.d.ts +20 -0
- package/dist/dashboard/widgets/TeamPendingApprovalsKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/TeamPendingApprovalsKpi.js +11 -0
- package/dist/dashboard/widgets/TeamPendingApprovalsKpi.js.map +1 -0
- package/dist/dashboard/widgets/TeamUtilizationOverview.d.ts +18 -0
- package/dist/dashboard/widgets/TeamUtilizationOverview.d.ts.map +1 -0
- package/dist/dashboard/widgets/TeamUtilizationOverview.js +17 -0
- package/dist/dashboard/widgets/TeamUtilizationOverview.js.map +1 -0
- package/dist/dashboard/widgets/TeamWorkloadAlerts.d.ts +24 -0
- package/dist/dashboard/widgets/TeamWorkloadAlerts.d.ts.map +1 -0
- package/dist/dashboard/widgets/TeamWorkloadAlerts.js +19 -0
- package/dist/dashboard/widgets/TeamWorkloadAlerts.js.map +1 -0
- package/dist/dashboard/widgets/index.d.ts +24 -0
- package/dist/dashboard/widgets/index.d.ts.map +1 -0
- package/dist/dashboard/widgets/index.js +54 -0
- package/dist/dashboard/widgets/index.js.map +1 -0
- package/dist/dto/create-collaborator-invoice.dto.d.ts +11 -0
- package/dist/dto/create-collaborator-invoice.dto.d.ts.map +1 -0
- package/dist/dto/create-collaborator-invoice.dto.js +55 -0
- package/dist/dto/create-collaborator-invoice.dto.js.map +1 -0
- package/dist/dto/create-collaborator-payment.dto.d.ts +10 -0
- package/dist/dto/create-collaborator-payment.dto.d.ts.map +1 -0
- package/dist/dto/create-collaborator-payment.dto.js +50 -0
- package/dist/dto/create-collaborator-payment.dto.js.map +1 -0
- package/dist/dto/create-collaborator.dto.d.ts +0 -1
- package/dist/dto/create-collaborator.dto.d.ts.map +1 -1
- package/dist/dto/create-collaborator.dto.js +0 -6
- package/dist/dto/create-collaborator.dto.js.map +1 -1
- package/dist/dto/list-collaborator-invoice.dto.d.ts +4 -0
- package/dist/dto/list-collaborator-invoice.dto.d.ts.map +1 -0
- package/dist/dto/list-collaborator-invoice.dto.js +8 -0
- package/dist/dto/list-collaborator-invoice.dto.js.map +1 -0
- package/dist/dto/list-collaborator-payment.dto.d.ts +4 -0
- package/dist/dto/list-collaborator-payment.dto.d.ts.map +1 -0
- package/dist/dto/list-collaborator-payment.dto.js +8 -0
- package/dist/dto/list-collaborator-payment.dto.js.map +1 -0
- package/dist/dto/update-collaborator-invoice.dto.d.ts +6 -0
- package/dist/dto/update-collaborator-invoice.dto.d.ts.map +1 -0
- package/dist/dto/update-collaborator-invoice.dto.js +9 -0
- package/dist/dto/update-collaborator-invoice.dto.js.map +1 -0
- package/dist/dto/update-collaborator-payment.dto.d.ts +6 -0
- package/dist/dto/update-collaborator-payment.dto.d.ts.map +1 -0
- package/dist/dto/update-collaborator-payment.dto.js +9 -0
- package/dist/dto/update-collaborator-payment.dto.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/operations.controller.d.ts +42 -0
- package/dist/operations.controller.d.ts.map +1 -1
- package/dist/operations.service.d.ts +258 -268
- package/dist/operations.service.d.ts.map +1 -1
- package/dist/operations.service.js +2381 -1341
- package/dist/operations.service.js.map +1 -1
- package/dist/operations.service.spec.js +345 -174
- package/dist/operations.service.spec.js.map +1 -1
- package/hedhog/data/dashboard_component.yaml +66 -0
- package/hedhog/data/dashboard_item.yaml +25 -25
- package/hedhog/data/menu.yaml +27 -8
- package/hedhog/data/route.yaml +133 -0
- package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +78 -102
- package/hedhog/frontend/app/_components/collaborator-invoices-tab.tsx.ejs +443 -0
- package/hedhog/frontend/app/_components/collaborator-payment-history-tab.tsx.ejs +429 -0
- package/hedhog/frontend/app/_components/collaborator-picker.tsx.ejs +158 -0
- package/hedhog/frontend/app/_components/my-project-summary-screen.tsx.ejs +247 -50
- package/hedhog/frontend/app/_components/project-assignments-tab.tsx.ejs +643 -450
- package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +992 -431
- package/hedhog/frontend/app/_components/project-file-attachments.tsx.ejs +371 -0
- package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +558 -386
- package/hedhog/frontend/app/_components/task-detail-sheet.tsx.ejs +383 -157
- package/hedhog/frontend/app/_components/task-file-attachments.tsx.ejs +4 -1
- package/hedhog/frontend/app/_components/task-form-fields.tsx.ejs +406 -0
- package/hedhog/frontend/app/_components/task-form-sheet.tsx.ejs +629 -784
- package/hedhog/frontend/app/_components/task-info-display.tsx.ejs +137 -0
- package/hedhog/frontend/app/_components/timesheet-entry-create-sheet.tsx.ejs +306 -0
- package/hedhog/frontend/app/_lib/api.ts.ejs +155 -0
- package/hedhog/frontend/app/_lib/types.ts.ejs +62 -0
- package/hedhog/frontend/app/_lib/utils/format.ts.ejs +0 -2
- package/hedhog/frontend/app/_lib/utils/task-ui.ts.ejs +61 -0
- package/hedhog/frontend/app/approvals/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/collaborator-types/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/collaborators/page.tsx.ejs +59 -8
- package/hedhog/frontend/app/contracts/page.tsx.ejs +29 -8
- package/hedhog/frontend/app/dashboard/widgets/CapacityDistribution.tsx.ejs +84 -0
- package/hedhog/frontend/app/dashboard/widgets/EffortByProject.tsx.ejs +85 -0
- package/hedhog/frontend/app/dashboard/widgets/HeadcountByArea.tsx.ejs +101 -0
- package/hedhog/frontend/app/dashboard/widgets/ManagedProjectsStatus.tsx.ejs +113 -0
- package/hedhog/frontend/app/dashboard/widgets/MyHoursPeriodKpi.tsx.ejs +87 -0
- package/hedhog/frontend/app/dashboard/widgets/MyOpenRequestsKpi.tsx.ejs +97 -0
- package/hedhog/frontend/app/dashboard/widgets/MyPendingRequestsList.tsx.ejs +99 -0
- package/hedhog/frontend/app/dashboard/widgets/MyProjectAllocationsKpi.tsx.ejs +78 -0
- package/hedhog/frontend/app/dashboard/widgets/MyQuickActions.tsx.ejs +130 -0
- package/hedhog/frontend/app/dashboard/widgets/MyRelevantDeadlines.tsx.ejs +144 -0
- package/hedhog/frontend/app/dashboard/widgets/MyTimesheetStatusKpi.tsx.ejs +78 -0
- package/hedhog/frontend/app/dashboard/widgets/MyWeeklyJourney.tsx.ejs +99 -0
- package/hedhog/frontend/app/dashboard/widgets/PortfolioCostsKpi.tsx.ejs +112 -0
- package/hedhog/frontend/app/dashboard/widgets/PortfolioEffortKpi.tsx.ejs +93 -0
- package/hedhog/frontend/app/dashboard/widgets/PortfolioProjectsKpi.tsx.ejs +96 -0
- package/hedhog/frontend/app/dashboard/widgets/PortfolioRiskKpi.tsx.ejs +115 -0
- package/hedhog/frontend/app/dashboard/widgets/ProjectStatusOverview.tsx.ejs +120 -0
- package/hedhog/frontend/app/dashboard/widgets/StrategicDeadlines.tsx.ejs +146 -0
- package/hedhog/frontend/app/dashboard/widgets/TeamApprovalQueue.tsx.ejs +108 -0
- package/hedhog/frontend/app/dashboard/widgets/TeamCapacityKpi.tsx.ejs +97 -0
- package/hedhog/frontend/app/dashboard/widgets/TeamHeadcountKpi.tsx.ejs +100 -0
- package/hedhog/frontend/app/dashboard/widgets/TeamHoursKpi.tsx.ejs +104 -0
- package/hedhog/frontend/app/dashboard/widgets/TeamPendingApprovalsKpi.tsx.ejs +110 -0
- package/hedhog/frontend/app/dashboard/widgets/TeamUtilizationOverview.tsx.ejs +115 -0
- package/hedhog/frontend/app/dashboard/widgets/TeamWorkloadAlerts.tsx.ejs +117 -0
- package/hedhog/frontend/app/dashboard/widgets/index.ts.ejs +26 -0
- package/hedhog/frontend/app/departments/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/my-projects/page.tsx.ejs +30 -12
- package/hedhog/frontend/app/my-tasks/page.tsx.ejs +286 -125
- package/hedhog/frontend/app/project-cost-categories/page.tsx.ejs +58 -52
- package/hedhog/frontend/app/project-cost-types/page.tsx.ejs +58 -51
- package/hedhog/frontend/app/projects/page.tsx.ejs +415 -33
- package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/tasks-gantt/page.tsx.ejs +953 -0
- package/hedhog/frontend/app/time-off/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/timesheets/page.tsx.ejs +10 -4
- package/hedhog/frontend/messages/en.json +332 -46
- package/hedhog/frontend/messages/operations/en.json +61 -52
- package/hedhog/frontend/messages/operations/pt.json +59 -43
- package/hedhog/frontend/messages/pt.json +332 -46
- package/hedhog/frontend/widgets/capacity-distribution.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/effort-by-project.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/headcount-by-area.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/index.ts.ejs +25 -0
- package/hedhog/frontend/widgets/managed-projects-status.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/my-hours-period-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/my-open-requests-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/my-pending-requests-list.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/my-project-allocations-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/my-quick-actions.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/my-relevant-deadlines.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/my-timesheet-status-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/my-weekly-journey.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/portfolio-costs-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/portfolio-effort-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/portfolio-projects-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/portfolio-risk-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/project-status-overview.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/shared-operations-widget.tsx.ejs +170 -0
- package/hedhog/frontend/widgets/strategic-deadlines.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/team-approval-queue.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/team-capacity-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/team-headcount-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/team-hours-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/team-pending-approvals-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/team-utilization-overview.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/team-workload-alerts.tsx.ejs +17 -0
- package/hedhog/table/operations_collaborator.yaml +8 -13
- package/hedhog/table/operations_collaborator_invoice.yaml +35 -0
- package/hedhog/table/operations_collaborator_payment.yaml +32 -0
- package/hedhog/table/operations_project.yaml +1 -1
- package/hedhog/table/operations_project_file.yaml +23 -0
- package/hedhog/table/operations_task.yaml +76 -69
- package/hedhog/table/operations_task_activity.yaml +51 -0
- package/package.json +6 -5
- package/src/controllers/operations-collaborators.controller.ts +117 -8
- package/src/controllers/operations-projects.controller.ts +41 -8
- package/src/controllers/operations-tasks.controller.ts +156 -166
- package/src/dashboard/README.md +214 -0
- package/src/dashboard/components/DashboardLayout.tsx +131 -0
- package/src/dashboard/components/widget-registry.ts +255 -0
- package/src/dashboard/hooks/useDashboardData.ts +29 -0
- package/src/dashboard/types/widgets.types.ts +237 -0
- package/src/dashboard/widgets/CapacityDistribution.tsx +56 -0
- package/src/dashboard/widgets/EffortByProject.tsx +51 -0
- package/src/dashboard/widgets/HeadcountByArea.tsx +57 -0
- package/src/dashboard/widgets/ManagedProjectsStatus.tsx +53 -0
- package/src/dashboard/widgets/MyHoursPeriodKpi.tsx +87 -0
- package/src/dashboard/widgets/MyOpenRequestsKpi.tsx +51 -0
- package/src/dashboard/widgets/MyPendingRequestsList.tsx +63 -0
- package/src/dashboard/widgets/MyProjectAllocationsKpi.tsx +57 -0
- package/src/dashboard/widgets/MyQuickActions.tsx +62 -0
- package/src/dashboard/widgets/MyRelevantDeadlines.tsx +84 -0
- package/src/dashboard/widgets/MyTimesheetStatusKpi.tsx +65 -0
- package/src/dashboard/widgets/MyWeeklyJourney.tsx +57 -0
- package/src/dashboard/widgets/PortfolioCostsKpi.tsx +48 -0
- package/src/dashboard/widgets/PortfolioEffortKpi.tsx +41 -0
- package/src/dashboard/widgets/PortfolioRiskKpi.tsx +50 -0
- package/src/dashboard/widgets/ProjectStatusOverview.tsx +52 -0
- package/src/dashboard/widgets/StrategicDeadlines.tsx +93 -0
- package/src/dashboard/widgets/TeamApprovalQueue.tsx +70 -0
- package/src/dashboard/widgets/TeamCapacityKpi.tsx +50 -0
- package/src/dashboard/widgets/TeamHoursKpi.tsx +51 -0
- package/src/dashboard/widgets/TeamPendingApprovalsKpi.tsx +53 -0
- package/src/dashboard/widgets/TeamUtilizationOverview.tsx +62 -0
- package/src/dashboard/widgets/TeamWorkloadAlerts.tsx +81 -0
- package/src/dashboard/widgets/index.ts +26 -0
- package/src/dto/create-collaborator-invoice.dto.ts +39 -0
- package/src/dto/create-collaborator-payment.dto.ts +35 -0
- package/src/dto/create-collaborator.dto.ts +4 -11
- package/src/dto/list-collaborator-invoice.dto.ts +3 -0
- package/src/dto/list-collaborator-payment.dto.ts +3 -0
- package/src/dto/update-collaborator-invoice.dto.ts +6 -0
- package/src/dto/update-collaborator-payment.dto.ts +6 -0
- package/src/index.ts +3 -0
- package/src/operations.service.spec.ts +988 -764
- package/src/operations.service.ts +4689 -2624
|
@@ -3,41 +3,61 @@
|
|
|
3
3
|
import { RichTextEditor } from '@/components/rich-text-editor';
|
|
4
4
|
import { Button } from '@/components/ui/button';
|
|
5
5
|
import { CommentContent } from '@/components/ui/comment-rich-editor';
|
|
6
|
+
import { KpiCardsGrid, type KpiCardItem } from '@/components/ui/kpi-cards-grid';
|
|
6
7
|
import {
|
|
7
8
|
Sheet,
|
|
8
9
|
SheetContent,
|
|
10
|
+
SheetDescription,
|
|
9
11
|
SheetHeader,
|
|
10
12
|
SheetTitle,
|
|
11
13
|
} from '@/components/ui/sheet';
|
|
12
14
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
|
13
15
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
14
16
|
import {
|
|
17
|
+
Activity,
|
|
15
18
|
AlarmClock,
|
|
16
19
|
Calendar,
|
|
20
|
+
Clock3,
|
|
21
|
+
History,
|
|
17
22
|
MessageSquare,
|
|
18
23
|
Pencil,
|
|
19
|
-
|
|
24
|
+
Timer,
|
|
20
25
|
Trash2,
|
|
21
|
-
|
|
26
|
+
Users,
|
|
22
27
|
X,
|
|
23
28
|
} from 'lucide-react';
|
|
24
29
|
import { useTranslations } from 'next-intl';
|
|
25
30
|
import type { ReactNode } from 'react';
|
|
26
|
-
import { useEffect, useRef, useState } from 'react';
|
|
31
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
27
32
|
import {
|
|
28
33
|
createTaskComment,
|
|
29
34
|
deleteTaskComment,
|
|
35
|
+
fetchTaskActivities,
|
|
30
36
|
fetchTaskComments,
|
|
31
37
|
updateTaskComment,
|
|
32
38
|
} from '../_lib/api';
|
|
33
39
|
import { useMentionItems } from '../_lib/hooks/use-mention-items';
|
|
34
|
-
import type {
|
|
35
|
-
|
|
40
|
+
import type {
|
|
41
|
+
OperationsTaskActivity,
|
|
42
|
+
OperationsTaskComment,
|
|
43
|
+
} from '../_lib/types';
|
|
44
|
+
import { formatDate, formatDateTime, getStatusBadgeClass } from '../_lib/utils/format';
|
|
45
|
+
import {
|
|
46
|
+
formatDurationMinutes,
|
|
47
|
+
getElapsedDoingMinutes,
|
|
48
|
+
getInitials,
|
|
49
|
+
getTaskPriorityLabel,
|
|
50
|
+
} from '../_lib/utils/task-ui';
|
|
51
|
+
|
|
36
52
|
import { StatusBadge } from './status-badge';
|
|
53
|
+
import { TaskFileAttachments } from './task-file-attachments';
|
|
54
|
+
import { TimesheetEntryCreateSheet } from './timesheet-entry-create-sheet';
|
|
37
55
|
|
|
38
56
|
export type TaskDetailSheetData = {
|
|
39
57
|
id: number;
|
|
40
58
|
name: string;
|
|
59
|
+
projectId?: number | null;
|
|
60
|
+
projectAssignmentId?: number | null;
|
|
41
61
|
description?: string | null;
|
|
42
62
|
status: string;
|
|
43
63
|
priority?: string | null;
|
|
@@ -49,6 +69,8 @@ export type TaskDetailSheetData = {
|
|
|
49
69
|
assigneeName?: string | null;
|
|
50
70
|
assigneeUserPhotoId?: number | null;
|
|
51
71
|
assigneePersonAvatarId?: number | null;
|
|
72
|
+
doingStartedAt?: string | null;
|
|
73
|
+
totalDoingMinutes?: number | null;
|
|
52
74
|
};
|
|
53
75
|
|
|
54
76
|
type Props = {
|
|
@@ -57,18 +79,9 @@ type Props = {
|
|
|
57
79
|
onOpenChange: (open: boolean) => void;
|
|
58
80
|
statusLabel?: (status: string) => string;
|
|
59
81
|
footer?: ReactNode;
|
|
60
|
-
defaultTab?: '
|
|
82
|
+
defaultTab?: 'comments' | 'activities';
|
|
61
83
|
};
|
|
62
84
|
|
|
63
|
-
function getPriorityLabel(value?: string | null) {
|
|
64
|
-
const labels: Record<string, string> = {
|
|
65
|
-
low: 'Baixa',
|
|
66
|
-
medium: 'Média',
|
|
67
|
-
high: 'Alta',
|
|
68
|
-
};
|
|
69
|
-
return labels[value ?? ''] ?? value ?? '—';
|
|
70
|
-
}
|
|
71
|
-
|
|
72
85
|
function getPriorityClassName(value?: string | null) {
|
|
73
86
|
if (value === 'high') return 'bg-rose-100 text-rose-700 border-rose-200';
|
|
74
87
|
if (value === 'medium') return 'bg-amber-100 text-amber-700 border-amber-200';
|
|
@@ -104,16 +117,6 @@ function getCommentAvatarSrc(comment: OperationsTaskComment) {
|
|
|
104
117
|
return null;
|
|
105
118
|
}
|
|
106
119
|
|
|
107
|
-
function getInitials(value?: string | null) {
|
|
108
|
-
const parts = String(value ?? '')
|
|
109
|
-
.trim()
|
|
110
|
-
.split(/\s+/)
|
|
111
|
-
.filter(Boolean)
|
|
112
|
-
.slice(0, 2);
|
|
113
|
-
if (!parts.length) return '??';
|
|
114
|
-
return parts.map((p) => p[0]?.toUpperCase() ?? '').join('');
|
|
115
|
-
}
|
|
116
|
-
|
|
117
120
|
function formatCommentDate(value?: string | null) {
|
|
118
121
|
if (!value) return '';
|
|
119
122
|
const date = new Date(value);
|
|
@@ -132,9 +135,10 @@ function formatCommentDate(value?: string | null) {
|
|
|
132
135
|
|
|
133
136
|
export type TaskCommentsSectionProps = {
|
|
134
137
|
taskId: number;
|
|
138
|
+
onChanged?: () => void;
|
|
135
139
|
};
|
|
136
140
|
|
|
137
|
-
export function TaskCommentsSection({ taskId }: TaskCommentsSectionProps) {
|
|
141
|
+
export function TaskCommentsSection({ taskId, onChanged }: TaskCommentsSectionProps) {
|
|
138
142
|
const { request, showToastHandler, getSettingValue } = useApp();
|
|
139
143
|
const ct = useTranslations('operations.ProjectDetailsPage.commentsSection');
|
|
140
144
|
const editWindowMinutes = Number(
|
|
@@ -156,9 +160,7 @@ export function TaskCommentsSection({ taskId }: TaskCommentsSectionProps) {
|
|
|
156
160
|
|
|
157
161
|
const mentionItems = useMentionItems(request);
|
|
158
162
|
|
|
159
|
-
const { data: fetchedComments = []
|
|
160
|
-
OperationsTaskComment[]
|
|
161
|
-
>({
|
|
163
|
+
const { data: fetchedComments = [] } = useQuery<OperationsTaskComment[]>({
|
|
162
164
|
queryKey: ['task-comments', taskId],
|
|
163
165
|
queryFn: () =>
|
|
164
166
|
fetchTaskComments(request, taskId) as Promise<OperationsTaskComment[]>,
|
|
@@ -183,8 +185,6 @@ export function TaskCommentsSection({ taskId }: TaskCommentsSectionProps) {
|
|
|
183
185
|
}
|
|
184
186
|
}
|
|
185
187
|
return () => timers.forEach(clearTimeout);
|
|
186
|
-
// Re-schedule whenever the comment list or the window setting changes
|
|
187
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
188
188
|
}, [comments, editWindowMinutes]);
|
|
189
189
|
|
|
190
190
|
const handleSubmit = async () => {
|
|
@@ -199,6 +199,7 @@ export function TaskCommentsSection({ taskId }: TaskCommentsSectionProps) {
|
|
|
199
199
|
)) as OperationsTaskComment;
|
|
200
200
|
setLocalComments([...(localComments ?? fetchedComments), created]);
|
|
201
201
|
setNewComment('');
|
|
202
|
+
onChanged?.();
|
|
202
203
|
} catch {
|
|
203
204
|
showToastHandler?.('error', ct('errors.addComment'));
|
|
204
205
|
} finally {
|
|
@@ -250,6 +251,7 @@ export function TaskCommentsSection({ taskId }: TaskCommentsSectionProps) {
|
|
|
250
251
|
setLocalComments(
|
|
251
252
|
(localComments ?? fetchedComments).filter((c) => c.id !== comment.id)
|
|
252
253
|
);
|
|
254
|
+
onChanged?.();
|
|
253
255
|
} catch {
|
|
254
256
|
showToastHandler?.('error', ct('errors.deleteComment'));
|
|
255
257
|
} finally {
|
|
@@ -380,12 +382,19 @@ export function TaskCommentsSection({ taskId }: TaskCommentsSectionProps) {
|
|
|
380
382
|
value={newComment}
|
|
381
383
|
onChange={setNewComment}
|
|
382
384
|
mentions={mentionItems}
|
|
385
|
+
onCtrlEnter={() => void handleSubmit()}
|
|
383
386
|
/>
|
|
384
|
-
<div className="flex justify-end">
|
|
387
|
+
<div className="flex items-center justify-end gap-2">
|
|
388
|
+
<span className="text-[10px] text-muted-foreground select-none">
|
|
389
|
+
<kbd className="rounded border bg-muted px-1 py-0.5 font-mono text-[10px]">Ctrl</kbd>
|
|
390
|
+
{' + '}
|
|
391
|
+
<kbd className="rounded border bg-muted px-1 py-0.5 font-mono text-[10px]">↵</kbd>
|
|
392
|
+
</span>
|
|
385
393
|
<Button
|
|
386
394
|
size="sm"
|
|
387
395
|
className="gap-1.5"
|
|
388
396
|
disabled={submitting || !newComment.replace(/<[^>]*>/g, '').trim()}
|
|
397
|
+
onMouseDown={(e) => e.preventDefault()}
|
|
389
398
|
onClick={() => void handleSubmit()}
|
|
390
399
|
>
|
|
391
400
|
{ct('submitButton')}
|
|
@@ -396,6 +405,177 @@ export function TaskCommentsSection({ taskId }: TaskCommentsSectionProps) {
|
|
|
396
405
|
);
|
|
397
406
|
}
|
|
398
407
|
|
|
408
|
+
export type TaskActivitiesSectionProps = {
|
|
409
|
+
taskId: number;
|
|
410
|
+
task?: {
|
|
411
|
+
status?: string | null;
|
|
412
|
+
doingStartedAt?: string | null;
|
|
413
|
+
totalDoingMinutes?: number | null;
|
|
414
|
+
} | null;
|
|
415
|
+
statusLabel?: (status: string) => string;
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
function getActivityAvatarSrc(activity: OperationsTaskActivity) {
|
|
419
|
+
if (
|
|
420
|
+
typeof activity.actorUserPhotoId === 'number' &&
|
|
421
|
+
activity.actorUserPhotoId > 0
|
|
422
|
+
)
|
|
423
|
+
return `${process.env.NEXT_PUBLIC_API_BASE_URL}/file/open/${activity.actorUserPhotoId}`;
|
|
424
|
+
if (
|
|
425
|
+
typeof activity.actorPersonAvatarId === 'number' &&
|
|
426
|
+
activity.actorPersonAvatarId > 0
|
|
427
|
+
)
|
|
428
|
+
return `${process.env.NEXT_PUBLIC_API_BASE_URL}/person/avatar/${activity.actorPersonAvatarId}`;
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
export function TaskActivitiesSection({
|
|
433
|
+
taskId,
|
|
434
|
+
task,
|
|
435
|
+
statusLabel,
|
|
436
|
+
}: TaskActivitiesSectionProps) {
|
|
437
|
+
const { request, getSettingValue, currentLocaleCode } = useApp();
|
|
438
|
+
const at = useTranslations('operations.ProjectDetailsPage.activitiesSection');
|
|
439
|
+
const [doingTick, setDoingTick] = useState(0);
|
|
440
|
+
|
|
441
|
+
const { data: activities = [] } = useQuery<OperationsTaskActivity[]>({
|
|
442
|
+
queryKey: ['task-activities', taskId],
|
|
443
|
+
queryFn: () =>
|
|
444
|
+
fetchTaskActivities(request, taskId) as Promise<OperationsTaskActivity[]>,
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
useEffect(() => {
|
|
448
|
+
if (task?.status !== 'doing' || !task.doingStartedAt) return;
|
|
449
|
+
const timer = setInterval(() => setDoingTick((value) => value + 1), 30000);
|
|
450
|
+
return () => clearInterval(timer);
|
|
451
|
+
}, [task?.doingStartedAt, task?.status]);
|
|
452
|
+
|
|
453
|
+
const uniqueCollaborators = useMemo(() => {
|
|
454
|
+
const collaborators = new Set<string>();
|
|
455
|
+
for (const activity of activities) {
|
|
456
|
+
if (activity.actorCollaboratorId != null) {
|
|
457
|
+
collaborators.add(`id:${activity.actorCollaboratorId}`);
|
|
458
|
+
} else if (activity.actorName) {
|
|
459
|
+
collaborators.add(`name:${activity.actorName}`);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return collaborators.size;
|
|
463
|
+
}, [activities]);
|
|
464
|
+
|
|
465
|
+
const totalDoingMinutes = getElapsedDoingMinutes(task, doingTick);
|
|
466
|
+
|
|
467
|
+
const kpiItems: KpiCardItem[] = [
|
|
468
|
+
{
|
|
469
|
+
key: 'activities',
|
|
470
|
+
title: at('kpis.activities'),
|
|
471
|
+
value: activities.length,
|
|
472
|
+
description: at('kpis.activitiesDescription'),
|
|
473
|
+
icon: Activity,
|
|
474
|
+
layout: 'compact',
|
|
475
|
+
accentClassName: 'from-sky-500/30 via-cyan-500/10 to-transparent',
|
|
476
|
+
iconContainerClassName: 'bg-sky-500/10 text-sky-700',
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
key: 'collaborators',
|
|
480
|
+
title: at('kpis.collaborators'),
|
|
481
|
+
value: uniqueCollaborators,
|
|
482
|
+
description: at('kpis.collaboratorsDescription'),
|
|
483
|
+
icon: Users,
|
|
484
|
+
layout: 'compact',
|
|
485
|
+
accentClassName: 'from-violet-500/30 via-fuchsia-500/10 to-transparent',
|
|
486
|
+
iconContainerClassName: 'bg-violet-500/10 text-violet-700',
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
key: 'doing-time',
|
|
490
|
+
title: at('kpis.executionTime'),
|
|
491
|
+
value: formatDurationMinutes(totalDoingMinutes),
|
|
492
|
+
description: at('kpis.executionTimeDescription'),
|
|
493
|
+
icon: Clock3,
|
|
494
|
+
layout: 'compact',
|
|
495
|
+
accentClassName: 'from-emerald-500/30 via-teal-500/10 to-transparent',
|
|
496
|
+
iconContainerClassName: 'bg-emerald-500/10 text-emerald-700',
|
|
497
|
+
},
|
|
498
|
+
];
|
|
499
|
+
|
|
500
|
+
const getActivityDescription = (activity: OperationsTaskActivity) => {
|
|
501
|
+
if (activity.action === 'status_changed') {
|
|
502
|
+
return at('statusChanged', {
|
|
503
|
+
from: statusLabel?.(activity.fromStatus) ?? activity.fromStatus,
|
|
504
|
+
to: statusLabel?.(activity.toStatus) ?? activity.toStatus,
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
return at('fallbackAction');
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
return (
|
|
511
|
+
<div className="flex flex-col gap-4">
|
|
512
|
+
<div className="flex items-center gap-1.5 text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
513
|
+
<History className="size-3" />
|
|
514
|
+
{at('title')}
|
|
515
|
+
{activities.length > 0 ? (
|
|
516
|
+
<span className="ml-0.5 rounded-full bg-muted px-1.5 py-0.5 text-[10px] font-semibold text-muted-foreground">
|
|
517
|
+
{activities.length}
|
|
518
|
+
</span>
|
|
519
|
+
) : null}
|
|
520
|
+
</div>
|
|
521
|
+
|
|
522
|
+
<KpiCardsGrid items={kpiItems} columns={3} className="gap-2" />
|
|
523
|
+
|
|
524
|
+
{activities.length > 0 ? (
|
|
525
|
+
<div className="relative flex flex-col">
|
|
526
|
+
{activities.map((activity, index) => {
|
|
527
|
+
const avatarSrc = getActivityAvatarSrc(activity);
|
|
528
|
+
const isLast = index === activities.length - 1;
|
|
529
|
+
return (
|
|
530
|
+
<div
|
|
531
|
+
key={activity.id}
|
|
532
|
+
className="relative flex gap-3 pb-4 last:pb-0"
|
|
533
|
+
>
|
|
534
|
+
<div className="relative flex shrink-0 justify-center">
|
|
535
|
+
{!isLast ? (
|
|
536
|
+
<span className="absolute bottom-0 top-9 w-px bg-border" />
|
|
537
|
+
) : null}
|
|
538
|
+
<div className="relative z-10 flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-full border-2 border-background bg-muted text-[11px] font-semibold uppercase text-muted-foreground shadow-sm ring-1 ring-border">
|
|
539
|
+
{avatarSrc ? (
|
|
540
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
541
|
+
<img
|
|
542
|
+
src={avatarSrc}
|
|
543
|
+
alt={activity.actorName ?? ''}
|
|
544
|
+
className="size-full object-cover"
|
|
545
|
+
/>
|
|
546
|
+
) : (
|
|
547
|
+
getInitials(activity.actorName)
|
|
548
|
+
)}
|
|
549
|
+
</div>
|
|
550
|
+
</div>
|
|
551
|
+
<div className="min-w-0 flex-1 rounded-md border bg-background px-3 py-2 shadow-sm">
|
|
552
|
+
<div className="flex flex-wrap items-start justify-between gap-x-2 gap-y-1">
|
|
553
|
+
<span className="min-w-0 truncate text-xs font-semibold">
|
|
554
|
+
{activity.actorName ?? at('defaultUser')}
|
|
555
|
+
</span>
|
|
556
|
+
<span className="shrink-0 text-[10px] font-medium text-muted-foreground">
|
|
557
|
+
{formatDateTime(
|
|
558
|
+
activity.createdAt,
|
|
559
|
+
getSettingValue,
|
|
560
|
+
currentLocaleCode
|
|
561
|
+
)}
|
|
562
|
+
</span>
|
|
563
|
+
</div>
|
|
564
|
+
<p className="mt-1.5 text-xs leading-relaxed text-muted-foreground">
|
|
565
|
+
{getActivityDescription(activity)}
|
|
566
|
+
</p>
|
|
567
|
+
</div>
|
|
568
|
+
</div>
|
|
569
|
+
);
|
|
570
|
+
})}
|
|
571
|
+
</div>
|
|
572
|
+
) : (
|
|
573
|
+
<p className="text-xs text-muted-foreground">{at('noActivities')}</p>
|
|
574
|
+
)}
|
|
575
|
+
</div>
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
|
|
399
579
|
export function TaskDetailSheet({
|
|
400
580
|
task,
|
|
401
581
|
open,
|
|
@@ -404,133 +584,143 @@ export function TaskDetailSheet({
|
|
|
404
584
|
footer,
|
|
405
585
|
defaultTab = 'comments',
|
|
406
586
|
}: Props) {
|
|
407
|
-
const commonT = useTranslations('operations.Common');
|
|
408
587
|
const detailT = useTranslations('operations.ProjectDetailsPage');
|
|
588
|
+
const commonT = useTranslations('operations.Common');
|
|
409
589
|
const { getSettingValue, currentLocaleCode } = useApp();
|
|
410
590
|
|
|
411
|
-
const [tab, setTab] = useState<'
|
|
591
|
+
const [tab, setTab] = useState<'comments' | 'activities'>(defaultTab);
|
|
592
|
+
const [isTimesheetOpen, setIsTimesheetOpen] = useState(false);
|
|
412
593
|
|
|
413
594
|
useEffect(() => {
|
|
414
|
-
if (open)
|
|
595
|
+
if (!open) {
|
|
596
|
+
setIsTimesheetOpen(false);
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
const timer = setTimeout(() => setTab(defaultTab), 0);
|
|
600
|
+
return () => clearTimeout(timer);
|
|
415
601
|
}, [open, defaultTab]);
|
|
416
602
|
|
|
417
603
|
const avatarSrc = getAvatarSrc(task);
|
|
418
604
|
|
|
605
|
+
const timesheetProject =
|
|
606
|
+
task && task.projectId
|
|
607
|
+
? {
|
|
608
|
+
id: task.projectId,
|
|
609
|
+
label:
|
|
610
|
+
[task.projectName, task.projectCode].filter(Boolean).join(' • ') ||
|
|
611
|
+
commonT('labels.notAssigned'),
|
|
612
|
+
projectAssignmentId: task.projectAssignmentId,
|
|
613
|
+
}
|
|
614
|
+
: null;
|
|
615
|
+
|
|
419
616
|
return (
|
|
420
|
-
|
|
421
|
-
<
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
<
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
<
|
|
450
|
-
{detailT('taskForm.tabInfo')}
|
|
451
|
-
</TabsTrigger>
|
|
452
|
-
<TabsTrigger value="comments">
|
|
453
|
-
<MessageSquare className="mr-1.5 size-3.5" />
|
|
454
|
-
{detailT('taskForm.tabComments')}
|
|
455
|
-
</TabsTrigger>
|
|
456
|
-
</TabsList>
|
|
457
|
-
|
|
458
|
-
<TabsContent
|
|
459
|
-
value="info"
|
|
460
|
-
className="flex-1 overflow-y-auto px-4 pb-4 pt-2 data-[state=inactive]:hidden"
|
|
461
|
-
>
|
|
462
|
-
<div className="flex flex-col gap-5">
|
|
617
|
+
<>
|
|
618
|
+
<Sheet open={open} onOpenChange={onOpenChange}>
|
|
619
|
+
<SheetContent className="flex w-full flex-col gap-0 overflow-hidden sm:max-w-5xl">
|
|
620
|
+
{task ? (
|
|
621
|
+
<>
|
|
622
|
+
<SheetHeader className="shrink-0 border-b px-5 pb-3 pt-4">
|
|
623
|
+
<div className="flex items-start justify-between gap-3 pr-8">
|
|
624
|
+
<SheetTitle className="text-base font-semibold leading-snug">
|
|
625
|
+
{task.name}
|
|
626
|
+
</SheetTitle>
|
|
627
|
+
{task.projectId ? (
|
|
628
|
+
<Button
|
|
629
|
+
variant="outline"
|
|
630
|
+
size="sm"
|
|
631
|
+
className="h-7 shrink-0 gap-1.5 text-xs"
|
|
632
|
+
onClick={() => setIsTimesheetOpen(true)}
|
|
633
|
+
>
|
|
634
|
+
<Timer className="size-3.5" />
|
|
635
|
+
{commonT('actions.logHours')}
|
|
636
|
+
</Button>
|
|
637
|
+
) : null}
|
|
638
|
+
</div>
|
|
639
|
+
<SheetDescription className="sr-only">
|
|
640
|
+
{task.description || task.name}
|
|
641
|
+
</SheetDescription>
|
|
642
|
+
</SheetHeader>
|
|
643
|
+
|
|
644
|
+
<div className="flex flex-1 min-h-0 overflow-hidden">
|
|
645
|
+
{/* ── Coluna esquerda ── */}
|
|
646
|
+
<div className="flex flex-1 flex-col min-h-0 overflow-hidden">
|
|
463
647
|
{task.description ? (
|
|
464
|
-
<div>
|
|
648
|
+
<div className="shrink-0 border-b px-5 py-4">
|
|
465
649
|
<p className="mb-1 text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
466
650
|
{detailT('taskForm.descriptionLabel')}
|
|
467
651
|
</p>
|
|
468
|
-
<
|
|
469
|
-
{task.description}
|
|
470
|
-
</p>
|
|
652
|
+
<CommentContent content={task.description} />
|
|
471
653
|
</div>
|
|
472
654
|
) : null}
|
|
473
655
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
</div>
|
|
491
|
-
</div>
|
|
492
|
-
) : null}
|
|
493
|
-
{task.estimateHours != null ? (
|
|
494
|
-
<div className="flex items-start gap-2 text-sm">
|
|
495
|
-
<AlarmClock className="mt-0.5 size-4 shrink-0 text-muted-foreground" />
|
|
496
|
-
<div>
|
|
497
|
-
<p className="text-[11px] text-muted-foreground">
|
|
498
|
-
{detailT('taskForm.estimateLabel')}
|
|
499
|
-
</p>
|
|
500
|
-
<p className="font-medium">{task.estimateHours}h</p>
|
|
501
|
-
</div>
|
|
502
|
-
</div>
|
|
503
|
-
) : null}
|
|
656
|
+
<Tabs
|
|
657
|
+
value={tab}
|
|
658
|
+
onValueChange={(v) => setTab(v as 'comments' | 'activities')}
|
|
659
|
+
className="flex flex-1 min-h-0 flex-col"
|
|
660
|
+
>
|
|
661
|
+
<div className="shrink-0 border-b px-5 py-2">
|
|
662
|
+
<TabsList className="grid grid-cols-2">
|
|
663
|
+
<TabsTrigger value="comments">
|
|
664
|
+
<MessageSquare className="mr-1.5 size-3.5" />
|
|
665
|
+
{detailT('taskForm.tabComments')}
|
|
666
|
+
</TabsTrigger>
|
|
667
|
+
<TabsTrigger value="activities">
|
|
668
|
+
<History className="mr-1.5 size-3.5" />
|
|
669
|
+
{detailT('taskForm.tabActivities')}
|
|
670
|
+
</TabsTrigger>
|
|
671
|
+
</TabsList>
|
|
504
672
|
</div>
|
|
505
|
-
|
|
673
|
+
<TabsContent
|
|
674
|
+
value="comments"
|
|
675
|
+
className="mt-0 flex-1 overflow-y-auto px-5 py-4 data-[state=inactive]:hidden"
|
|
676
|
+
>
|
|
677
|
+
<TaskCommentsSection taskId={task.id} />
|
|
678
|
+
</TabsContent>
|
|
679
|
+
<TabsContent
|
|
680
|
+
value="activities"
|
|
681
|
+
className="mt-0 flex-1 overflow-y-auto px-5 py-4 data-[state=inactive]:hidden"
|
|
682
|
+
>
|
|
683
|
+
<TaskActivitiesSection
|
|
684
|
+
taskId={task.id}
|
|
685
|
+
task={task}
|
|
686
|
+
statusLabel={statusLabel}
|
|
687
|
+
/>
|
|
688
|
+
</TabsContent>
|
|
689
|
+
</Tabs>
|
|
690
|
+
</div>
|
|
506
691
|
|
|
507
|
-
|
|
692
|
+
{/* ── Aside direito ── */}
|
|
693
|
+
<aside className="flex w-72 shrink-0 flex-col gap-4 overflow-y-auto border-l px-4 py-4">
|
|
694
|
+
<div>
|
|
695
|
+
<p className="mb-1.5 text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
696
|
+
{detailT('taskForm.columnLabel')}
|
|
697
|
+
</p>
|
|
698
|
+
<StatusBadge
|
|
699
|
+
label={statusLabel?.(task.status) ?? task.status}
|
|
700
|
+
className={getStatusBadgeClass(task.status)}
|
|
701
|
+
/>
|
|
702
|
+
</div>
|
|
703
|
+
|
|
704
|
+
{task.priority ? (
|
|
508
705
|
<div>
|
|
509
|
-
<
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
className="rounded-md bg-muted px-2 py-0.5 text-xs text-muted-foreground"
|
|
518
|
-
>
|
|
519
|
-
{tag.trim()}
|
|
520
|
-
</span>
|
|
521
|
-
))}
|
|
522
|
-
</div>
|
|
706
|
+
<p className="mb-1.5 text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
707
|
+
{detailT('taskForm.priorityLabel')}
|
|
708
|
+
</p>
|
|
709
|
+
<span
|
|
710
|
+
className={`inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold ${getPriorityClassName(task.priority)}`}
|
|
711
|
+
>
|
|
712
|
+
{getTaskPriorityLabel(task.priority)}
|
|
713
|
+
</span>
|
|
523
714
|
</div>
|
|
524
715
|
) : null}
|
|
525
716
|
|
|
526
717
|
{task.assigneeName ? (
|
|
527
718
|
<div>
|
|
528
|
-
<
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
<div className="flex size-8 shrink-0 items-center justify-center overflow-hidden rounded-full bg-muted text-xs font-semibold uppercase text-muted-foreground ring-1 ring-border">
|
|
719
|
+
<p className="mb-1.5 text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
720
|
+
{detailT('taskForm.assigneeLabel')}
|
|
721
|
+
</p>
|
|
722
|
+
<div className="flex items-center gap-2">
|
|
723
|
+
<div className="flex size-7 shrink-0 items-center justify-center overflow-hidden rounded-full bg-muted text-[10px] font-semibold uppercase text-muted-foreground ring-1 ring-border">
|
|
534
724
|
{avatarSrc ? (
|
|
535
725
|
// eslint-disable-next-line @next/next/no-img-element
|
|
536
726
|
<img
|
|
@@ -547,35 +737,71 @@ export function TaskDetailSheet({
|
|
|
547
737
|
</div>
|
|
548
738
|
) : null}
|
|
549
739
|
|
|
550
|
-
{task.
|
|
551
|
-
<div
|
|
552
|
-
<p className="text-
|
|
553
|
-
{
|
|
740
|
+
{task.dueDate ? (
|
|
741
|
+
<div>
|
|
742
|
+
<p className="mb-1.5 text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
743
|
+
{detailT('taskForm.deadlineLabel')}
|
|
554
744
|
</p>
|
|
555
|
-
<
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
.
|
|
745
|
+
<div className="flex items-center gap-1.5 text-sm">
|
|
746
|
+
<Calendar className="size-3.5 shrink-0 text-muted-foreground" />
|
|
747
|
+
<span>
|
|
748
|
+
{formatDate(task.dueDate, getSettingValue, currentLocaleCode)}
|
|
749
|
+
</span>
|
|
750
|
+
</div>
|
|
751
|
+
</div>
|
|
752
|
+
) : null}
|
|
753
|
+
|
|
754
|
+
{task.estimateHours != null ? (
|
|
755
|
+
<div>
|
|
756
|
+
<p className="mb-1.5 text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
757
|
+
{detailT('taskForm.estimateLabel')}
|
|
559
758
|
</p>
|
|
759
|
+
<div className="flex items-center gap-1.5 text-sm">
|
|
760
|
+
<AlarmClock className="size-3.5 shrink-0 text-muted-foreground" />
|
|
761
|
+
<span>{task.estimateHours}h</span>
|
|
762
|
+
</div>
|
|
560
763
|
</div>
|
|
561
764
|
) : null}
|
|
562
765
|
|
|
563
|
-
{
|
|
564
|
-
<div
|
|
766
|
+
{task.tags ? (
|
|
767
|
+
<div>
|
|
768
|
+
<p className="mb-1.5 text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
769
|
+
{detailT('taskForm.tagsLabel')}
|
|
770
|
+
</p>
|
|
771
|
+
<div className="flex flex-wrap gap-1.5">
|
|
772
|
+
{task.tags.split(',').map((tag) => (
|
|
773
|
+
<span
|
|
774
|
+
key={tag.trim()}
|
|
775
|
+
className="rounded-md bg-muted px-2 py-0.5 text-xs text-muted-foreground"
|
|
776
|
+
>
|
|
777
|
+
{tag.trim()}
|
|
778
|
+
</span>
|
|
779
|
+
))}
|
|
780
|
+
</div>
|
|
781
|
+
</div>
|
|
565
782
|
) : null}
|
|
566
|
-
</div>
|
|
567
|
-
</TabsContent>
|
|
568
783
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
784
|
+
<div className="border-t pt-2">
|
|
785
|
+
<p className="mb-2 text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
786
|
+
{detailT('taskForm.attachmentsLabel')}
|
|
787
|
+
</p>
|
|
788
|
+
<TaskFileAttachments taskId={task.id} />
|
|
789
|
+
</div>
|
|
790
|
+
</aside>
|
|
791
|
+
</div>
|
|
792
|
+
{footer ? (
|
|
793
|
+
<div className="shrink-0 border-t px-5 py-4">{footer}</div>
|
|
794
|
+
) : null}
|
|
795
|
+
</>
|
|
796
|
+
) : null}
|
|
797
|
+
</SheetContent>
|
|
798
|
+
</Sheet>
|
|
799
|
+
<TimesheetEntryCreateSheet
|
|
800
|
+
open={isTimesheetOpen}
|
|
801
|
+
onOpenChange={setIsTimesheetOpen}
|
|
802
|
+
project={timesheetProject}
|
|
803
|
+
task={task ? { id: task.id, label: task.name } : null}
|
|
804
|
+
/>
|
|
805
|
+
</>
|
|
580
806
|
);
|
|
581
807
|
}
|