@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,51 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { BarChart3 } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
interface ProjectEffort {
|
|
7
|
+
name: string;
|
|
8
|
+
hours: number;
|
|
9
|
+
percent: number;
|
|
10
|
+
color: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface EffortByProjectProps {
|
|
14
|
+
slug: string;
|
|
15
|
+
title: string;
|
|
16
|
+
roleSlug: string;
|
|
17
|
+
width?: number;
|
|
18
|
+
height?: number;
|
|
19
|
+
data?: { projects: ProjectEffort[]; totalHours: number };
|
|
20
|
+
style?: React.CSSProperties;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const EffortByProject: React.FC<EffortByProjectProps> = ({
|
|
24
|
+
title,
|
|
25
|
+
data = { projects: [], totalHours: 0 },
|
|
26
|
+
style,
|
|
27
|
+
}) => {
|
|
28
|
+
const colorMap = ['bg-blue-500', 'bg-purple-500', 'bg-pink-500', 'bg-green-500', 'bg-orange-500'];
|
|
29
|
+
return (
|
|
30
|
+
<div className="bg-linear-to-br from-white to-pink-50 rounded-lg shadow-sm p-4 border border-pink-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-pink-600" /></div>
|
|
32
|
+
<div className="space-y-3">
|
|
33
|
+
{data.projects.length === 0 ? (
|
|
34
|
+
<div className="text-center py-6 text-gray-500"><BarChart3 className="w-8 h-8 mx-auto mb-2 opacity-50" /><p className="text-sm">Nenhum projeto com esforço</p></div>
|
|
35
|
+
) : (
|
|
36
|
+
<>
|
|
37
|
+
{data.projects.slice(0, 5).map((project, idx) => (
|
|
38
|
+
<div key={idx} className="space-y-1">
|
|
39
|
+
<div className="flex justify-between items-center"><span className="text-xs font-medium text-gray-900 truncate">{project.name}</span><span className="text-xs font-bold text-gray-700">{project.hours}h ({project.percent}%)</span></div>
|
|
40
|
+
<div className="relative w-full h-4 bg-gray-100 rounded-full overflow-hidden"><div className={`h-full ${colorMap[idx % colorMap.length]} transition-all`} style={{ width: `${project.percent}%` }} /></div>
|
|
41
|
+
</div>
|
|
42
|
+
))}
|
|
43
|
+
<div className="pt-2 border-t border-pink-100 text-center"><p className="text-xs text-gray-600 mb-1">Esforço Total</p><p className="text-2xl font-bold text-pink-600">{data.totalHours}h</p></div>
|
|
44
|
+
</>
|
|
45
|
+
)}
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export default EffortByProject;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Users } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
interface DepartmentHeadcount {
|
|
7
|
+
name: string;
|
|
8
|
+
count: number;
|
|
9
|
+
percentage: number;
|
|
10
|
+
maxCapacity: number;
|
|
11
|
+
utilization: number;
|
|
12
|
+
icon: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface HeadcountByAreaProps {
|
|
16
|
+
slug: string;
|
|
17
|
+
title: string;
|
|
18
|
+
roleSlug: string;
|
|
19
|
+
width?: number;
|
|
20
|
+
height?: number;
|
|
21
|
+
data?: { departments: DepartmentHeadcount[]; totalHeadcount: number };
|
|
22
|
+
style?: React.CSSProperties;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const HeadcountByArea: React.FC<HeadcountByAreaProps> = ({
|
|
26
|
+
title,
|
|
27
|
+
data = { departments: [], totalHeadcount: 0 },
|
|
28
|
+
style,
|
|
29
|
+
}) => {
|
|
30
|
+
const colorMap = ['blue', 'indigo', 'purple', 'pink', 'red', 'orange', 'yellow', 'green'];
|
|
31
|
+
return (
|
|
32
|
+
<div className="bg-linear-to-br from-white to-blue-50 rounded-lg shadow-sm p-4 border border-blue-100" style={style}>
|
|
33
|
+
<div className="flex items-center justify-between mb-4"><h3 className="text-sm font-medium text-gray-700">{title}</h3><Users className="w-4 h-4 text-blue-600" /></div>
|
|
34
|
+
<div className="space-y-3">
|
|
35
|
+
{data.departments.length === 0 ? (
|
|
36
|
+
<div className="text-center py-6 text-gray-500"><Users className="w-8 h-8 mx-auto mb-2 opacity-50" /><p className="text-sm">Nenhuma área com colaboradores</p></div>
|
|
37
|
+
) : (
|
|
38
|
+
<>
|
|
39
|
+
{data.departments.map((dept, idx) => (
|
|
40
|
+
<div key={idx} className="space-y-1">
|
|
41
|
+
<div className="flex items-center justify-between">
|
|
42
|
+
<div className="flex items-center gap-2 flex-1"><span className="text-lg">{dept.icon}</span><span className="text-xs font-medium text-gray-900 truncate">{dept.name}</span></div>
|
|
43
|
+
<span className="text-xs font-bold text-gray-700 ml-2">{dept.count}</span>
|
|
44
|
+
</div>
|
|
45
|
+
<div className="relative w-full h-4 bg-gray-100 rounded-full overflow-hidden border border-gray-200"><div className={`h-full bg-${colorMap[idx % colorMap.length]}-500 transition-all`} style={{ width: `${(dept.count / Math.max(...data.departments.map(d => d.count))) * 100}%` }} /></div>
|
|
46
|
+
<div className="flex justify-between text-xs text-gray-600"><span>{dept.percentage}% total</span><span>{dept.utilization}% capacidade</span></div>
|
|
47
|
+
</div>
|
|
48
|
+
))}
|
|
49
|
+
<div className="pt-3 border-t border-blue-100 bg-blue-50 rounded-lg p-3 text-center"><p className="text-xs text-blue-700 mb-1">Total de Colaboradores</p><p className="text-3xl font-bold text-blue-600">{data.totalHeadcount}</p></div>
|
|
50
|
+
</>
|
|
51
|
+
)}
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export default HeadcountByArea;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { CheckCircle2, AlertCircle, Clock, TrendingUp } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
interface ManagedProjectsStatusProps {
|
|
7
|
+
slug: string;
|
|
8
|
+
title: string;
|
|
9
|
+
roleSlug: string;
|
|
10
|
+
width?: number;
|
|
11
|
+
height?: number;
|
|
12
|
+
data?: { onTrack: number; atRisk: number; delayed: number; completed: number };
|
|
13
|
+
style?: React.CSSProperties;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const ManagedProjectsStatus: React.FC<ManagedProjectsStatusProps> = ({
|
|
17
|
+
title,
|
|
18
|
+
data = { onTrack: 0, atRisk: 0, delayed: 0, completed: 0 },
|
|
19
|
+
style,
|
|
20
|
+
}) => {
|
|
21
|
+
const total = data.onTrack + data.atRisk + data.delayed + data.completed;
|
|
22
|
+
const healthPercent = total > 0 ? Math.round(((data.onTrack + data.completed) / total) * 100) : 0;
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div className="bg-linear-to-br from-white to-cyan-50 rounded-lg shadow-sm p-4 border border-cyan-100" style={style}>
|
|
26
|
+
<div className="flex items-center justify-between mb-4"><h3 className="text-sm font-medium text-gray-700">{title}</h3><TrendingUp className="w-4 h-4 text-cyan-600" /></div>
|
|
27
|
+
<div className="bg-linear-to-r from-cyan-500 to-blue-600 rounded-lg p-4 text-white mb-3">
|
|
28
|
+
<div className="flex items-center justify-between">
|
|
29
|
+
<div><div className="text-xs opacity-90">Saúde dos Projetos</div><div className="text-3xl font-bold">{healthPercent}%</div></div>
|
|
30
|
+
<div className="text-4xl">📊</div>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
<div className="grid grid-cols-2 gap-2 mb-3">
|
|
34
|
+
<div className="bg-green-50 rounded-lg p-3 border border-green-200"><div className="text-2xl font-bold text-green-600">{data.onTrack}</div><div className="text-xs text-gray-600">No Prazo</div></div>
|
|
35
|
+
<div className="bg-yellow-50 rounded-lg p-3 border border-yellow-200"><div className="text-2xl font-bold text-yellow-600">{data.atRisk}</div><div className="text-xs text-gray-600">Em Risco</div></div>
|
|
36
|
+
<div className="bg-red-50 rounded-lg p-3 border border-red-200"><div className="text-2xl font-bold text-red-600">{data.delayed}</div><div className="text-xs text-gray-600">Atrasado</div></div>
|
|
37
|
+
<div className="bg-blue-50 rounded-lg p-3 border border-blue-200"><div className="text-2xl font-bold text-blue-600">{data.completed}</div><div className="text-xs text-gray-600">Concluído</div></div>
|
|
38
|
+
</div>
|
|
39
|
+
<div className="space-y-2 bg-cyan-50 rounded-lg p-3 border border-cyan-200">
|
|
40
|
+
<div className="text-xs font-medium text-gray-700 mb-2">Distribuição</div>
|
|
41
|
+
<div className="flex gap-1 h-8 rounded-lg overflow-hidden">
|
|
42
|
+
{data.onTrack > 0 && <div className="bg-green-500 hover:brightness-110 transition-all" style={{ width: `${(data.onTrack / total) * 100}%` }} />}
|
|
43
|
+
{data.atRisk > 0 && <div className="bg-yellow-500 hover:brightness-110 transition-all" style={{ width: `${(data.atRisk / total) * 100}%` }} />}
|
|
44
|
+
{data.delayed > 0 && <div className="bg-red-500 hover:brightness-110 transition-all" style={{ width: `${(data.delayed / total) * 100}%` }} />}
|
|
45
|
+
{data.completed > 0 && <div className="bg-blue-500 hover:brightness-110 transition-all" style={{ width: `${(data.completed / total) * 100}%` }} />}
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
<div className="text-center text-xs text-gray-600 pt-2 border-t border-cyan-100"><span className="font-semibold text-gray-900">{total}</span> projetos sob supervisão</div>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
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 React from 'react';
|
|
10
|
+
import { Clock, CheckCircle2, AlertCircle } from 'lucide-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">
|
|
45
|
+
{totalHours}
|
|
46
|
+
</span>
|
|
47
|
+
<span className="text-sm text-gray-600">
|
|
48
|
+
de {data.plannedHours}h
|
|
49
|
+
</span>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
{/* Progress bar */}
|
|
53
|
+
<div className="space-y-1">
|
|
54
|
+
<div className="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
|
|
55
|
+
<div
|
|
56
|
+
className="bg-linear-to-r from-blue-500 to-blue-600 h-full rounded-full transition-all"
|
|
57
|
+
style={{ width: `${Math.min(completionPercent, 100)}%` }}
|
|
58
|
+
/>
|
|
59
|
+
</div>
|
|
60
|
+
<div className="flex justify-between text-xs text-gray-600">
|
|
61
|
+
<span>{completionPercent}% completo</span>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
{/* Breakdown */}
|
|
66
|
+
<div className="pt-2 border-t border-gray-200 space-y-1 text-xs">
|
|
67
|
+
<div className="flex justify-between">
|
|
68
|
+
<span className="text-gray-600 flex items-center gap-1">
|
|
69
|
+
<CheckCircle2 className="w-3 h-3 text-green-600" />
|
|
70
|
+
Aprovado:
|
|
71
|
+
</span>
|
|
72
|
+
<span className="font-medium text-green-600">{data.approvedHours}h</span>
|
|
73
|
+
</div>
|
|
74
|
+
<div className="flex justify-between">
|
|
75
|
+
<span className="text-gray-600 flex items-center gap-1">
|
|
76
|
+
<AlertCircle className="w-3 h-3 text-yellow-600" />
|
|
77
|
+
Pendente:
|
|
78
|
+
</span>
|
|
79
|
+
<span className="font-medium text-yellow-600">{data.pendingHours}h</span>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export default MyHoursPeriodKpi;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { MessageSquare, Clock, AlertTriangle } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
interface MyOpenRequestsKpiProps {
|
|
7
|
+
slug: string;
|
|
8
|
+
title: string;
|
|
9
|
+
roleSlug: string;
|
|
10
|
+
width?: number;
|
|
11
|
+
height?: number;
|
|
12
|
+
data?: { totalOpen: number; vacationRequests: number; scheduleAdjustments: number; expenseRequests: number; otherRequests: number };
|
|
13
|
+
style?: React.CSSProperties;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const MyOpenRequestsKpi: React.FC<MyOpenRequestsKpiProps> = ({
|
|
17
|
+
title,
|
|
18
|
+
data = { totalOpen: 0, vacationRequests: 0, scheduleAdjustments: 0, expenseRequests: 0, otherRequests: 0 },
|
|
19
|
+
style,
|
|
20
|
+
}) => {
|
|
21
|
+
const hasUrgent = data.totalOpen > 5;
|
|
22
|
+
const requests = [
|
|
23
|
+
{ label: 'Férias', count: data.vacationRequests, color: 'blue' },
|
|
24
|
+
{ label: 'Ajuste Jornada', count: data.scheduleAdjustments, color: 'purple' },
|
|
25
|
+
{ label: 'Despesas', count: data.expenseRequests, color: 'green' },
|
|
26
|
+
{ label: 'Outros', count: data.otherRequests, color: 'gray' },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div 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`} style={style}>
|
|
31
|
+
<div className="flex items-center justify-between mb-4">
|
|
32
|
+
<h3 className="text-sm font-medium text-gray-700">{title}</h3>
|
|
33
|
+
{hasUrgent && <div className="flex items-center gap-1 px-2 py-1 bg-orange-100 rounded-full"><AlertTriangle className="w-3 h-3 text-orange-600" /><span className="text-xs font-semibold text-orange-600">Atenção</span></div>}
|
|
34
|
+
</div>
|
|
35
|
+
<div className="bg-white rounded-lg p-3 border border-orange-100 mb-3 flex items-center gap-2">
|
|
36
|
+
<MessageSquare className="w-5 h-5 text-orange-600" />
|
|
37
|
+
<div><div className="text-xs text-gray-600">Solicitações abertas</div><div className="text-3xl font-bold text-orange-600">{data.totalOpen}</div></div>
|
|
38
|
+
</div>
|
|
39
|
+
<div className="grid grid-cols-2 gap-2">
|
|
40
|
+
{requests.map((req) => (
|
|
41
|
+
<div key={req.label} className={`bg-${req.color}-50 rounded-lg p-3 border border-${req.color}-200`}>
|
|
42
|
+
<div className="text-xs text-gray-600 mb-1">{req.label}</div>
|
|
43
|
+
<div className={`text-xl font-bold text-${req.color}-600`}>{req.count}</div>
|
|
44
|
+
</div>
|
|
45
|
+
))}
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export default MyOpenRequestsKpi;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { FileText, Clock, User } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
interface Request {
|
|
7
|
+
id: string;
|
|
8
|
+
type: string;
|
|
9
|
+
description: string;
|
|
10
|
+
submittedDate: string;
|
|
11
|
+
daysWaiting: number;
|
|
12
|
+
assignee?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface MyPendingRequestsListProps {
|
|
16
|
+
slug: string;
|
|
17
|
+
title: string;
|
|
18
|
+
roleSlug: string;
|
|
19
|
+
width?: number;
|
|
20
|
+
height?: number;
|
|
21
|
+
data?: { requests: Request[] };
|
|
22
|
+
style?: React.CSSProperties;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const MyPendingRequestsList: React.FC<MyPendingRequestsListProps> = ({
|
|
26
|
+
title,
|
|
27
|
+
data = { requests: [] },
|
|
28
|
+
style,
|
|
29
|
+
}) => {
|
|
30
|
+
const getTypeIcon = (type: string) => {
|
|
31
|
+
const icons: Record<string, string> = { vacation: '🏖️', schedule: '📅', expense: '💰', adjustment: '🔧' };
|
|
32
|
+
return icons[type] || '📋';
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div className="bg-linear-to-br from-white to-violet-50 rounded-lg shadow-sm p-4 border border-violet-100" style={style}>
|
|
37
|
+
<div className="flex items-center justify-between mb-4">
|
|
38
|
+
<h3 className="text-sm font-medium text-gray-700">{title}</h3>
|
|
39
|
+
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-violet-100 text-violet-800">{data.requests.length}</span>
|
|
40
|
+
</div>
|
|
41
|
+
<div className="space-y-2 max-h-72 overflow-y-auto">
|
|
42
|
+
{data.requests.length === 0 ? (
|
|
43
|
+
<div className="text-center py-8"><FileText className="w-8 h-8 text-gray-300 mx-auto mb-2" /><p className="text-sm text-gray-600">Nenhuma solicitação pendente</p></div>
|
|
44
|
+
) : (
|
|
45
|
+
data.requests.map((request) => (
|
|
46
|
+
<div key={request.id} className={`rounded-lg p-3 border transition-all ${request.daysWaiting > 14 ? 'bg-red-50 border-red-200' : request.daysWaiting > 7 ? 'bg-yellow-50 border-yellow-200' : 'bg-blue-50 border-blue-200'}`}>
|
|
47
|
+
<div className="flex items-start gap-2 mb-2">
|
|
48
|
+
<span className="text-lg">{getTypeIcon(request.type)}</span>
|
|
49
|
+
<div className="flex-1"><p className="text-sm font-medium text-gray-900">{request.description}</p></div>
|
|
50
|
+
</div>
|
|
51
|
+
<div className="grid grid-cols-2 gap-2 text-xs">
|
|
52
|
+
<div className="flex items-center gap-1 text-gray-700"><Clock className="w-3 h-3" /><span>{request.daysWaiting === 0 ? 'Hoje' : request.daysWaiting === 1 ? 'Ontem' : `${request.daysWaiting}d atrás`}</span></div>
|
|
53
|
+
{request.assignee && <div className="flex items-center gap-1 text-gray-700"><User className="w-3 h-3" /><span className="truncate">{request.assignee}</span></div>}
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
))
|
|
57
|
+
)}
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export default MyPendingRequestsList;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Briefcase, TrendingUp } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
interface MyProjectAllocationsKpiProps {
|
|
7
|
+
slug: string;
|
|
8
|
+
title: string;
|
|
9
|
+
roleSlug: string;
|
|
10
|
+
width?: number;
|
|
11
|
+
height?: number;
|
|
12
|
+
data?: {
|
|
13
|
+
activeAllocations: number;
|
|
14
|
+
totalCapacity: number;
|
|
15
|
+
usedCapacity: number;
|
|
16
|
+
projects: Array<{ name: string; allocation: number; color: string }>;
|
|
17
|
+
};
|
|
18
|
+
style?: React.CSSProperties;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const MyProjectAllocationsKpi: React.FC<MyProjectAllocationsKpiProps> = ({
|
|
22
|
+
title,
|
|
23
|
+
data = { activeAllocations: 0, totalCapacity: 100, usedCapacity: 0, projects: [] },
|
|
24
|
+
style,
|
|
25
|
+
}) => {
|
|
26
|
+
const capacityPercent = Math.round((data.usedCapacity / data.totalCapacity) * 100);
|
|
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
|
+
<h3 className="text-sm font-medium text-gray-700 mb-4">{title}</h3>
|
|
31
|
+
<div className="flex items-center gap-3 bg-indigo-50 rounded-lg p-3 mb-3">
|
|
32
|
+
<Briefcase className="w-5 h-5 text-indigo-600" />
|
|
33
|
+
<div>
|
|
34
|
+
<div className="text-xs text-gray-600">Projetos ativos</div>
|
|
35
|
+
<div className="text-2xl font-bold text-indigo-600">{data.activeAllocations}</div>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
<div className="space-y-2">
|
|
39
|
+
<div className="flex justify-between items-center">
|
|
40
|
+
<span className="text-xs font-medium text-gray-700">Capacidade</span>
|
|
41
|
+
<span className={`text-xs font-bold px-2 py-1 rounded-full ${capacityPercent > 85 ? 'bg-red-100 text-red-700' : capacityPercent > 70 ? 'bg-yellow-100 text-yellow-700' : 'bg-green-100 text-green-700'}`}>
|
|
42
|
+
{capacityPercent}%
|
|
43
|
+
</span>
|
|
44
|
+
</div>
|
|
45
|
+
<div className="relative w-full h-3 bg-gray-200 rounded-full overflow-hidden">
|
|
46
|
+
<div className="h-full bg-linear-to-r from-indigo-400 to-indigo-600 transition-all" style={{ width: `${Math.min(capacityPercent, 100)}%` }} />
|
|
47
|
+
</div>
|
|
48
|
+
<div className="flex justify-between text-xs text-gray-600">
|
|
49
|
+
<span>{data.usedCapacity}h usado</span>
|
|
50
|
+
<span>{data.totalCapacity}h total</span>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export default MyProjectAllocationsKpi;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Plus, Send, Clock, FileText } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
interface QuickAction {
|
|
7
|
+
id: string;
|
|
8
|
+
label: string;
|
|
9
|
+
icon: React.ReactNode;
|
|
10
|
+
color: string;
|
|
11
|
+
bgColor: string;
|
|
12
|
+
onClick?: () => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface MyQuickActionsProps {
|
|
16
|
+
slug: string;
|
|
17
|
+
title: string;
|
|
18
|
+
roleSlug: string;
|
|
19
|
+
width?: number;
|
|
20
|
+
height?: number;
|
|
21
|
+
data?: { actions?: QuickAction[] };
|
|
22
|
+
style?: React.CSSProperties;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const defaultActions: QuickAction[] = [
|
|
26
|
+
{ id: 'timesheet', label: 'Registrar Timesheet', icon: <Clock className="w-5 h-5" />, color: 'text-blue-600', bgColor: 'bg-blue-50 hover:bg-blue-100 border-blue-200' },
|
|
27
|
+
{ id: 'request', label: 'Nova Solicitação', icon: <Plus className="w-5 h-5" />, color: 'text-green-600', bgColor: 'bg-green-50 hover:bg-green-100 border-green-200' },
|
|
28
|
+
{ id: 'vacation', label: 'Solicitar Férias', icon: <Send className="w-5 h-5" />, color: 'text-purple-600', bgColor: 'bg-purple-50 hover:bg-purple-100 border-purple-200' },
|
|
29
|
+
{ id: 'expense', label: 'Lançar Despesa', icon: <FileText className="w-5 h-5" />, color: 'text-orange-600', bgColor: 'bg-orange-50 hover:bg-orange-100 border-orange-200' },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
const MyQuickActions: React.FC<MyQuickActionsProps> = ({
|
|
33
|
+
title,
|
|
34
|
+
data,
|
|
35
|
+
style,
|
|
36
|
+
}) => {
|
|
37
|
+
const actions = data?.actions ?? defaultActions;
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div className="bg-linear-to-br from-white to-emerald-50 rounded-lg shadow-sm p-4 border border-emerald-100" style={style}>
|
|
41
|
+
<h3 className="text-sm font-medium text-gray-700 mb-4">{title}</h3>
|
|
42
|
+
<div className="grid grid-cols-2 gap-2">
|
|
43
|
+
{actions.map((action) => (
|
|
44
|
+
<button key={action.id} onClick={action.onClick} className={`${action.bgColor} border rounded-lg p-3 transition-all transform hover:scale-105 hover:shadow-md active:scale-95`}>
|
|
45
|
+
<div className={`${action.color} mb-2 transition-transform hover:scale-110`}>{action.icon}</div>
|
|
46
|
+
<p className="text-xs font-medium text-gray-900 text-left">{action.label}</p>
|
|
47
|
+
</button>
|
|
48
|
+
))}
|
|
49
|
+
</div>
|
|
50
|
+
<div className="mt-4 pt-4 border-t border-emerald-100">
|
|
51
|
+
<p className="text-xs text-gray-600 mb-2">💡 Dicas úteis:</p>
|
|
52
|
+
<ul className="text-xs space-y-1 text-gray-700">
|
|
53
|
+
<li>✓ Complete seu timesheet toda semana</li>
|
|
54
|
+
<li>✓ Solicite férias com antecedência</li>
|
|
55
|
+
<li>✓ Registre despesas no prazo</li>
|
|
56
|
+
</ul>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export default MyQuickActions;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { AlertCircle, CheckCircle2, Clock } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
interface Deadline {
|
|
7
|
+
id: string;
|
|
8
|
+
title: string;
|
|
9
|
+
dueDate: string;
|
|
10
|
+
daysLeft: number;
|
|
11
|
+
priority: 'high' | 'medium' | 'low';
|
|
12
|
+
status: 'on-track' | 'at-risk' | 'critical';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface MyRelevantDeadlinesProps {
|
|
16
|
+
slug: string;
|
|
17
|
+
title: string;
|
|
18
|
+
roleSlug: string;
|
|
19
|
+
width?: number;
|
|
20
|
+
height?: number;
|
|
21
|
+
data?: { deadlines: Deadline[] };
|
|
22
|
+
style?: React.CSSProperties;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const MyRelevantDeadlines: React.FC<MyRelevantDeadlinesProps> = ({
|
|
26
|
+
title,
|
|
27
|
+
data = { deadlines: [] },
|
|
28
|
+
style,
|
|
29
|
+
}) => {
|
|
30
|
+
const criticalCount = data.deadlines.filter(d => d.status === 'critical').length;
|
|
31
|
+
const atRiskCount = data.deadlines.filter(d => d.status === 'at-risk').length;
|
|
32
|
+
|
|
33
|
+
const getStatusColor = (status: string) => {
|
|
34
|
+
switch (status) {
|
|
35
|
+
case 'critical': return { bg: 'bg-red-50', border: 'border-red-300', dot: 'bg-red-500 animate-pulse', text: 'text-red-700' };
|
|
36
|
+
case 'at-risk': return { bg: 'bg-yellow-50', border: 'border-yellow-300', dot: 'bg-yellow-500', text: 'text-yellow-700' };
|
|
37
|
+
default: return { bg: 'bg-green-50', border: 'border-green-300', dot: 'bg-green-500', text: 'text-green-700' };
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div className="bg-linear-to-br from-white to-red-50 rounded-lg shadow-sm p-4 border border-red-100" style={style}>
|
|
43
|
+
<div className="flex items-center justify-between mb-4">
|
|
44
|
+
<h3 className="text-sm font-medium text-gray-700">{title}</h3>
|
|
45
|
+
{(criticalCount > 0 || atRiskCount > 0) && <AlertCircle className="w-4 h-4 text-red-600 animate-pulse" />}
|
|
46
|
+
</div>
|
|
47
|
+
<div className="space-y-3">
|
|
48
|
+
{data.deadlines.length === 0 ? (
|
|
49
|
+
<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>
|
|
50
|
+
) : (
|
|
51
|
+
<>
|
|
52
|
+
{(criticalCount > 0 || atRiskCount > 0) && (
|
|
53
|
+
<div className="grid grid-cols-2 gap-2 mb-3">
|
|
54
|
+
{criticalCount > 0 && <div className="bg-red-100 rounded-lg p-2 border border-red-300"><div className="text-xs text-red-700 font-semibold">{criticalCount} crítico{criticalCount !== 1 ? 's' : ''}</div></div>}
|
|
55
|
+
{atRiskCount > 0 && <div className="bg-yellow-100 rounded-lg p-2 border border-yellow-300"><div className="text-xs text-yellow-700 font-semibold">{atRiskCount} em risco</div></div>}
|
|
56
|
+
</div>
|
|
57
|
+
)}
|
|
58
|
+
<div className="space-y-2 max-h-64 overflow-y-auto">
|
|
59
|
+
{data.deadlines.slice(0, 5).map((deadline) => {
|
|
60
|
+
const colors = getStatusColor(deadline.status);
|
|
61
|
+
return (
|
|
62
|
+
<div key={deadline.id} className={`${colors.bg} rounded-lg p-3 border ${colors.border} hover:shadow-sm transition-all`}>
|
|
63
|
+
<div className="flex items-start gap-2 mb-2">
|
|
64
|
+
<div className={`w-3 h-3 rounded-full ${colors.dot} mt-1`} />
|
|
65
|
+
<div className="flex-1"><p className="text-sm font-medium text-gray-900 truncate">{deadline.title}</p></div>
|
|
66
|
+
</div>
|
|
67
|
+
<div className="flex items-center justify-between text-xs">
|
|
68
|
+
<div className="flex items-center gap-1 text-gray-700"><Clock className="w-3 h-3" /><span>{deadline.dueDate}</span></div>
|
|
69
|
+
<span className={`font-semibold ${deadline.daysLeft <= 0 ? 'text-red-600' : deadline.daysLeft <= 3 ? 'text-orange-600' : 'text-gray-600'}`}>
|
|
70
|
+
{deadline.daysLeft <= 0 ? 'Vencido' : `${deadline.daysLeft}d`}
|
|
71
|
+
</span>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
);
|
|
75
|
+
})}
|
|
76
|
+
</div>
|
|
77
|
+
</>
|
|
78
|
+
)}
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export default MyRelevantDeadlines;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { CheckCircle2, Clock, AlertCircle } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
interface MyTimesheetStatusKpiProps {
|
|
7
|
+
slug: string;
|
|
8
|
+
title: string;
|
|
9
|
+
roleSlug: string;
|
|
10
|
+
width?: number;
|
|
11
|
+
height?: number;
|
|
12
|
+
data?: { pendingCount: number; approvedCount: number; rejectedCount: number };
|
|
13
|
+
style?: React.CSSProperties;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const MyTimesheetStatusKpi: React.FC<MyTimesheetStatusKpiProps> = ({
|
|
17
|
+
title,
|
|
18
|
+
data = { pendingCount: 0, approvedCount: 0, rejectedCount: 0 },
|
|
19
|
+
style,
|
|
20
|
+
}) => {
|
|
21
|
+
const total = data.pendingCount + data.approvedCount + data.rejectedCount;
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div className="bg-linear-to-br from-white to-blue-50 rounded-lg shadow-sm p-4 border border-blue-100 hover:shadow-md transition-shadow" style={style}>
|
|
25
|
+
<h3 className="text-sm font-medium text-gray-700 mb-4">{title}</h3>
|
|
26
|
+
<div className="grid grid-cols-3 gap-2">
|
|
27
|
+
<div className="bg-yellow-50 rounded-lg p-3 border border-yellow-200">
|
|
28
|
+
<div className="flex items-center gap-2 mb-2">
|
|
29
|
+
<Clock className="w-4 h-4 text-yellow-600" />
|
|
30
|
+
<span className="text-xs font-medium text-yellow-700">Pendente</span>
|
|
31
|
+
</div>
|
|
32
|
+
<div className="text-2xl font-bold text-yellow-600">{data.pendingCount}</div>
|
|
33
|
+
</div>
|
|
34
|
+
<div className="bg-green-50 rounded-lg p-3 border border-green-200">
|
|
35
|
+
<div className="flex items-center gap-2 mb-2">
|
|
36
|
+
<CheckCircle2 className="w-4 h-4 text-green-600" />
|
|
37
|
+
<span className="text-xs font-medium text-green-700">Aprovado</span>
|
|
38
|
+
</div>
|
|
39
|
+
<div className="text-2xl font-bold text-green-600">{data.approvedCount}</div>
|
|
40
|
+
</div>
|
|
41
|
+
<div className="bg-red-50 rounded-lg p-3 border border-red-200">
|
|
42
|
+
<div className="flex items-center gap-2 mb-2">
|
|
43
|
+
<AlertCircle className="w-4 h-4 text-red-600" />
|
|
44
|
+
<span className="text-xs font-medium text-red-700">Rejeitado</span>
|
|
45
|
+
</div>
|
|
46
|
+
<div className="text-2xl font-bold text-red-600">{data.rejectedCount}</div>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
<div className="pt-2 border-t border-blue-100 mt-3 space-y-2 text-xs">
|
|
50
|
+
<div className="flex justify-between">
|
|
51
|
+
<span className="text-gray-600">Total:</span>
|
|
52
|
+
<span className="font-semibold text-gray-900">{total}</span>
|
|
53
|
+
</div>
|
|
54
|
+
<div className="flex justify-between">
|
|
55
|
+
<span className="text-gray-600">Taxa aprovação:</span>
|
|
56
|
+
<span className={`font-semibold ${total > 0 && data.approvedCount / total > 0.8 ? 'text-green-600' : 'text-yellow-600'}`}>
|
|
57
|
+
{total > 0 ? Math.round((data.approvedCount / total) * 100) : 0}%
|
|
58
|
+
</span>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export default MyTimesheetStatusKpi;
|