@hed-hog/operations 0.0.330 → 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 +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.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 +182 -268
- package/dist/operations.service.d.ts.map +1 -1
- package/dist/operations.service.js +2147 -1337
- 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/route.yaml +61 -0
- 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-details-screen.tsx.ejs +289 -412
- package/hedhog/frontend/app/_components/project-file-attachments.tsx.ejs +371 -0
- package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +426 -374
- package/hedhog/frontend/app/_components/task-detail-sheet.tsx.ejs +803 -581
- 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 +480 -476
- 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 +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 +14 -10
- package/hedhog/frontend/app/my-tasks/page.tsx.ejs +328 -105
- 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 +376 -30
- package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +6 -1
- 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 +238 -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 +238 -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_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-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 +4277 -2535
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Calendar } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
interface MyWeeklyJourneyProps {
|
|
7
|
+
slug: string;
|
|
8
|
+
title: string;
|
|
9
|
+
roleSlug: string;
|
|
10
|
+
width?: number;
|
|
11
|
+
height?: number;
|
|
12
|
+
data?: {
|
|
13
|
+
weekDays: Array<{ day: string; hours: number; status: 'completed' | 'pending' | 'off' }>;
|
|
14
|
+
weekProgress: number;
|
|
15
|
+
targetHours: number;
|
|
16
|
+
};
|
|
17
|
+
style?: React.CSSProperties;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const MyWeeklyJourney: React.FC<MyWeeklyJourneyProps> = ({
|
|
21
|
+
title,
|
|
22
|
+
data = { weekDays: [], weekProgress: 60, targetHours: 40 },
|
|
23
|
+
style,
|
|
24
|
+
}) => {
|
|
25
|
+
const totalHours = data.weekDays.reduce((sum, day) => sum + day.hours, 0);
|
|
26
|
+
const progressPercent = Math.min(Math.round((totalHours / data.targetHours) * 100), 100);
|
|
27
|
+
|
|
28
|
+
const getStatusColor = (status: string) => {
|
|
29
|
+
switch (status) {
|
|
30
|
+
case 'completed': return 'bg-gradient-to-t from-green-500 to-green-400';
|
|
31
|
+
case 'pending': return 'bg-gradient-to-t from-gray-300 to-gray-200';
|
|
32
|
+
default: return 'bg-gradient-to-t from-gray-100 to-gray-50';
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div className="bg-linear-to-br from-white to-cyan-50 rounded-lg shadow-sm p-4 border border-cyan-100" style={style}>
|
|
38
|
+
<div className="flex items-center justify-between mb-4"><h3 className="text-sm font-medium text-gray-700">{title}</h3><Calendar className="w-4 h-4 text-cyan-600" /></div>
|
|
39
|
+
<div className="space-y-2 mb-3">
|
|
40
|
+
<div className="flex justify-between items-center"><span className="text-xs font-medium text-gray-700">Progresso da Semana</span><span className="text-xs font-bold text-cyan-600">{progressPercent}%</span></div>
|
|
41
|
+
<div className="relative w-full h-4 bg-gray-200 rounded-full overflow-hidden"><div className="h-full bg-linear-to-r from-cyan-400 to-cyan-600 rounded-full transition-all" style={{ width: `${progressPercent}%` }} /></div>
|
|
42
|
+
<div className="flex justify-between text-xs text-gray-600"><span>{totalHours}h registradas</span><span>{data.targetHours}h meta</span></div>
|
|
43
|
+
</div>
|
|
44
|
+
<div className="text-xs font-medium text-gray-700 mb-2">Horas por dia</div>
|
|
45
|
+
<div className="flex items-end justify-between gap-1 h-24 bg-gradient-to-b from-cyan-50 to-white rounded-lg p-3 border border-cyan-100">
|
|
46
|
+
{data.weekDays.map((day, idx) => (
|
|
47
|
+
<div key={idx} className="flex flex-col items-center flex-1 gap-1">
|
|
48
|
+
<div className={`w-full rounded-t transition-all ${getStatusColor(day.status)}`} style={{ height: day.status === 'off' ? '8px' : `${Math.max((day.hours / 10) * 100, 8)}%`, minHeight: day.status === 'off' ? '8px' : '12px' }} />
|
|
49
|
+
<div className="text-xs font-semibold text-gray-700">{day.day}</div>
|
|
50
|
+
</div>
|
|
51
|
+
))}
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export default MyWeeklyJourney;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { DollarSign, TrendingDown } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
interface PortfolioCostsKpiProps {
|
|
7
|
+
slug: string;
|
|
8
|
+
title: string;
|
|
9
|
+
roleSlug: string;
|
|
10
|
+
width?: number;
|
|
11
|
+
height?: number;
|
|
12
|
+
data?: { totalCosts: number; budgetAmount: number; costsByCategoryCount: number; savingsPercent: number; costTrend: 'up' | 'down' | 'stable' };
|
|
13
|
+
style?: React.CSSProperties;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const PortfolioCostsKpi: React.FC<PortfolioCostsKpiProps> = ({
|
|
17
|
+
title,
|
|
18
|
+
data = { totalCosts: 0, budgetAmount: 0, costsByCategoryCount: 0, savingsPercent: 0, costTrend: 'stable' },
|
|
19
|
+
style,
|
|
20
|
+
}) => {
|
|
21
|
+
const budgetUtilization = Math.round((data.totalCosts / data.budgetAmount) * 100);
|
|
22
|
+
const isBudgetHealth = budgetUtilization <= 85;
|
|
23
|
+
return (
|
|
24
|
+
<div className="bg-linear-to-br from-white to-green-50 rounded-lg shadow-sm p-4 border border-green-100" style={style}>
|
|
25
|
+
<div className="flex items-center justify-between mb-4"><h3 className="text-sm font-medium text-gray-700">{title}</h3><DollarSign className="w-4 h-4 text-green-600" /></div>
|
|
26
|
+
<div className="bg-linear-to-r from-green-500 to-emerald-600 rounded-lg p-4 text-white mb-3">
|
|
27
|
+
<div className="flex items-center justify-between">
|
|
28
|
+
<div><div className="text-xs opacity-90">Custos Totais</div><div className="text-3xl font-bold">{(data.totalCosts / 1000).toFixed(1)}k</div></div>
|
|
29
|
+
<DollarSign className="w-12 h-12 opacity-30" />
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
<div className="space-y-2">
|
|
33
|
+
<div className="flex justify-between items-center"><span className="text-xs font-medium text-gray-700">Utilização do Orçamento</span><span className={`text-sm font-bold px-2 py-1 rounded-full ${isBudgetHealth ? 'bg-green-100 text-green-700' : 'bg-yellow-100 text-yellow-700'}`}>{budgetUtilization}%</span></div>
|
|
34
|
+
<div className="relative w-full h-4 bg-gray-200 rounded-full overflow-hidden"><div className={`h-full transition-all duration-500 ease-out rounded-full ${isBudgetHealth ? 'bg-linear-to-r from-green-400 to-green-600' : 'bg-linear-to-r from-yellow-400 to-yellow-600'}`} style={{ width: `${Math.min(budgetUtilization, 100)}%` }} /></div>
|
|
35
|
+
<div className="flex justify-between text-xs text-gray-600"><span>R$ {(data.totalCosts / 1000).toFixed(1)}k gasto</span><span>R$ {(data.budgetAmount / 1000).toFixed(1)}k orçado</span></div>
|
|
36
|
+
</div>
|
|
37
|
+
<div className="grid grid-cols-2 gap-2 bg-green-50 rounded-lg p-3 border border-green-200 mt-3">
|
|
38
|
+
<div><div className="text-xs text-gray-600 mb-1">Economias</div><div className="text-2xl font-bold text-green-600">{data.savingsPercent}%</div></div>
|
|
39
|
+
<div><div className="text-xs text-gray-600 mb-1">Tendência</div><div className={`text-sm font-bold flex items-center gap-1 ${data.costTrend === 'down' ? 'text-green-600' : data.costTrend === 'up' ? 'text-red-600' : 'text-gray-600'}`}>
|
|
40
|
+
{data.costTrend === 'down' && '📉'}{data.costTrend === 'up' && '📈'}{data.costTrend === 'stable' && '➡️'}
|
|
41
|
+
{data.costTrend === 'down' ? 'Reduzindo' : data.costTrend === 'up' ? 'Aumentando' : 'Estável'}
|
|
42
|
+
</div></div>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export default PortfolioCostsKpi;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Activity, Clock } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
interface PortfolioEffortKpiProps {
|
|
7
|
+
slug: string;
|
|
8
|
+
title: string;
|
|
9
|
+
roleSlug: string;
|
|
10
|
+
width?: number;
|
|
11
|
+
height?: number;
|
|
12
|
+
data?: { totalEffortHours: number; allocatedHours: number; availableHours: number; effortUtilization: number };
|
|
13
|
+
style?: React.CSSProperties;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const PortfolioEffortKpi: React.FC<PortfolioEffortKpiProps> = ({
|
|
17
|
+
title,
|
|
18
|
+
data = { totalEffortHours: 0, allocatedHours: 0, availableHours: 0, effortUtilization: 0 },
|
|
19
|
+
style,
|
|
20
|
+
}) => (
|
|
21
|
+
<div className="bg-linear-to-br from-white to-orange-50 rounded-lg shadow-sm p-4 border border-orange-100" style={style}>
|
|
22
|
+
<div className="flex items-center justify-between mb-4"><h3 className="text-sm font-medium text-gray-700">{title}</h3><Activity className="w-4 h-4 text-orange-600" /></div>
|
|
23
|
+
<div className="bg-linear-to-r from-orange-500 to-amber-600 rounded-lg p-4 text-white mb-3">
|
|
24
|
+
<div className="flex items-center justify-between">
|
|
25
|
+
<div><div className="text-xs opacity-90">Esforço Total</div><div className="text-3xl font-bold">{data.totalEffortHours}</div><div className="text-xs opacity-90">horas</div></div>
|
|
26
|
+
<Clock className="w-12 h-12 opacity-30" />
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
<div className="space-y-2">
|
|
30
|
+
<div className="flex justify-between items-center"><span className="text-xs font-medium text-gray-700">Taxa de Utilização</span><span className="text-sm font-bold text-orange-600">{data.effortUtilization}%</span></div>
|
|
31
|
+
<div className="relative w-full h-4 bg-gray-200 rounded-full overflow-hidden"><div className="h-full bg-linear-to-r from-orange-400 to-orange-600 transition-all" style={{ width: `${Math.min(data.effortUtilization, 100)}%` }} /></div>
|
|
32
|
+
</div>
|
|
33
|
+
<div className="grid grid-cols-2 gap-2 mt-3">
|
|
34
|
+
<div className="bg-orange-100 rounded-lg p-3 border border-orange-200"><div className="text-xs text-orange-700 mb-1">Alocado</div><div className="text-2xl font-bold text-orange-600">{data.allocatedHours}h</div></div>
|
|
35
|
+
<div className="bg-amber-100 rounded-lg p-3 border border-amber-200"><div className="text-xs text-amber-700 mb-1">Disponível</div><div className="text-2xl font-bold text-amber-600">{data.availableHours}h</div></div>
|
|
36
|
+
</div>
|
|
37
|
+
<div className="text-center text-xs text-gray-600 pt-2 border-t border-orange-100 mt-3">{data.effortUtilization > 90 ? '⚠️ Portfólio com alta demanda' : data.effortUtilization > 70 ? '✓ Utilização equilibrada' : '📊 Capacidade disponível'}</div>
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
export default PortfolioEffortKpi;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { AlertTriangle, TrendingUp } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
interface PortfolioRiskKpiProps {
|
|
7
|
+
slug: string;
|
|
8
|
+
title: string;
|
|
9
|
+
roleSlug: string;
|
|
10
|
+
width?: number;
|
|
11
|
+
height?: number;
|
|
12
|
+
data?: { totalRisks: number; criticalRisks: number; highRisks: number; mediumRisks: number; riskTrend: 'increasing' | 'stable' | 'decreasing' };
|
|
13
|
+
style?: React.CSSProperties;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const PortfolioRiskKpi: React.FC<PortfolioRiskKpiProps> = ({
|
|
17
|
+
title,
|
|
18
|
+
data = { totalRisks: 0, criticalRisks: 0, highRisks: 0, mediumRisks: 0, riskTrend: 'stable' },
|
|
19
|
+
style,
|
|
20
|
+
}) => {
|
|
21
|
+
const hasHighRisk = data.criticalRisks > 0 || data.highRisks > 3;
|
|
22
|
+
return (
|
|
23
|
+
<div className={`bg-linear-to-br from-white to-red-50 rounded-lg shadow-sm p-4 border ${hasHighRisk ? 'border-red-400 border-2' : 'border-red-100'} transition-all`} style={style}>
|
|
24
|
+
<div className="flex items-center justify-between mb-4">
|
|
25
|
+
<h3 className="text-sm font-medium text-gray-700">{title}</h3>
|
|
26
|
+
{hasHighRisk && <div className="relative"><div className="absolute inset-0 bg-red-600 rounded-full animate-pulse" style={{ opacity: 0.25 }} /><AlertTriangle className="w-5 h-5 text-red-600 relative" /></div>}
|
|
27
|
+
</div>
|
|
28
|
+
<div className={`rounded-lg p-4 ${data.totalRisks > 10 ? 'bg-red-500 text-white' : data.totalRisks > 5 ? 'bg-yellow-500 text-white' : 'bg-green-500 text-white'}`}>
|
|
29
|
+
<div className="flex items-center justify-between">
|
|
30
|
+
<div><div className="text-xs opacity-90">Riscos Identificados</div><div className="text-3xl font-bold">{data.totalRisks}</div></div>
|
|
31
|
+
<AlertTriangle className="w-12 h-12 opacity-30" />
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
<div className="space-y-2 mt-3">
|
|
35
|
+
{data.criticalRisks > 0 && <div className="flex items-center justify-between bg-red-50 p-2 rounded-lg border border-red-200"><span className="text-xs font-medium text-red-700">🔴 Crítico</span><span className="text-sm font-bold text-red-600">{data.criticalRisks}</span></div>}
|
|
36
|
+
{data.highRisks > 0 && <div className="flex items-center justify-between bg-yellow-50 p-2 rounded-lg border border-yellow-200"><span className="text-xs font-medium text-yellow-700">🟠 Alto</span><span className="text-sm font-bold text-yellow-600">{data.highRisks}</span></div>}
|
|
37
|
+
{data.mediumRisks > 0 && <div className="flex items-center justify-between bg-blue-50 p-2 rounded-lg border border-blue-200"><span className="text-xs font-medium text-blue-700">🟡 Médio</span><span className="text-sm font-bold text-blue-600">{data.mediumRisks}</span></div>}
|
|
38
|
+
</div>
|
|
39
|
+
<div className="flex items-center gap-2 pt-2 border-t border-red-100 mt-3">
|
|
40
|
+
<TrendingUp className={`w-4 h-4 ${data.riskTrend === 'increasing' ? 'text-red-600' : data.riskTrend === 'decreasing' ? 'text-green-600' : 'text-gray-600'}`} />
|
|
41
|
+
<span className="text-xs font-medium text-gray-700">
|
|
42
|
+
Tendência: {data.riskTrend === 'increasing' && '📈 Aumentando'}{data.riskTrend === 'decreasing' && '📉 Reduzindo'}{data.riskTrend === 'stable' && '➡️ Estável'}
|
|
43
|
+
</span>
|
|
44
|
+
</div>
|
|
45
|
+
{hasHighRisk && <div className="bg-red-100 rounded-lg p-2 border border-red-300 text-xs text-red-700 font-semibold text-center mt-3">⚠️ Requer atenção imediata</div>}
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default PortfolioRiskKpi;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { CheckCircle2, AlertCircle, Clock } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
interface ProjectStatusOverviewProps {
|
|
7
|
+
slug: string;
|
|
8
|
+
title: string;
|
|
9
|
+
roleSlug: string;
|
|
10
|
+
width?: number;
|
|
11
|
+
height?: number;
|
|
12
|
+
data?: { planning: number; inProgress: number; review: number; completed: number; onHold: number };
|
|
13
|
+
style?: React.CSSProperties;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const ProjectStatusOverview: React.FC<ProjectStatusOverviewProps> = ({
|
|
17
|
+
title,
|
|
18
|
+
data = { planning: 0, inProgress: 0, review: 0, completed: 0, onHold: 0 },
|
|
19
|
+
style,
|
|
20
|
+
}) => {
|
|
21
|
+
const total = data.planning + data.inProgress + data.review + data.completed + data.onHold;
|
|
22
|
+
const statuses = [
|
|
23
|
+
{ label: 'Planejamento', count: data.planning, emoji: '📋', color: 'blue', width: total > 0 ? (data.planning / total) * 100 : 0 },
|
|
24
|
+
{ label: 'Em Progresso', count: data.inProgress, emoji: '🚀', color: 'green', width: total > 0 ? (data.inProgress / total) * 100 : 0 },
|
|
25
|
+
{ label: 'Em Revisão', count: data.review, emoji: '👀', color: 'yellow', width: total > 0 ? (data.review / total) * 100 : 0 },
|
|
26
|
+
{ label: 'Concluído', count: data.completed, emoji: '✅', color: 'purple', width: total > 0 ? (data.completed / total) * 100 : 0 },
|
|
27
|
+
{ label: 'Suspenso', count: data.onHold, emoji: '⏸️', color: 'gray', width: total > 0 ? (data.onHold / total) * 100 : 0 },
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div className="bg-linear-to-br from-white to-violet-50 rounded-lg shadow-sm p-4 border border-violet-100" style={style}>
|
|
32
|
+
<div className="flex items-center justify-between mb-4"><h3 className="text-sm font-medium text-gray-700">{title}</h3><CheckCircle2 className="w-4 h-4 text-violet-600" /></div>
|
|
33
|
+
<div className="flex gap-1 h-8 rounded-lg overflow-hidden bg-gray-100 border border-gray-200 mb-3">
|
|
34
|
+
{statuses.map((status) => status.width > 0 && <div key={status.label} className={`bg-${status.color}-500 hover:brightness-110 transition-all`} style={{ width: `${status.width}%` }} />)}
|
|
35
|
+
</div>
|
|
36
|
+
<div className="grid grid-cols-2 gap-2">
|
|
37
|
+
{statuses.map((status) => (
|
|
38
|
+
<div key={status.label} className={`bg-${status.color}-50 rounded-lg p-3 border border-${status.color}-200`}>
|
|
39
|
+
<div className="text-lg mb-1">{status.emoji}</div>
|
|
40
|
+
<div className={`text-2xl font-bold text-${status.color}-600`}>{status.count}</div>
|
|
41
|
+
<div className="text-xs text-gray-600 truncate">{status.label}</div>
|
|
42
|
+
</div>
|
|
43
|
+
))}
|
|
44
|
+
</div>
|
|
45
|
+
<div className="pt-2 border-t border-violet-100 mt-3">
|
|
46
|
+
<div className="flex items-center justify-between text-xs"><span className="text-gray-700 font-medium">Total de Projetos</span><span className="text-lg font-bold text-violet-600">{total}</span></div>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export default ProjectStatusOverview;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Flag, AlertTriangle, CheckCircle2 } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
interface StrategicDeadline {
|
|
7
|
+
id: string;
|
|
8
|
+
title: string;
|
|
9
|
+
deadline: string;
|
|
10
|
+
daysLeft: number;
|
|
11
|
+
status: 'on-track' | 'at-risk' | 'critical';
|
|
12
|
+
priority: 'high' | 'critical';
|
|
13
|
+
impact: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface StrategicDeadlinesProps {
|
|
17
|
+
slug: string;
|
|
18
|
+
title: string;
|
|
19
|
+
roleSlug: string;
|
|
20
|
+
width?: number;
|
|
21
|
+
height?: number;
|
|
22
|
+
data?: { deadlines: StrategicDeadline[] };
|
|
23
|
+
style?: React.CSSProperties;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const StrategicDeadlines: React.FC<StrategicDeadlinesProps> = ({
|
|
27
|
+
title,
|
|
28
|
+
data = { deadlines: [] },
|
|
29
|
+
style,
|
|
30
|
+
}) => {
|
|
31
|
+
const criticalCount = data.deadlines.filter(d => d.status === 'critical').length;
|
|
32
|
+
const atRiskCount = data.deadlines.filter(d => d.status === 'at-risk').length;
|
|
33
|
+
|
|
34
|
+
const getStatusColor = (status: string) => {
|
|
35
|
+
switch (status) {
|
|
36
|
+
case 'critical': return { bg: 'bg-red-50', border: 'border-red-300 border-l-4', dot: 'bg-red-600 animate-pulse' };
|
|
37
|
+
case 'at-risk': return { bg: 'bg-yellow-50', border: 'border-yellow-300 border-l-4', dot: 'bg-yellow-600' };
|
|
38
|
+
default: return { bg: 'bg-green-50', border: 'border-green-300 border-l-4', dot: 'bg-green-600' };
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div className={`bg-linear-to-br from-white to-rose-50 rounded-lg shadow-sm p-4 border ${criticalCount > 0 ? 'border-rose-400 border-2' : 'border-rose-100'} transition-all`} style={style}>
|
|
44
|
+
<div className="flex items-center justify-between mb-4">
|
|
45
|
+
<h3 className="text-sm font-medium text-gray-700">{title}</h3>
|
|
46
|
+
<Flag className="w-4 h-4 text-rose-600" />
|
|
47
|
+
</div>
|
|
48
|
+
<div className="space-y-3">
|
|
49
|
+
{data.deadlines.length === 0 ? (
|
|
50
|
+
<div className="text-center py-8"><CheckCircle2 className="w-8 h-8 text-green-500 mx-auto mb-2" /><p className="text-sm text-gray-600">Nenhum prazo crítico</p></div>
|
|
51
|
+
) : (
|
|
52
|
+
<>
|
|
53
|
+
{(criticalCount > 0 || atRiskCount > 0) && (
|
|
54
|
+
<div className="flex gap-2 mb-2">
|
|
55
|
+
{criticalCount > 0 && <div className="bg-red-100 rounded-lg px-3 py-1 text-xs font-bold text-red-700 flex items-center gap-1"><AlertTriangle className="w-3 h-3" />{criticalCount} crítico{criticalCount !== 1 ? 's' : ''}</div>}
|
|
56
|
+
{atRiskCount > 0 && <div className="bg-yellow-100 rounded-lg px-3 py-1 text-xs font-bold text-yellow-700">{atRiskCount} em risco</div>}
|
|
57
|
+
</div>
|
|
58
|
+
)}
|
|
59
|
+
<div className="space-y-2 max-h-72 overflow-y-auto">
|
|
60
|
+
{data.deadlines.map((deadline) => {
|
|
61
|
+
const colors = getStatusColor(deadline.status);
|
|
62
|
+
return (
|
|
63
|
+
<div key={deadline.id} className={`${colors.bg} rounded-lg p-3 border ${colors.border} hover:shadow-md transition-all`}>
|
|
64
|
+
<div className="flex items-start gap-2">
|
|
65
|
+
<div className={`w-3 h-3 rounded-full ${colors.dot} flex-shrink-0 mt-1`} />
|
|
66
|
+
<div className="flex-1">
|
|
67
|
+
<div className="flex items-start justify-between gap-2 mb-1">
|
|
68
|
+
<p className="text-sm font-bold text-gray-900 truncate">{deadline.title}</p>
|
|
69
|
+
<span className={`text-xs font-bold px-2 py-1 rounded-full flex-shrink-0 ${deadline.priority === 'critical' ? 'bg-red-100 text-red-700' : 'bg-orange-100 text-orange-700'}`}>
|
|
70
|
+
{deadline.priority === 'critical' ? '🔴' : '🟠'}
|
|
71
|
+
</span>
|
|
72
|
+
</div>
|
|
73
|
+
<p className="text-xs text-gray-700 mb-2">{deadline.impact}</p>
|
|
74
|
+
<div className="flex items-center justify-between text-xs">
|
|
75
|
+
<span className="text-gray-600">{deadline.deadline}</span>
|
|
76
|
+
<span className={`font-bold ${deadline.daysLeft <= 0 ? 'text-red-600' : deadline.daysLeft <= 7 ? 'text-orange-600' : 'text-gray-600'}`}>
|
|
77
|
+
{deadline.daysLeft <= 0 ? '❌ Vencido' : `⏱️ ${deadline.daysLeft}d`}
|
|
78
|
+
</span>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
})}
|
|
85
|
+
</div>
|
|
86
|
+
</>
|
|
87
|
+
)}
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export default StrategicDeadlines;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Clock, CheckCircle2, User, Calendar } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
interface ApprovalItem {
|
|
7
|
+
id: string;
|
|
8
|
+
type: string;
|
|
9
|
+
submittedBy: string;
|
|
10
|
+
submittedDate: string;
|
|
11
|
+
daysWaiting: number;
|
|
12
|
+
priority: 'high' | 'normal';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface TeamApprovalQueueProps {
|
|
16
|
+
slug: string;
|
|
17
|
+
title: string;
|
|
18
|
+
roleSlug: string;
|
|
19
|
+
width?: number;
|
|
20
|
+
height?: number;
|
|
21
|
+
data?: { queue: ApprovalItem[]; totalPending: number };
|
|
22
|
+
style?: React.CSSProperties;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const TeamApprovalQueue: React.FC<TeamApprovalQueueProps> = ({
|
|
26
|
+
title,
|
|
27
|
+
data = { queue: [], totalPending: 0 },
|
|
28
|
+
style,
|
|
29
|
+
}) => {
|
|
30
|
+
const getTypeIcon = (type: string) => ({ timesheet: '⏱️', expense: '💰', vacation: '🏖️', schedule: '📅' }[type] || '📋');
|
|
31
|
+
const getTypeLabel = (type: string) => ({ timesheet: 'Timesheet', expense: 'Despesa', vacation: 'Férias', schedule: 'Ajuste' }[type] || type);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div className="bg-linear-to-br from-white to-purple-50 rounded-lg shadow-sm p-4 border border-purple-100" style={style}>
|
|
35
|
+
<div className="flex items-center justify-between mb-4">
|
|
36
|
+
<h3 className="text-sm font-medium text-gray-700">{title}</h3>
|
|
37
|
+
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-bold bg-purple-100 text-purple-800">{data.totalPending}</span>
|
|
38
|
+
</div>
|
|
39
|
+
<div className="space-y-2 max-h-80 overflow-y-auto">
|
|
40
|
+
{data.queue.length === 0 ? (
|
|
41
|
+
<div className="text-center py-8"><CheckCircle2 className="w-8 h-8 text-green-500 mx-auto mb-2" /><p className="text-sm text-gray-600">Fila de aprovações vazia</p></div>
|
|
42
|
+
) : (
|
|
43
|
+
data.queue.map((item) => (
|
|
44
|
+
<div key={item.id} className={`rounded-lg p-3 border transition-all ${item.priority === 'high' ? 'bg-red-50 border-red-300 border-l-4' : 'bg-blue-50 border-blue-200'}`}>
|
|
45
|
+
<div className="flex items-start gap-2 mb-2">
|
|
46
|
+
<span className="text-lg">{getTypeIcon(item.type)}</span>
|
|
47
|
+
<div className="flex-1">
|
|
48
|
+
<div className="flex items-center justify-between gap-2">
|
|
49
|
+
<p className="text-sm font-medium text-gray-900 truncate">{getTypeLabel(item.type)}</p>
|
|
50
|
+
{item.priority === 'high' && <span className="text-xs font-bold bg-red-100 text-red-700 px-2 py-1 rounded">🔴 Alta</span>}
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
<div className="space-y-1 text-xs">
|
|
55
|
+
<div className="flex items-center gap-1 text-gray-700"><User className="w-3 h-3" /><span className="truncate">{item.submittedBy}</span></div>
|
|
56
|
+
<div className="flex items-center justify-between">
|
|
57
|
+
<div className="flex items-center gap-1 text-gray-700"><Calendar className="w-3 h-3" /><span>{item.submittedDate}</span></div>
|
|
58
|
+
<div className={`font-semibold ${item.daysWaiting > 14 ? 'text-red-600' : item.daysWaiting > 7 ? 'text-yellow-600' : 'text-blue-600'}`}>{item.daysWaiting}d</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
))
|
|
63
|
+
)}
|
|
64
|
+
</div>
|
|
65
|
+
<div className="mt-3 pt-3 border-t border-purple-100"><p className="text-xs text-gray-600">💡 <span className="font-semibold">Dica:</span> Itens com 🔴 Alta prioridade precisam de atenção imediata</p></div>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export default TeamApprovalQueue;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Users, Zap } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
interface TeamCapacityKpiProps {
|
|
7
|
+
slug: string;
|
|
8
|
+
title: string;
|
|
9
|
+
roleSlug: string;
|
|
10
|
+
width?: number;
|
|
11
|
+
height?: number;
|
|
12
|
+
data?: { totalCapacity: number; allocatedCapacity: number; availableCapacity: number; utilizationPercent: number };
|
|
13
|
+
style?: React.CSSProperties;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const TeamCapacityKpi: React.FC<TeamCapacityKpiProps> = ({
|
|
17
|
+
title,
|
|
18
|
+
data = { totalCapacity: 100, allocatedCapacity: 0, availableCapacity: 100, utilizationPercent: 0 },
|
|
19
|
+
style,
|
|
20
|
+
}) => {
|
|
21
|
+
const getUtilizationColor = () => {
|
|
22
|
+
if (data.utilizationPercent > 95) return 'from-red-500 to-red-600';
|
|
23
|
+
if (data.utilizationPercent > 85) return 'from-yellow-500 to-yellow-600';
|
|
24
|
+
if (data.utilizationPercent > 70) return 'from-green-500 to-green-600';
|
|
25
|
+
return 'from-blue-500 to-blue-600';
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div className="bg-linear-to-br from-white to-indigo-50 rounded-lg shadow-sm p-4 border border-indigo-100" style={style}>
|
|
30
|
+
<div className="flex items-center justify-between mb-4"><h3 className="text-sm font-medium text-gray-700">{title}</h3><Zap className="w-4 h-4 text-indigo-600" /></div>
|
|
31
|
+
<div className="space-y-4">
|
|
32
|
+
<div className="space-y-2">
|
|
33
|
+
<div className="flex justify-between items-center"><span className="text-xs font-medium text-gray-700">Utilização</span><span className="text-sm font-bold text-indigo-600">{data.utilizationPercent}%</span></div>
|
|
34
|
+
<div className="relative w-full h-5 bg-gray-200 rounded-full overflow-hidden">
|
|
35
|
+
<div className={`h-full bg-linear-to-r ${getUtilizationColor()} transition-all duration-500 rounded-full`} style={{ width: `${Math.min(data.utilizationPercent, 100)}%` }} />
|
|
36
|
+
</div>
|
|
37
|
+
<div className="flex justify-between text-xs text-gray-600"><span>{data.allocatedCapacity}h alocado</span><span>{data.totalCapacity}h total</span></div>
|
|
38
|
+
</div>
|
|
39
|
+
<div className="grid grid-cols-2 gap-2">
|
|
40
|
+
<div className="bg-indigo-100 rounded-lg p-3 border border-indigo-300"><div className="text-xs text-indigo-700 mb-1">Disponível</div><div className="text-2xl font-bold text-indigo-600">{data.availableCapacity}h</div></div>
|
|
41
|
+
<div className="bg-purple-100 rounded-lg p-3 border border-purple-300"><div className="text-xs text-purple-700 mb-1">Status</div><div className={`text-sm font-bold ${data.utilizationPercent > 95 ? 'text-red-600' : data.utilizationPercent > 85 ? 'text-yellow-600' : 'text-green-600'}`}>
|
|
42
|
+
{data.utilizationPercent > 95 ? 'Crítica' : data.utilizationPercent > 85 ? 'Alta' : data.utilizationPercent > 70 ? 'Ótima' : 'Normal'}
|
|
43
|
+
</div></div>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default TeamCapacityKpi;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Clock, TrendingUp } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
interface TeamHoursKpiProps {
|
|
7
|
+
slug: string;
|
|
8
|
+
title: string;
|
|
9
|
+
roleSlug: string;
|
|
10
|
+
width?: number;
|
|
11
|
+
height?: number;
|
|
12
|
+
data?: { totalHours: number; targetHours: number; teamMembers: number; avgHoursPerMember: number; variance: number };
|
|
13
|
+
style?: React.CSSProperties;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const TeamHoursKpi: React.FC<TeamHoursKpiProps> = ({
|
|
17
|
+
title,
|
|
18
|
+
data = { totalHours: 0, targetHours: 0, teamMembers: 0, avgHoursPerMember: 0, variance: 0 },
|
|
19
|
+
style,
|
|
20
|
+
}) => {
|
|
21
|
+
const hoursPercent = Math.round((data.totalHours / data.targetHours) * 100);
|
|
22
|
+
const isAboveTarget = data.totalHours > data.targetHours;
|
|
23
|
+
const varianceSign = data.variance >= 0 ? '+' : '';
|
|
24
|
+
return (
|
|
25
|
+
<div className="bg-linear-to-br from-white to-blue-50 rounded-lg shadow-sm p-4 border border-blue-100" style={style}>
|
|
26
|
+
<h3 className="text-sm font-medium text-gray-700 mb-4">{title}</h3>
|
|
27
|
+
<div className="space-y-4">
|
|
28
|
+
<div className="space-y-2">
|
|
29
|
+
<div className="flex justify-between items-center">
|
|
30
|
+
<span className="text-xs font-medium text-gray-700">Horas Registradas</span>
|
|
31
|
+
<span className={`text-sm font-bold px-2 py-1 rounded-full ${isAboveTarget ? 'bg-green-100 text-green-700' : hoursPercent >= 90 ? 'bg-blue-100 text-blue-700' : 'bg-yellow-100 text-yellow-700'}`}>{hoursPercent}%</span>
|
|
32
|
+
</div>
|
|
33
|
+
<div className="relative w-full h-4 bg-gray-200 rounded-full overflow-hidden">
|
|
34
|
+
<div className={`h-full transition-all duration-500 ease-out rounded-full ${isAboveTarget ? 'bg-linear-to-r from-green-400 to-green-600' : 'bg-linear-to-r from-blue-400 to-blue-600'}`} style={{ width: `${Math.min(hoursPercent, 100)}%` }} />
|
|
35
|
+
</div>
|
|
36
|
+
<div className="flex justify-between text-xs text-gray-600"><span>{data.totalHours}h registrado</span><span>{data.targetHours}h meta</span></div>
|
|
37
|
+
</div>
|
|
38
|
+
<div className="grid grid-cols-2 gap-2 bg-blue-50 rounded-lg p-3 border border-blue-100">
|
|
39
|
+
<div><div className="text-xs text-gray-600 mb-1">Colaboradores</div><div className="text-2xl font-bold text-blue-600">{data.teamMembers}</div></div>
|
|
40
|
+
<div><div className="text-xs text-gray-600 mb-1">Média/Pessoa</div><div className="text-2xl font-bold text-blue-600">{data.avgHoursPerMember}h</div></div>
|
|
41
|
+
</div>
|
|
42
|
+
<div className="flex items-center gap-2 p-3 bg-linear-to-r from-blue-100 to-blue-50 rounded-lg border border-blue-200">
|
|
43
|
+
<TrendingUp className={`w-5 h-5 ${isAboveTarget ? 'text-green-600' : 'text-blue-600'}`} />
|
|
44
|
+
<div><div className="text-xs text-gray-600">Variação da Meta</div><div className={`text-sm font-bold ${isAboveTarget ? 'text-green-600' : 'text-blue-600'}`}>{varianceSign}{data.variance}%</div></div>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export default TeamHoursKpi;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { CheckCircle2, Clock, AlertTriangle } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
interface TeamPendingApprovalsKpiProps {
|
|
7
|
+
slug: string;
|
|
8
|
+
title: string;
|
|
9
|
+
roleSlug: string;
|
|
10
|
+
width?: number;
|
|
11
|
+
height?: number;
|
|
12
|
+
data?: { totalPending: number; urgent: number; timesheets: number; expenses: number; requests: number; avgWaitingDays: number };
|
|
13
|
+
style?: React.CSSProperties;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const TeamPendingApprovalsKpi: React.FC<TeamPendingApprovalsKpiProps> = ({
|
|
17
|
+
title,
|
|
18
|
+
data = { totalPending: 0, urgent: 0, timesheets: 0, expenses: 0, requests: 0, avgWaitingDays: 0 },
|
|
19
|
+
style,
|
|
20
|
+
}) => {
|
|
21
|
+
const hasUrgent = data.urgent > 0;
|
|
22
|
+
return (
|
|
23
|
+
<div className={`bg-linear-to-br from-white to-red-50 rounded-lg shadow-sm p-4 border ${hasUrgent ? 'border-red-400 border-2' : 'border-red-100'} transition-all`} style={style}>
|
|
24
|
+
<div className="flex items-center justify-between mb-4">
|
|
25
|
+
<h3 className="text-sm font-medium text-gray-700">{title}</h3>
|
|
26
|
+
{hasUrgent && <div className="relative inline-block"><div className="absolute inset-0 bg-red-600 rounded-full animate-pulse" style={{ opacity: 0.25 }} /><AlertTriangle className="w-5 h-5 text-red-600 relative" /></div>}
|
|
27
|
+
</div>
|
|
28
|
+
<div className="bg-linear-to-r from-red-500 to-red-600 rounded-lg p-4 text-white mb-3">
|
|
29
|
+
<div className="flex items-center justify-between">
|
|
30
|
+
<div><div className="text-sm opacity-90">Aguardando Aprovação</div><div className="text-4xl font-bold">{data.totalPending}</div></div>
|
|
31
|
+
<CheckCircle2 className="w-12 h-12 opacity-30" />
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
<div className="grid grid-cols-3 gap-2 mb-3">
|
|
35
|
+
<div className="bg-blue-50 rounded-lg p-2 border border-blue-200"><div className="text-2xl font-bold text-blue-600">{data.timesheets}</div><div className="text-xs text-gray-600">Timesheets</div></div>
|
|
36
|
+
<div className="bg-green-50 rounded-lg p-2 border border-green-200"><div className="text-2xl font-bold text-green-600">{data.expenses}</div><div className="text-xs text-gray-600">Despesas</div></div>
|
|
37
|
+
<div className="bg-purple-50 rounded-lg p-2 border border-purple-200"><div className="text-2xl font-bold text-purple-600">{data.requests}</div><div className="text-xs text-gray-600">Solicitações</div></div>
|
|
38
|
+
</div>
|
|
39
|
+
<div className="space-y-2 text-xs border-t border-red-100 pt-3">
|
|
40
|
+
<div className="flex items-center justify-between">
|
|
41
|
+
<div className="flex items-center gap-2"><AlertTriangle className="w-4 h-4 text-red-600" /><span className="text-gray-600">Urgentes:</span></div>
|
|
42
|
+
<span className={`font-bold px-2 py-1 rounded ${data.urgent > 5 ? 'bg-red-100 text-red-700' : 'bg-yellow-100 text-yellow-700'}`}>{data.urgent}</span>
|
|
43
|
+
</div>
|
|
44
|
+
<div className="flex items-center justify-between">
|
|
45
|
+
<div className="flex items-center gap-2"><Clock className="w-4 h-4 text-gray-400" /><span className="text-gray-600">Tempo médio:</span></div>
|
|
46
|
+
<span className="font-semibold text-gray-900">{data.avgWaitingDays} dias</span>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default TeamPendingApprovalsKpi;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { BarChart3, AlertCircle } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
interface TeamUtilizationOverviewProps {
|
|
7
|
+
slug: string;
|
|
8
|
+
title: string;
|
|
9
|
+
roleSlug: string;
|
|
10
|
+
width?: number;
|
|
11
|
+
height?: number;
|
|
12
|
+
data?: { overUtilized: number; wellUtilized: number; underUtilized: number; idle: number };
|
|
13
|
+
style?: React.CSSProperties;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const TeamUtilizationOverview: React.FC<TeamUtilizationOverviewProps> = ({
|
|
17
|
+
title,
|
|
18
|
+
data = { overUtilized: 0, wellUtilized: 0, underUtilized: 0, idle: 0 },
|
|
19
|
+
style,
|
|
20
|
+
}) => {
|
|
21
|
+
const total = data.overUtilized + data.wellUtilized + data.underUtilized + data.idle;
|
|
22
|
+
const categories = [
|
|
23
|
+
{ label: 'Sobre-utilizado', count: data.overUtilized, emoji: '🔴', percent: total > 0 ? Math.round((data.overUtilized / total) * 100) : 0, color: 'bg-red-500' },
|
|
24
|
+
{ label: 'Bem utilizado', count: data.wellUtilized, emoji: '🟢', percent: total > 0 ? Math.round((data.wellUtilized / total) * 100) : 0, color: 'bg-green-500' },
|
|
25
|
+
{ label: 'Sub-utilizado', count: data.underUtilized, emoji: '🟡', percent: total > 0 ? Math.round((data.underUtilized / total) * 100) : 0, color: 'bg-yellow-500' },
|
|
26
|
+
{ label: 'Inativo', count: data.idle, emoji: '⚪', percent: total > 0 ? Math.round((data.idle / total) * 100) : 0, color: 'bg-gray-400' },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div className="bg-linear-to-br from-white to-lime-50 rounded-lg shadow-sm p-4 border border-lime-100" style={style}>
|
|
31
|
+
<div className="flex items-center justify-between mb-4"><h3 className="text-sm font-medium text-gray-700">{title}</h3><BarChart3 className="w-4 h-4 text-lime-600" /></div>
|
|
32
|
+
<div className="space-y-4">
|
|
33
|
+
<div className="space-y-3">
|
|
34
|
+
{categories.map((cat) => (
|
|
35
|
+
<div key={cat.label} className="space-y-1">
|
|
36
|
+
<div className="flex items-center justify-between">
|
|
37
|
+
<span className="text-xs font-medium text-gray-700">{cat.emoji} {cat.label}</span>
|
|
38
|
+
<span className="text-xs font-bold text-gray-900">{cat.count} ({cat.percent}%)</span>
|
|
39
|
+
</div>
|
|
40
|
+
<div className="relative w-full h-3 bg-gray-100 rounded-full overflow-hidden"><div className={`h-full ${cat.color} transition-all duration-500 ease-out`} style={{ width: `${cat.percent}%` }} /></div>
|
|
41
|
+
</div>
|
|
42
|
+
))}
|
|
43
|
+
</div>
|
|
44
|
+
{data.overUtilized > 0 && (
|
|
45
|
+
<div className="bg-red-50 border border-red-300 rounded-lg p-3 flex gap-2">
|
|
46
|
+
<AlertCircle className="w-4 h-4 text-red-600 flex-shrink-0 mt-0.5" />
|
|
47
|
+
<div className="text-xs text-red-700">
|
|
48
|
+
<p className="font-semibold">{data.overUtilized} colaborador(es) sobre-utilizado(s)</p>
|
|
49
|
+
<p className="text-red-600 mt-1">Considere rebalanceamento de projetos</p>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
)}
|
|
53
|
+
<div className="bg-linear-to-r from-lime-50 to-lime-100 rounded-lg p-3 border border-lime-200 text-center">
|
|
54
|
+
<p className="text-xs text-gray-600 mb-1">Total de Colaboradores</p>
|
|
55
|
+
<p className="text-2xl font-bold text-lime-700">{total}</p>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export default TeamUtilizationOverview;
|