@hed-hog/operations 0.0.329 → 0.0.331
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 +7 -216
- package/dist/controllers/operations-collaborators.controller.d.ts.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 +30 -5
- 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.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/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 +178 -264
- package/dist/operations.service.d.ts.map +1 -1
- package/dist/operations.service.js +2170 -1340
- 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_component_role.yaml +8 -8
- package/hedhog/data/dashboard_item.yaml +25 -25
- package/hedhog/data/dashboard_role.yaml +1 -1
- package/hedhog/data/menu.yaml +6 -16
- package/hedhog/data/role.yaml +1 -1
- package/hedhog/data/route.yaml +116 -55
- package/hedhog/frontend/app/_components/async-options-combobox.tsx.ejs +15 -9
- package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +39 -99
- package/hedhog/frontend/app/_components/collaborator-picker.tsx.ejs +158 -0
- package/hedhog/frontend/app/_components/my-project-summary-screen.tsx.ejs +314 -116
- package/hedhog/frontend/app/_components/project-assignments-tab.tsx.ejs +434 -449
- package/hedhog/frontend/app/_components/project-costs-section.tsx.ejs +51 -81
- package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +328 -423
- package/hedhog/frontend/app/_components/project-file-attachments.tsx.ejs +371 -0
- package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +446 -377
- package/hedhog/frontend/app/_components/task-detail-sheet.tsx.ejs +803 -581
- package/hedhog/frontend/app/_components/task-file-attachments.tsx.ejs +14 -9
- 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 +480 -476
- package/hedhog/frontend/app/_lib/hooks/use-values-visibility.ts.ejs +61 -0
- package/hedhog/frontend/app/_lib/types.ts.ejs +66 -5
- package/hedhog/frontend/app/_lib/utils/format.ts.ejs +0 -2
- package/hedhog/frontend/app/_lib/utils/task-ui.ts.ejs +43 -0
- package/hedhog/frontend/app/approvals/page.tsx.ejs +11 -2
- package/hedhog/frontend/app/collaborator-types/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/collaborators/page.tsx.ejs +127 -42
- 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 +59 -16
- package/hedhog/frontend/app/my-tasks/page.tsx.ejs +329 -106
- 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 +436 -35
- package/hedhog/frontend/app/reports/collaborators/page.tsx.ejs +65 -52
- package/hedhog/frontend/app/reports/projects/page.tsx.ejs +80 -82
- package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +13 -2
- 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 +460 -61
- package/hedhog/frontend/messages/operations/en.json +61 -52
- package/hedhog/frontend/messages/operations/pt.json +59 -43
- package/hedhog/frontend/messages/pt.json +460 -61
- 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_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 +7 -6
- 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.dto.ts +4 -11
- package/src/index.ts +3 -0
- package/src/operations.service.spec.ts +988 -764
- package/src/operations.service.ts +4300 -2538
|
@@ -58,6 +58,7 @@ import { ContractFormScreen } from '../_components/contract-form-screen';
|
|
|
58
58
|
import { OperationsHeader } from '../_components/operations-header';
|
|
59
59
|
import { StatusBadge } from '../_components/status-badge';
|
|
60
60
|
import { fetchOperations, mutateOperations } from '../_lib/api';
|
|
61
|
+
import { usePersistedPageSize } from '@/hooks/use-persisted-page-size';
|
|
61
62
|
import { useOperationsAccess } from '../_lib/hooks/use-operations-access';
|
|
62
63
|
import type {
|
|
63
64
|
OperationsContract,
|
|
@@ -97,7 +98,11 @@ export default function OperationsContractsPage() {
|
|
|
97
98
|
const [search, setSearch] = useState('');
|
|
98
99
|
const [isActiveFilter, setIsActiveFilter] = useState('all');
|
|
99
100
|
const [page, setPage] = useState(1);
|
|
100
|
-
const [pageSize, setPageSize] =
|
|
101
|
+
const [pageSize, setPageSize] = usePersistedPageSize({
|
|
102
|
+
storageKey: 'pagination:operations-contracts:pageSize',
|
|
103
|
+
defaultValue: 12,
|
|
104
|
+
allowedValues: [12, 24, 48],
|
|
105
|
+
});
|
|
101
106
|
const [isCreateSheetOpen, setIsCreateSheetOpen] = useState(false);
|
|
102
107
|
const [contractToDelete, setContractToDelete] =
|
|
103
108
|
useState<OperationsContract | null>(null);
|
|
@@ -159,7 +164,10 @@ export default function OperationsContractsPage() {
|
|
|
159
164
|
const { data: stats } = useQuery<OperationsContractStats>({
|
|
160
165
|
queryKey: ['operations-contracts-stats', currentLocaleCode],
|
|
161
166
|
queryFn: () =>
|
|
162
|
-
fetchOperations<OperationsContractStats>(
|
|
167
|
+
fetchOperations<OperationsContractStats>(
|
|
168
|
+
request,
|
|
169
|
+
'/operations/contracts/stats'
|
|
170
|
+
),
|
|
163
171
|
});
|
|
164
172
|
|
|
165
173
|
const rows = contractsResponse?.data ?? [];
|
|
@@ -206,7 +214,11 @@ export default function OperationsContractsPage() {
|
|
|
206
214
|
) ?? null;
|
|
207
215
|
|
|
208
216
|
if (document?.fileId) {
|
|
209
|
-
window.open(
|
|
217
|
+
window.open(
|
|
218
|
+
buildStoredFileUrl(document.fileId) || '',
|
|
219
|
+
'_blank',
|
|
220
|
+
'noopener,noreferrer'
|
|
221
|
+
);
|
|
210
222
|
return;
|
|
211
223
|
}
|
|
212
224
|
|
|
@@ -288,7 +300,9 @@ export default function OperationsContractsPage() {
|
|
|
288
300
|
</DropdownMenuItem>
|
|
289
301
|
{access.isDirector ? <DropdownMenuSeparator /> : null}
|
|
290
302
|
{access.isDirector ? (
|
|
291
|
-
<DropdownMenuItem
|
|
303
|
+
<DropdownMenuItem
|
|
304
|
+
onSelect={() => updateSheetQuery({ editId: contract.id })}
|
|
305
|
+
>
|
|
292
306
|
<Pencil className="size-4" />
|
|
293
307
|
{commonT('actions.edit')}
|
|
294
308
|
</DropdownMenuItem>
|
|
@@ -300,7 +314,9 @@ export default function OperationsContractsPage() {
|
|
|
300
314
|
) : (
|
|
301
315
|
<ShieldCheck className="size-4" />
|
|
302
316
|
)}
|
|
303
|
-
{contract.isActive
|
|
317
|
+
{contract.isActive
|
|
318
|
+
? detailT('labels.inactive')
|
|
319
|
+
: detailT('labels.active')}
|
|
304
320
|
</DropdownMenuItem>
|
|
305
321
|
) : null}
|
|
306
322
|
{access.isDirector ? <DropdownMenuSeparator /> : null}
|
|
@@ -384,7 +400,9 @@ export default function OperationsContractsPage() {
|
|
|
384
400
|
<TableRow
|
|
385
401
|
key={contract.id}
|
|
386
402
|
className="cursor-pointer hover:bg-muted/30"
|
|
387
|
-
onClick={() =>
|
|
403
|
+
onClick={() =>
|
|
404
|
+
router.push(`/operations/contracts/${contract.id}`)
|
|
405
|
+
}
|
|
388
406
|
>
|
|
389
407
|
<TableCell>
|
|
390
408
|
<div className="min-w-0">
|
|
@@ -401,7 +419,8 @@ export default function OperationsContractsPage() {
|
|
|
401
419
|
</TableCell>
|
|
402
420
|
<TableCell>
|
|
403
421
|
<span className="truncate text-sm text-muted-foreground">
|
|
404
|
-
{contract.currentPdfFileName ||
|
|
422
|
+
{contract.currentPdfFileName ||
|
|
423
|
+
commonT('labels.notAvailable')}
|
|
405
424
|
</span>
|
|
406
425
|
</TableCell>
|
|
407
426
|
<TableCell>
|
|
@@ -510,7 +529,9 @@ export default function OperationsContractsPage() {
|
|
|
510
529
|
<SheetContent className="flex h-full w-full flex-col overflow-hidden p-0 sm:max-w-xl lg:max-w-4xl">
|
|
511
530
|
<SheetHeader className="sr-only">
|
|
512
531
|
<SheetTitle>
|
|
513
|
-
{editingContractId
|
|
532
|
+
{editingContractId
|
|
533
|
+
? commonT('actions.edit')
|
|
534
|
+
: commonT('actions.create')}
|
|
514
535
|
</SheetTitle>
|
|
515
536
|
<SheetDescription>{t('description')}</SheetDescription>
|
|
516
537
|
</SheetHeader>
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { PieChart } from 'lucide-react';
|
|
4
|
+
import { useTranslations } from 'next-intl';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
interface CapacityArea {
|
|
8
|
+
name: string;
|
|
9
|
+
capacity: number;
|
|
10
|
+
percentage: number;
|
|
11
|
+
color: string;
|
|
12
|
+
icon: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface CapacityDistributionProps {
|
|
16
|
+
slug: string;
|
|
17
|
+
title: string;
|
|
18
|
+
roleSlug: string;
|
|
19
|
+
width?: number;
|
|
20
|
+
height?: number;
|
|
21
|
+
data?: { areas: CapacityArea[]; totalCapacity: number };
|
|
22
|
+
style?: React.CSSProperties;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const CapacityDistribution: React.FC<CapacityDistributionProps> = ({
|
|
26
|
+
title,
|
|
27
|
+
data = { areas: [], totalCapacity: 0 },
|
|
28
|
+
style,
|
|
29
|
+
}) => {
|
|
30
|
+
const t = useTranslations('operations.CapacityDistribution');
|
|
31
|
+
const colorMap = ['blue', 'purple', 'pink', 'green', 'yellow', 'red'];
|
|
32
|
+
return (
|
|
33
|
+
<div
|
|
34
|
+
className="bg-linear-to-br from-white to-teal-50 rounded-lg shadow-sm p-4 border border-teal-100"
|
|
35
|
+
style={style}
|
|
36
|
+
>
|
|
37
|
+
<div className="flex items-center justify-between mb-4">
|
|
38
|
+
<h3 className="text-sm font-medium text-gray-700">{title}</h3>
|
|
39
|
+
<PieChart className="w-4 h-4 text-teal-600" />
|
|
40
|
+
</div>
|
|
41
|
+
<div className="space-y-3">
|
|
42
|
+
{data.areas.length === 0 ? (
|
|
43
|
+
<div className="text-center py-6 text-gray-500">
|
|
44
|
+
<PieChart className="w-8 h-8 mx-auto mb-2 opacity-50" />
|
|
45
|
+
<p className="text-sm">{t('empty')}</p>
|
|
46
|
+
</div>
|
|
47
|
+
) : (
|
|
48
|
+
<>
|
|
49
|
+
{data.areas.map((area, idx) => (
|
|
50
|
+
<div key={idx} className="space-y-1">
|
|
51
|
+
<div className="flex items-center justify-between">
|
|
52
|
+
<div className="flex items-center gap-2">
|
|
53
|
+
<span className="text-lg">{area.icon}</span>
|
|
54
|
+
<span className="text-xs font-medium text-gray-900 truncate">
|
|
55
|
+
{area.name}
|
|
56
|
+
</span>
|
|
57
|
+
</div>
|
|
58
|
+
<span className="text-xs font-bold text-gray-700">
|
|
59
|
+
{area.percentage}%
|
|
60
|
+
</span>
|
|
61
|
+
</div>
|
|
62
|
+
<div className="relative w-full h-3 bg-gray-100 rounded-full overflow-hidden">
|
|
63
|
+
<div
|
|
64
|
+
className={`h-full bg-${colorMap[idx % colorMap.length]}-500 transition-all`}
|
|
65
|
+
style={{ width: `${area.percentage}%` }}
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
68
|
+
<div className="text-xs text-gray-600">{area.capacity}h</div>
|
|
69
|
+
</div>
|
|
70
|
+
))}
|
|
71
|
+
<div className="pt-3 border-t border-teal-100 bg-teal-50 rounded-lg p-3 text-center">
|
|
72
|
+
<p className="text-xs text-teal-700 mb-1">{t('total')}</p>
|
|
73
|
+
<p className="text-2xl font-bold text-teal-600">
|
|
74
|
+
{data.totalCapacity}h
|
|
75
|
+
</p>
|
|
76
|
+
</div>
|
|
77
|
+
</>
|
|
78
|
+
)}
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export default CapacityDistribution;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { BarChart3 } from 'lucide-react';
|
|
4
|
+
import { useTranslations } from 'next-intl';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
interface ProjectEffort {
|
|
8
|
+
name: string;
|
|
9
|
+
hours: number;
|
|
10
|
+
percent: number;
|
|
11
|
+
color: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface EffortByProjectProps {
|
|
15
|
+
slug: string;
|
|
16
|
+
title: string;
|
|
17
|
+
roleSlug: string;
|
|
18
|
+
width?: number;
|
|
19
|
+
height?: number;
|
|
20
|
+
data?: { projects: ProjectEffort[]; totalHours: number };
|
|
21
|
+
style?: React.CSSProperties;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const EffortByProject: React.FC<EffortByProjectProps> = ({
|
|
25
|
+
title,
|
|
26
|
+
data = { projects: [], totalHours: 0 },
|
|
27
|
+
style,
|
|
28
|
+
}) => {
|
|
29
|
+
const t = useTranslations('operations.EffortByProject');
|
|
30
|
+
const colorMap = [
|
|
31
|
+
'bg-blue-500',
|
|
32
|
+
'bg-purple-500',
|
|
33
|
+
'bg-pink-500',
|
|
34
|
+
'bg-green-500',
|
|
35
|
+
'bg-orange-500',
|
|
36
|
+
];
|
|
37
|
+
return (
|
|
38
|
+
<div
|
|
39
|
+
className="bg-linear-to-br from-white to-pink-50 rounded-lg shadow-sm p-4 border border-pink-100"
|
|
40
|
+
style={style}
|
|
41
|
+
>
|
|
42
|
+
<div className="flex items-center justify-between mb-4">
|
|
43
|
+
<h3 className="text-sm font-medium text-gray-700">{title}</h3>
|
|
44
|
+
<BarChart3 className="w-4 h-4 text-pink-600" />
|
|
45
|
+
</div>
|
|
46
|
+
<div className="space-y-3">
|
|
47
|
+
{data.projects.length === 0 ? (
|
|
48
|
+
<div className="text-center py-6 text-gray-500">
|
|
49
|
+
<BarChart3 className="w-8 h-8 mx-auto mb-2 opacity-50" />
|
|
50
|
+
<p className="text-sm">{t('empty')}</p>
|
|
51
|
+
</div>
|
|
52
|
+
) : (
|
|
53
|
+
<>
|
|
54
|
+
{data.projects.slice(0, 5).map((project, idx) => (
|
|
55
|
+
<div key={idx} className="space-y-1">
|
|
56
|
+
<div className="flex justify-between items-center">
|
|
57
|
+
<span className="text-xs font-medium text-gray-900 truncate">
|
|
58
|
+
{project.name}
|
|
59
|
+
</span>
|
|
60
|
+
<span className="text-xs font-bold text-gray-700">
|
|
61
|
+
{project.hours}h ({project.percent}%)
|
|
62
|
+
</span>
|
|
63
|
+
</div>
|
|
64
|
+
<div className="relative w-full h-4 bg-gray-100 rounded-full overflow-hidden">
|
|
65
|
+
<div
|
|
66
|
+
className={`h-full ${colorMap[idx % colorMap.length]} transition-all`}
|
|
67
|
+
style={{ width: `${project.percent}%` }}
|
|
68
|
+
/>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
))}
|
|
72
|
+
<div className="pt-2 border-t border-pink-100 text-center">
|
|
73
|
+
<p className="text-xs text-gray-600 mb-1">{t('total')}</p>
|
|
74
|
+
<p className="text-2xl font-bold text-pink-600">
|
|
75
|
+
{data.totalHours}h
|
|
76
|
+
</p>
|
|
77
|
+
</div>
|
|
78
|
+
</>
|
|
79
|
+
)}
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export default EffortByProject;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Users } from 'lucide-react';
|
|
4
|
+
import { useTranslations } from 'next-intl';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
interface DepartmentHeadcount {
|
|
8
|
+
name: string;
|
|
9
|
+
count: number;
|
|
10
|
+
percentage: number;
|
|
11
|
+
maxCapacity: number;
|
|
12
|
+
utilization: number;
|
|
13
|
+
icon: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface HeadcountByAreaProps {
|
|
17
|
+
slug: string;
|
|
18
|
+
title: string;
|
|
19
|
+
roleSlug: string;
|
|
20
|
+
width?: number;
|
|
21
|
+
height?: number;
|
|
22
|
+
data?: { departments: DepartmentHeadcount[]; totalHeadcount: number };
|
|
23
|
+
style?: React.CSSProperties;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const HeadcountByArea: React.FC<HeadcountByAreaProps> = ({
|
|
27
|
+
title,
|
|
28
|
+
data = { departments: [], totalHeadcount: 0 },
|
|
29
|
+
style,
|
|
30
|
+
}) => {
|
|
31
|
+
const t = useTranslations('operations.HeadcountByArea');
|
|
32
|
+
const colorMap = [
|
|
33
|
+
'blue',
|
|
34
|
+
'indigo',
|
|
35
|
+
'purple',
|
|
36
|
+
'pink',
|
|
37
|
+
'red',
|
|
38
|
+
'orange',
|
|
39
|
+
'yellow',
|
|
40
|
+
'green',
|
|
41
|
+
];
|
|
42
|
+
return (
|
|
43
|
+
<div
|
|
44
|
+
className="bg-linear-to-br from-white to-blue-50 rounded-lg shadow-sm p-4 border border-blue-100"
|
|
45
|
+
style={style}
|
|
46
|
+
>
|
|
47
|
+
<div className="flex items-center justify-between mb-4">
|
|
48
|
+
<h3 className="text-sm font-medium text-gray-700">{title}</h3>
|
|
49
|
+
<Users className="w-4 h-4 text-blue-600" />
|
|
50
|
+
</div>
|
|
51
|
+
<div className="space-y-3">
|
|
52
|
+
{data.departments.length === 0 ? (
|
|
53
|
+
<div className="text-center py-6 text-gray-500">
|
|
54
|
+
<Users className="w-8 h-8 mx-auto mb-2 opacity-50" />
|
|
55
|
+
<p className="text-sm">{t('empty')}</p>
|
|
56
|
+
</div>
|
|
57
|
+
) : (
|
|
58
|
+
<>
|
|
59
|
+
{data.departments.map((dept, idx) => (
|
|
60
|
+
<div key={idx} className="space-y-1">
|
|
61
|
+
<div className="flex items-center justify-between">
|
|
62
|
+
<div className="flex items-center gap-2 flex-1">
|
|
63
|
+
<span className="text-lg">{dept.icon}</span>
|
|
64
|
+
<span className="text-xs font-medium text-gray-900 truncate">
|
|
65
|
+
{dept.name}
|
|
66
|
+
</span>
|
|
67
|
+
</div>
|
|
68
|
+
<span className="text-xs font-bold text-gray-700 ml-2">
|
|
69
|
+
{dept.count}
|
|
70
|
+
</span>
|
|
71
|
+
</div>
|
|
72
|
+
<div className="relative w-full h-4 bg-gray-100 rounded-full overflow-hidden border border-gray-200">
|
|
73
|
+
<div
|
|
74
|
+
className={`h-full bg-${colorMap[idx % colorMap.length]}-500 transition-all`}
|
|
75
|
+
style={{
|
|
76
|
+
width: `${(dept.count / Math.max(...data.departments.map((d) => d.count))) * 100}%`,
|
|
77
|
+
}}
|
|
78
|
+
/>
|
|
79
|
+
</div>
|
|
80
|
+
<div className="flex justify-between text-xs text-gray-600">
|
|
81
|
+
<span>{dept.percentage}% total</span>
|
|
82
|
+
<span>{dept.utilization}% capacidade</span>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
))}
|
|
86
|
+
<div className="pt-3 border-t border-blue-100 bg-blue-50 rounded-lg p-3 text-center">
|
|
87
|
+
<p className="text-xs text-blue-700 mb-1">
|
|
88
|
+
{t('totalCollaborators')}
|
|
89
|
+
</p>
|
|
90
|
+
<p className="text-3xl font-bold text-blue-600">
|
|
91
|
+
{data.totalHeadcount}
|
|
92
|
+
</p>
|
|
93
|
+
</div>
|
|
94
|
+
</>
|
|
95
|
+
)}
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export default HeadcountByArea;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { TrendingUp } from 'lucide-react';
|
|
4
|
+
import { useTranslations } from 'next-intl';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
interface ManagedProjectsStatusProps {
|
|
8
|
+
slug: string;
|
|
9
|
+
title: string;
|
|
10
|
+
roleSlug: string;
|
|
11
|
+
width?: number;
|
|
12
|
+
height?: number;
|
|
13
|
+
data?: {
|
|
14
|
+
onTrack: number;
|
|
15
|
+
atRisk: number;
|
|
16
|
+
delayed: number;
|
|
17
|
+
completed: number;
|
|
18
|
+
};
|
|
19
|
+
style?: React.CSSProperties;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const ManagedProjectsStatus: React.FC<ManagedProjectsStatusProps> = ({
|
|
23
|
+
title,
|
|
24
|
+
data = { onTrack: 0, atRisk: 0, delayed: 0, completed: 0 },
|
|
25
|
+
style,
|
|
26
|
+
}) => {
|
|
27
|
+
const t = useTranslations('operations.ManagedProjectsStatus');
|
|
28
|
+
const total = data.onTrack + data.atRisk + data.delayed + data.completed;
|
|
29
|
+
const healthPercent =
|
|
30
|
+
total > 0 ? Math.round(((data.onTrack + data.completed) / total) * 100) : 0;
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div
|
|
34
|
+
className="bg-linear-to-br from-white to-cyan-50 rounded-lg shadow-sm p-4 border border-cyan-100"
|
|
35
|
+
style={style}
|
|
36
|
+
>
|
|
37
|
+
<div className="flex items-center justify-between mb-4">
|
|
38
|
+
<h3 className="text-sm font-medium text-gray-700">{title}</h3>
|
|
39
|
+
<TrendingUp className="w-4 h-4 text-cyan-600" />
|
|
40
|
+
</div>
|
|
41
|
+
<div className="bg-linear-to-r from-cyan-500 to-blue-600 rounded-lg p-4 text-white mb-3">
|
|
42
|
+
<div className="flex items-center justify-between">
|
|
43
|
+
<div>
|
|
44
|
+
<div className="text-xs opacity-90">{t('title')}</div>
|
|
45
|
+
<div className="text-3xl font-bold">{healthPercent}%</div>
|
|
46
|
+
</div>
|
|
47
|
+
<div className="text-4xl">📊</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
<div className="grid grid-cols-2 gap-2 mb-3">
|
|
51
|
+
<div className="bg-green-50 rounded-lg p-3 border border-green-200">
|
|
52
|
+
<div className="text-2xl font-bold text-green-600">
|
|
53
|
+
{data.onTrack}
|
|
54
|
+
</div>
|
|
55
|
+
<div className="text-xs text-gray-600">{t('onSchedule')}</div>
|
|
56
|
+
</div>
|
|
57
|
+
<div className="bg-yellow-50 rounded-lg p-3 border border-yellow-200">
|
|
58
|
+
<div className="text-2xl font-bold text-yellow-600">
|
|
59
|
+
{data.atRisk}
|
|
60
|
+
</div>
|
|
61
|
+
<div className="text-xs text-gray-600">{t('atRisk')}</div>
|
|
62
|
+
</div>
|
|
63
|
+
<div className="bg-red-50 rounded-lg p-3 border border-red-200">
|
|
64
|
+
<div className="text-2xl font-bold text-red-600">{data.delayed}</div>
|
|
65
|
+
<div className="text-xs text-gray-600">{t('delayed')}</div>
|
|
66
|
+
</div>
|
|
67
|
+
<div className="bg-blue-50 rounded-lg p-3 border border-blue-200">
|
|
68
|
+
<div className="text-2xl font-bold text-blue-600">
|
|
69
|
+
{data.completed}
|
|
70
|
+
</div>
|
|
71
|
+
<div className="text-xs text-gray-600">{t('done')}</div>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
<div className="space-y-2 bg-cyan-50 rounded-lg p-3 border border-cyan-200">
|
|
75
|
+
<div className="text-xs font-medium text-gray-700 mb-2">
|
|
76
|
+
{t('distribution')}
|
|
77
|
+
</div>
|
|
78
|
+
<div className="flex gap-1 h-8 rounded-lg overflow-hidden">
|
|
79
|
+
{data.onTrack > 0 && (
|
|
80
|
+
<div
|
|
81
|
+
className="bg-green-500 hover:brightness-110 transition-all"
|
|
82
|
+
style={{ width: `${(data.onTrack / total) * 100}%` }}
|
|
83
|
+
/>
|
|
84
|
+
)}
|
|
85
|
+
{data.atRisk > 0 && (
|
|
86
|
+
<div
|
|
87
|
+
className="bg-yellow-500 hover:brightness-110 transition-all"
|
|
88
|
+
style={{ width: `${(data.atRisk / total) * 100}%` }}
|
|
89
|
+
/>
|
|
90
|
+
)}
|
|
91
|
+
{data.delayed > 0 && (
|
|
92
|
+
<div
|
|
93
|
+
className="bg-red-500 hover:brightness-110 transition-all"
|
|
94
|
+
style={{ width: `${(data.delayed / total) * 100}%` }}
|
|
95
|
+
/>
|
|
96
|
+
)}
|
|
97
|
+
{data.completed > 0 && (
|
|
98
|
+
<div
|
|
99
|
+
className="bg-blue-500 hover:brightness-110 transition-all"
|
|
100
|
+
style={{ width: `${(data.completed / total) * 100}%` }}
|
|
101
|
+
/>
|
|
102
|
+
)}
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
<div className="text-center text-xs text-gray-600 pt-2 border-t border-cyan-100">
|
|
106
|
+
<span className="font-semibold text-gray-900">{total}</span>{' '}
|
|
107
|
+
{t('supervision')}
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export default ManagedProjectsStatus;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Widget KPI: Minhas Horas no Período
|
|
5
|
+
* Mostra horas aprovadas vs. planejadas
|
|
6
|
+
* Para: operations-collaborator
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { AlertCircle, CheckCircle2 } from 'lucide-react';
|
|
10
|
+
import React from 'react';
|
|
11
|
+
|
|
12
|
+
interface MyHoursPeriodKpiProps {
|
|
13
|
+
slug: string;
|
|
14
|
+
title: string;
|
|
15
|
+
roleSlug: string;
|
|
16
|
+
width?: number;
|
|
17
|
+
height?: number;
|
|
18
|
+
data?: {
|
|
19
|
+
plannedHours: number;
|
|
20
|
+
approvedHours: number;
|
|
21
|
+
pendingHours: number;
|
|
22
|
+
};
|
|
23
|
+
style?: React.CSSProperties;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const MyHoursPeriodKpi: React.FC<MyHoursPeriodKpiProps> = ({
|
|
27
|
+
title,
|
|
28
|
+
data = { plannedHours: 40, approvedHours: 32, pendingHours: 4 },
|
|
29
|
+
style,
|
|
30
|
+
}) => {
|
|
31
|
+
const totalHours = data.approvedHours + data.pendingHours;
|
|
32
|
+
const completionPercent = Math.round((totalHours / data.plannedHours) * 100);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div
|
|
36
|
+
className="bg-white rounded-lg shadow-sm p-4 border border-gray-200 hover:shadow-md transition-shadow"
|
|
37
|
+
style={style}
|
|
38
|
+
>
|
|
39
|
+
<h3 className="text-sm font-medium text-gray-700 mb-3">{title}</h3>
|
|
40
|
+
|
|
41
|
+
<div className="space-y-3">
|
|
42
|
+
{/* Main metric */}
|
|
43
|
+
<div className="flex items-baseline gap-2">
|
|
44
|
+
<span className="text-3xl font-bold text-blue-600">{totalHours}</span>
|
|
45
|
+
<span className="text-sm text-gray-600">de {data.plannedHours}h</span>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
{/* Progress bar */}
|
|
49
|
+
<div className="space-y-1">
|
|
50
|
+
<div className="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
|
|
51
|
+
<div
|
|
52
|
+
className="bg-linear-to-r from-blue-500 to-blue-600 h-full rounded-full transition-all"
|
|
53
|
+
style={{ width: `${Math.min(completionPercent, 100)}%` }}
|
|
54
|
+
/>
|
|
55
|
+
</div>
|
|
56
|
+
<div className="flex justify-between text-xs text-gray-600">
|
|
57
|
+
<span>{completionPercent}% completo</span>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
{/* Breakdown */}
|
|
62
|
+
<div className="pt-2 border-t border-gray-200 space-y-1 text-xs">
|
|
63
|
+
<div className="flex justify-between">
|
|
64
|
+
<span className="text-gray-600 flex items-center gap-1">
|
|
65
|
+
<CheckCircle2 className="w-3 h-3 text-green-600" />
|
|
66
|
+
Aprovado:
|
|
67
|
+
</span>
|
|
68
|
+
<span className="font-medium text-green-600">
|
|
69
|
+
{data.approvedHours}h
|
|
70
|
+
</span>
|
|
71
|
+
</div>
|
|
72
|
+
<div className="flex justify-between">
|
|
73
|
+
<span className="text-gray-600 flex items-center gap-1">
|
|
74
|
+
<AlertCircle className="w-3 h-3 text-yellow-600" />
|
|
75
|
+
Pendente:
|
|
76
|
+
</span>
|
|
77
|
+
<span className="font-medium text-yellow-600">
|
|
78
|
+
{data.pendingHours}h
|
|
79
|
+
</span>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export default MyHoursPeriodKpi;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { AlertTriangle, MessageSquare } from 'lucide-react';
|
|
4
|
+
import { useTranslations } from 'next-intl';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
interface MyOpenRequestsKpiProps {
|
|
8
|
+
slug: string;
|
|
9
|
+
title: string;
|
|
10
|
+
roleSlug: string;
|
|
11
|
+
width?: number;
|
|
12
|
+
height?: number;
|
|
13
|
+
data?: {
|
|
14
|
+
totalOpen: number;
|
|
15
|
+
vacationRequests: number;
|
|
16
|
+
scheduleAdjustments: number;
|
|
17
|
+
expenseRequests: number;
|
|
18
|
+
otherRequests: number;
|
|
19
|
+
};
|
|
20
|
+
style?: React.CSSProperties;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const MyOpenRequestsKpi: React.FC<MyOpenRequestsKpiProps> = ({
|
|
24
|
+
title,
|
|
25
|
+
data = {
|
|
26
|
+
totalOpen: 0,
|
|
27
|
+
vacationRequests: 0,
|
|
28
|
+
scheduleAdjustments: 0,
|
|
29
|
+
expenseRequests: 0,
|
|
30
|
+
otherRequests: 0,
|
|
31
|
+
},
|
|
32
|
+
style,
|
|
33
|
+
}) => {
|
|
34
|
+
const t = useTranslations('operations.MyOpenRequestsKpi');
|
|
35
|
+
const hasUrgent = data.totalOpen > 5;
|
|
36
|
+
const requests = [
|
|
37
|
+
{
|
|
38
|
+
label: t('requests.vacation'),
|
|
39
|
+
count: data.vacationRequests,
|
|
40
|
+
color: 'blue',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
label: t('requests.scheduleAdjust'),
|
|
44
|
+
count: data.scheduleAdjustments,
|
|
45
|
+
color: 'purple',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
label: t('requests.expenses'),
|
|
49
|
+
count: data.expenseRequests,
|
|
50
|
+
color: 'green',
|
|
51
|
+
},
|
|
52
|
+
{ label: t('requests.others'), count: data.otherRequests, color: 'gray' },
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<div
|
|
57
|
+
className={`bg-linear-to-br from-white to-orange-50 rounded-lg shadow-sm p-4 border ${hasUrgent ? 'border-orange-300 border-2' : 'border-orange-100'} transition-all`}
|
|
58
|
+
style={style}
|
|
59
|
+
>
|
|
60
|
+
<div className="flex items-center justify-between mb-4">
|
|
61
|
+
<h3 className="text-sm font-medium text-gray-700">{title}</h3>
|
|
62
|
+
{hasUrgent && (
|
|
63
|
+
<div className="flex items-center gap-1 px-2 py-1 bg-orange-100 rounded-full">
|
|
64
|
+
<AlertTriangle className="w-3 h-3 text-orange-600" />
|
|
65
|
+
<span className="text-xs font-semibold text-orange-600">
|
|
66
|
+
{t('alert')}
|
|
67
|
+
</span>
|
|
68
|
+
</div>
|
|
69
|
+
)}
|
|
70
|
+
</div>
|
|
71
|
+
<div className="bg-white rounded-lg p-3 border border-orange-100 mb-3 flex items-center gap-2">
|
|
72
|
+
<MessageSquare className="w-5 h-5 text-orange-600" />
|
|
73
|
+
<div>
|
|
74
|
+
<div className="text-xs text-gray-600">{t('openRequests')}</div>
|
|
75
|
+
<div className="text-3xl font-bold text-orange-600">
|
|
76
|
+
{data.totalOpen}
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
<div className="grid grid-cols-2 gap-2">
|
|
81
|
+
{requests.map((req) => (
|
|
82
|
+
<div
|
|
83
|
+
key={req.label}
|
|
84
|
+
className={`bg-${req.color}-50 rounded-lg p-3 border border-${req.color}-200`}
|
|
85
|
+
>
|
|
86
|
+
<div className="text-xs text-gray-600 mb-1">{req.label}</div>
|
|
87
|
+
<div className={`text-xl font-bold text-${req.color}-600`}>
|
|
88
|
+
{req.count}
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
))}
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export default MyOpenRequestsKpi;
|