@burdenoff/microfe-crewfoundry 2026.704.6 → 2026.707.1
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/dist/crewfoundry/CrewFoundryRoutes.js +51 -43
- package/dist/crewfoundry/CrewFoundryRoutes.js.map +1 -1
- package/dist/crewfoundry/components/forms/formValues.js +5 -2
- package/dist/crewfoundry/components/forms/formValues.js.map +1 -1
- package/dist/crewfoundry/components/viz/AllocationBar.js +68 -0
- package/dist/crewfoundry/components/viz/AllocationBar.js.map +1 -0
- package/dist/crewfoundry/components/viz/AreaChart.js +102 -0
- package/dist/crewfoundry/components/viz/AreaChart.js.map +1 -0
- package/dist/crewfoundry/components/viz/AvatarStack.js +36 -0
- package/dist/crewfoundry/components/viz/AvatarStack.js.map +1 -0
- package/dist/crewfoundry/components/viz/BarChart.js +83 -0
- package/dist/crewfoundry/components/viz/BarChart.js.map +1 -0
- package/dist/crewfoundry/components/viz/CertificateCard.js +118 -0
- package/dist/crewfoundry/components/viz/CertificateCard.js.map +1 -0
- package/dist/crewfoundry/components/viz/DonutChart.js +109 -0
- package/dist/crewfoundry/components/viz/DonutChart.js.map +1 -0
- package/dist/crewfoundry/components/viz/HBarFunnel.js +54 -0
- package/dist/crewfoundry/components/viz/HBarFunnel.js.map +1 -0
- package/dist/crewfoundry/components/viz/Heatmap.js +91 -0
- package/dist/crewfoundry/components/viz/Heatmap.js.map +1 -0
- package/dist/crewfoundry/components/viz/KanbanBoard.js +50 -0
- package/dist/crewfoundry/components/viz/KanbanBoard.js.map +1 -0
- package/dist/crewfoundry/components/viz/LineChart.js +123 -0
- package/dist/crewfoundry/components/viz/LineChart.js.map +1 -0
- package/dist/crewfoundry/components/viz/OrgTree.js +83 -0
- package/dist/crewfoundry/components/viz/OrgTree.js.map +1 -0
- package/dist/crewfoundry/components/viz/PathSteps.js +68 -0
- package/dist/crewfoundry/components/viz/PathSteps.js.map +1 -0
- package/dist/crewfoundry/components/viz/ProgressRing.js +68 -0
- package/dist/crewfoundry/components/viz/ProgressRing.js.map +1 -0
- package/dist/crewfoundry/components/viz/RatingStars.js +51 -0
- package/dist/crewfoundry/components/viz/RatingStars.js.map +1 -0
- package/dist/crewfoundry/components/viz/SegmentedTabs.js +69 -0
- package/dist/crewfoundry/components/viz/SegmentedTabs.js.map +1 -0
- package/dist/crewfoundry/components/viz/SkillBar.js +3 -0
- package/dist/crewfoundry/components/viz/Sparkline.js +74 -0
- package/dist/crewfoundry/components/viz/Sparkline.js.map +1 -0
- package/dist/crewfoundry/components/viz/StatCard.js +60 -0
- package/dist/crewfoundry/components/viz/StatCard.js.map +1 -0
- package/dist/crewfoundry/components/viz/StepProgress.js +3 -0
- package/dist/crewfoundry/components/viz/Timeline.js +66 -0
- package/dist/crewfoundry/components/viz/Timeline.js.map +1 -0
- package/dist/crewfoundry/components/viz/index.js +23 -0
- package/dist/crewfoundry/components/viz/lib/Avatar.js +30 -0
- package/dist/crewfoundry/components/viz/lib/Avatar.js.map +1 -0
- package/dist/crewfoundry/components/viz/lib/CartesianFrame.js +119 -0
- package/dist/crewfoundry/components/viz/lib/CartesianFrame.js.map +1 -0
- package/dist/crewfoundry/components/viz/lib/ResponsiveSvg.js +35 -0
- package/dist/crewfoundry/components/viz/lib/ResponsiveSvg.js.map +1 -0
- package/dist/crewfoundry/components/viz/lib/format.js +21 -0
- package/dist/crewfoundry/components/viz/lib/format.js.map +1 -0
- package/dist/crewfoundry/components/viz/lib/geometry.js +69 -0
- package/dist/crewfoundry/components/viz/lib/geometry.js.map +1 -0
- package/dist/crewfoundry/components/viz/lib/hooks.js +39 -0
- package/dist/crewfoundry/components/viz/lib/hooks.js.map +1 -0
- package/dist/crewfoundry/components/viz/lib/palette.js +27 -0
- package/dist/crewfoundry/components/viz/lib/palette.js.map +1 -0
- package/dist/crewfoundry/lib/analytics.js +323 -0
- package/dist/crewfoundry/lib/analytics.js.map +1 -0
- package/dist/crewfoundry/lib/companyFinance.js +16 -0
- package/dist/crewfoundry/lib/companyFinance.js.map +1 -0
- package/dist/crewfoundry/pages/AnalyticsAnalyticsPagePage.js +226 -283
- package/dist/crewfoundry/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsEmployeeAnalyticsPagePage.js +107 -120
- package/dist/crewfoundry/pages/AnalyticsEmployeeAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js +122 -162
- package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.js +121 -158
- package/dist/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js +316 -211
- package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.js +75 -27
- package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js +167 -165
- package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js +168 -109
- package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardDashboardPagePage.js +401 -290
- package/dist/crewfoundry/pages/DashboardDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HelpApiDocumentationPagePage.js +0 -41
- package/dist/crewfoundry/pages/HelpApiDocumentationPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringApplicationsPagePage.js +84 -75
- package/dist/crewfoundry/pages/HiringApplicationsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringCandidatesPagePage.js +368 -174
- package/dist/crewfoundry/pages/HiringCandidatesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringCreateJobPagePage.js +236 -201
- package/dist/crewfoundry/pages/HiringCreateJobPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringInterviewsPagePage.js +369 -196
- package/dist/crewfoundry/pages/HiringInterviewsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js +269 -270
- package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringJobsPagePage.js +210 -110
- package/dist/crewfoundry/pages/HiringJobsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrDepartmentsPagePage.js +216 -178
- package/dist/crewfoundry/pages/HrDepartmentsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrEmployeeProfilePagePage.js +385 -0
- package/dist/crewfoundry/pages/HrEmployeeProfilePagePage.js.map +1 -0
- package/dist/crewfoundry/pages/HrEmployeesPagePage.js +189 -130
- package/dist/crewfoundry/pages/HrEmployeesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js +397 -241
- package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrPayrollPagePage.js +262 -147
- package/dist/crewfoundry/pages/HrPayrollPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js +345 -167
- package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCertificatesPagePage.js +98 -24
- package/dist/crewfoundry/pages/LearningCertificatesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js +1 -1
- package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js +289 -108
- package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCoursesPagePage.js +221 -114
- package/dist/crewfoundry/pages/LearningCoursesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js +134 -135
- package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js +148 -97
- package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningMyLearningPagePage.js +264 -157
- package/dist/crewfoundry/pages/LearningMyLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js +85 -93
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js +211 -122
- package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js +94 -54
- package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js +289 -140
- package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js +169 -79
- package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js +296 -125
- package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsResourcingPagePage.js +187 -0
- package/dist/crewfoundry/pages/ProjectsResourcingPagePage.js.map +1 -0
- package/dist/crewfoundry/pages/TeamsTeamDetailPagePage.js +140 -78
- package/dist/crewfoundry/pages/TeamsTeamDetailPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamDirectoryPagePage.js +93 -43
- package/dist/crewfoundry/pages/TeamsTeamDirectoryPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamsPagePage.js +250 -98
- package/dist/crewfoundry/pages/TeamsTeamsPagePage.js.map +1 -1
- package/dist/crewfoundry/store/crewfoundryStore.js +142 -184
- package/dist/crewfoundry/store/crewfoundryStore.js.map +1 -1
- package/dist/crewfoundry/utils/cn.js +10 -0
- package/dist/crewfoundry/utils/cn.js.map +1 -0
- package/dist/crewfoundry/utils/courseProgress.js +46 -0
- package/dist/crewfoundry/utils/courseProgress.js.map +1 -0
- package/dist/node_modules/clsx/dist/clsx.js +18 -0
- package/dist/node_modules/clsx/dist/clsx.js.map +1 -0
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +1716 -0
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { useCrewFoundryStore as e } from "../store/crewfoundryStore.js";
|
|
2
|
+
import { StatCard as t } from "../components/viz/StatCard.js";
|
|
3
|
+
import { Heatmap as n } from "../components/viz/Heatmap.js";
|
|
4
|
+
import { AllocationBar as r } from "../components/viz/AllocationBar.js";
|
|
5
|
+
import "../components/viz/index.js";
|
|
6
|
+
import { allocationByPerson as i, benchAndOverallocated as a, skillMatch as o, utilizationByPersonWeek as s } from "../lib/analytics.js";
|
|
7
|
+
import { useMemo as c } from "react";
|
|
8
|
+
import { AlertTriangle as l, ArrowLeft as u, TrendingUp as d, Users as f } from "lucide-react";
|
|
9
|
+
import { useNavigate as p } from "react-router-dom";
|
|
10
|
+
import { Fragment as m, jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
11
|
+
import { Badge as _, Button as v, Card as y, CardContent as b, CardDescription as x, CardHeader as S, CardTitle as C, GradientText as w, IllustratedEmptyState as T, PagePurpose as E, Spinner as D } from "@burdenoff/fe-libs/ui";
|
|
12
|
+
//#region src/crewfoundry/pages/ProjectsResourcingPagePage.tsx
|
|
13
|
+
var O = 8, k = 12;
|
|
14
|
+
function A() {
|
|
15
|
+
let A = p(), j = e((e) => e.timeEntries), M = e((e) => e.employees), N = e((e) => e.jobPostings), P = e((e) => e.candidates), F = e((e) => e.initialized), I = c(() => s(j, M, O), [j, M]), L = c(() => a(I, M), [I, M]), R = c(() => i(j, M).slice(0, k), [j, M]), z = c(() => N.find((e) => e.status === "active"), [N]), B = c(() => z ? o(z, M, P) : null, [
|
|
16
|
+
z,
|
|
17
|
+
M,
|
|
18
|
+
P
|
|
19
|
+
]);
|
|
20
|
+
if (!F) return /* @__PURE__ */ h("div", {
|
|
21
|
+
className: "flex min-h-[60vh] items-center justify-center p-6",
|
|
22
|
+
children: /* @__PURE__ */ h(D, { size: "lg" })
|
|
23
|
+
});
|
|
24
|
+
let V = I.hasData && I.weekRanges.length > 0 ? `${I.weekRanges[0]} → ${I.weekRanges[I.weekRanges.length - 1]}` : null;
|
|
25
|
+
return /* @__PURE__ */ g("div", {
|
|
26
|
+
className: "space-y-6 p-6",
|
|
27
|
+
children: [
|
|
28
|
+
/* @__PURE__ */ g(v, {
|
|
29
|
+
variant: "ghost",
|
|
30
|
+
size: "sm",
|
|
31
|
+
onClick: () => A("../projects/projects-page"),
|
|
32
|
+
children: [/* @__PURE__ */ h(u, { className: "mr-1 h-4 w-4" }), "Back to projects"]
|
|
33
|
+
}),
|
|
34
|
+
/* @__PURE__ */ g("div", {
|
|
35
|
+
className: "flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between",
|
|
36
|
+
children: [/* @__PURE__ */ g("div", { children: [
|
|
37
|
+
/* @__PURE__ */ h("p", {
|
|
38
|
+
className: "text-sm text-text-secondary",
|
|
39
|
+
children: "Skills-based staffing & utilization"
|
|
40
|
+
}),
|
|
41
|
+
/* @__PURE__ */ h("h1", {
|
|
42
|
+
className: "text-2xl font-semibold",
|
|
43
|
+
children: /* @__PURE__ */ h(w, { children: "Resourcing & Utilization" })
|
|
44
|
+
}),
|
|
45
|
+
/* @__PURE__ */ h(E, {
|
|
46
|
+
className: "mt-1",
|
|
47
|
+
children: "See who is over- or under-utilized week by week, how people are allocated across projects, and which candidates best match an open role — all from real logged time."
|
|
48
|
+
})
|
|
49
|
+
] }), /* @__PURE__ */ g("div", {
|
|
50
|
+
className: "grid grid-cols-1 gap-3 sm:grid-cols-3 lg:w-auto",
|
|
51
|
+
children: [
|
|
52
|
+
/* @__PURE__ */ h(t, {
|
|
53
|
+
label: "Avg utilization",
|
|
54
|
+
value: `${L.avgUtilization}%`,
|
|
55
|
+
icon: /* @__PURE__ */ h(d, { className: "h-4 w-4" })
|
|
56
|
+
}),
|
|
57
|
+
/* @__PURE__ */ h(t, {
|
|
58
|
+
label: "Bench",
|
|
59
|
+
value: L.bench,
|
|
60
|
+
icon: /* @__PURE__ */ h(f, { className: "h-4 w-4" })
|
|
61
|
+
}),
|
|
62
|
+
/* @__PURE__ */ h(t, {
|
|
63
|
+
label: "Over-allocated",
|
|
64
|
+
value: L.overAllocated,
|
|
65
|
+
icon: /* @__PURE__ */ h(l, { className: "h-4 w-4" })
|
|
66
|
+
})
|
|
67
|
+
]
|
|
68
|
+
})]
|
|
69
|
+
}),
|
|
70
|
+
/* @__PURE__ */ g("div", {
|
|
71
|
+
className: "grid gap-4 lg:grid-cols-3",
|
|
72
|
+
children: [/* @__PURE__ */ g(y, {
|
|
73
|
+
className: "lg:col-span-2",
|
|
74
|
+
children: [/* @__PURE__ */ g(S, { children: [/* @__PURE__ */ h(C, {
|
|
75
|
+
className: "text-base",
|
|
76
|
+
children: "Utilization heatmap"
|
|
77
|
+
}), /* @__PURE__ */ h(x, { children: V ? `Weekly utilization (Σ logged hours ÷ 40h capacity) · ${V}` : "Weekly utilization from logged time" })] }), /* @__PURE__ */ h(b, { children: I.hasData ? /* @__PURE__ */ g("div", {
|
|
78
|
+
className: "space-y-3",
|
|
79
|
+
children: [/* @__PURE__ */ h(n, {
|
|
80
|
+
rows: I.rows,
|
|
81
|
+
colLabels: I.weekLabels,
|
|
82
|
+
showValues: !0,
|
|
83
|
+
valueFormatter: (e) => `${e}%`,
|
|
84
|
+
title: "Per-person weekly utilization"
|
|
85
|
+
}), /* @__PURE__ */ h("ul", {
|
|
86
|
+
className: "flex flex-wrap gap-x-3 gap-y-1 text-xs text-text-muted",
|
|
87
|
+
children: I.weekLabels.map((e, t) => /* @__PURE__ */ g("li", { children: [
|
|
88
|
+
/* @__PURE__ */ h("span", {
|
|
89
|
+
className: "font-medium text-text-secondary",
|
|
90
|
+
children: e
|
|
91
|
+
}),
|
|
92
|
+
" ",
|
|
93
|
+
I.weekRanges[t]
|
|
94
|
+
] }, e))
|
|
95
|
+
})]
|
|
96
|
+
}) : /* @__PURE__ */ h(T, {
|
|
97
|
+
illustration: "empty-data",
|
|
98
|
+
title: "No time entries yet",
|
|
99
|
+
description: "Once your team logs time, per-person weekly utilization will appear here. Nothing is shown for weeks without real entries."
|
|
100
|
+
}) })]
|
|
101
|
+
}), /* @__PURE__ */ g(y, { children: [/* @__PURE__ */ g(S, { children: [/* @__PURE__ */ h(C, {
|
|
102
|
+
className: "text-base",
|
|
103
|
+
children: "Skills match for open role"
|
|
104
|
+
}), B ? /* @__PURE__ */ g(x, { children: [
|
|
105
|
+
B.roleTitle,
|
|
106
|
+
" ·",
|
|
107
|
+
" ",
|
|
108
|
+
B.source === "employee" ? "internal team" : "candidate pool"
|
|
109
|
+
] }) : null] }), /* @__PURE__ */ h(b, {
|
|
110
|
+
className: "space-y-4",
|
|
111
|
+
children: z ? B && B.requiredSkills.length > 0 ? /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ h("div", {
|
|
112
|
+
className: "flex flex-wrap gap-1.5",
|
|
113
|
+
children: B.requiredSkills.map((e) => /* @__PURE__ */ h(_, {
|
|
114
|
+
variant: "outline",
|
|
115
|
+
className: "text-[11px]",
|
|
116
|
+
children: e
|
|
117
|
+
}, e))
|
|
118
|
+
}), B.matches.length > 0 ? /* @__PURE__ */ h("ol", {
|
|
119
|
+
className: "space-y-3",
|
|
120
|
+
children: B.matches.map((e, t) => /* @__PURE__ */ g("li", {
|
|
121
|
+
className: "rounded-[var(--radius-card)] border border-border-subtle bg-bg-surface p-3",
|
|
122
|
+
children: [
|
|
123
|
+
/* @__PURE__ */ g("div", {
|
|
124
|
+
className: "flex items-baseline justify-between gap-2",
|
|
125
|
+
children: [/* @__PURE__ */ g("span", {
|
|
126
|
+
className: "text-sm font-medium text-text-primary",
|
|
127
|
+
children: [
|
|
128
|
+
t + 1,
|
|
129
|
+
". ",
|
|
130
|
+
e.name
|
|
131
|
+
]
|
|
132
|
+
}), /* @__PURE__ */ g("span", {
|
|
133
|
+
className: "text-sm font-semibold tabular-nums text-status-success-text",
|
|
134
|
+
children: [e.matchPercent, "% match"]
|
|
135
|
+
})]
|
|
136
|
+
}),
|
|
137
|
+
/* @__PURE__ */ h("div", {
|
|
138
|
+
className: "mt-2 flex flex-wrap gap-1",
|
|
139
|
+
children: e.matchedSkills.map((e) => /* @__PURE__ */ h(_, {
|
|
140
|
+
variant: "secondary",
|
|
141
|
+
className: "text-[10px]",
|
|
142
|
+
children: e
|
|
143
|
+
}, e))
|
|
144
|
+
}),
|
|
145
|
+
e.detail ? /* @__PURE__ */ h("p", {
|
|
146
|
+
className: "mt-2 text-xs text-text-muted",
|
|
147
|
+
children: e.detail
|
|
148
|
+
}) : null
|
|
149
|
+
]
|
|
150
|
+
}, e.id))
|
|
151
|
+
}) : /* @__PURE__ */ h(T, {
|
|
152
|
+
illustration: "empty-people",
|
|
153
|
+
title: "No people match yet",
|
|
154
|
+
description: "No recorded skills overlap this role’s required skills."
|
|
155
|
+
})] }) : /* @__PURE__ */ h(T, {
|
|
156
|
+
illustration: "empty-data",
|
|
157
|
+
title: "No skill tags on this role",
|
|
158
|
+
description: "Add recognised skill keywords to this role’s requirements to rank candidate matches."
|
|
159
|
+
}) : /* @__PURE__ */ h(T, {
|
|
160
|
+
illustration: "empty-search",
|
|
161
|
+
title: "No active roles",
|
|
162
|
+
description: "Publish a job posting to match candidates against its required skills."
|
|
163
|
+
})
|
|
164
|
+
})] })]
|
|
165
|
+
}),
|
|
166
|
+
/* @__PURE__ */ g(y, { children: [/* @__PURE__ */ g(S, { children: [/* @__PURE__ */ h(C, {
|
|
167
|
+
className: "text-base",
|
|
168
|
+
children: "People allocation"
|
|
169
|
+
}), /* @__PURE__ */ h(x, { children: R.length > 0 ? `Project split for the latest week with logged time (${R[0].weekLabel})` : "Per-person project allocation from logged time" })] }), /* @__PURE__ */ h(b, {
|
|
170
|
+
className: "space-y-5",
|
|
171
|
+
children: R.length > 0 ? R.map((e) => /* @__PURE__ */ h(r, {
|
|
172
|
+
segments: e.segments,
|
|
173
|
+
label: e.name,
|
|
174
|
+
rightLabel: `${e.utilization}% utilized`
|
|
175
|
+
}, e.employeeId)) : /* @__PURE__ */ h(T, {
|
|
176
|
+
illustration: "empty-data",
|
|
177
|
+
title: "No allocation to show",
|
|
178
|
+
description: "No time entries have been logged yet, so there is nothing to allocate across projects."
|
|
179
|
+
})
|
|
180
|
+
})] })
|
|
181
|
+
]
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
//#endregion
|
|
185
|
+
export { A as ProjectsResourcingPagePage };
|
|
186
|
+
|
|
187
|
+
//# sourceMappingURL=ProjectsResourcingPagePage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProjectsResourcingPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/ProjectsResourcingPagePage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n Spinner,\n} from '@burdenoff/fe-libs/ui';\nimport { AlertTriangle, ArrowLeft, TrendingUp, Users } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\nimport {\n AllocationBar,\n Heatmap,\n StatCard,\n} from '../components/viz';\nimport {\n allocationByPerson,\n benchAndOverallocated,\n skillMatch,\n utilizationByPersonWeek,\n} from '../lib/analytics';\n\nconst UTILIZATION_WEEKS = 8;\nconst MAX_ALLOCATION_ROWS = 12;\n\nexport function ProjectsResourcingPagePage() {\n const navigate = useNavigate();\n const timeEntries = useCrewFoundryStore((state) => state.timeEntries);\n const employees = useCrewFoundryStore((state) => state.employees);\n const jobPostings = useCrewFoundryStore((state) => state.jobPostings);\n const candidates = useCrewFoundryStore((state) => state.candidates);\n const initialized = useCrewFoundryStore((state) => state.initialized);\n\n const matrix = useMemo(\n () => utilizationByPersonWeek(timeEntries, employees, UTILIZATION_WEEKS),\n [timeEntries, employees]\n );\n const kpis = useMemo(() => benchAndOverallocated(matrix, employees), [matrix, employees]);\n const allocation = useMemo(\n () => allocationByPerson(timeEntries, employees).slice(0, MAX_ALLOCATION_ROWS),\n [timeEntries, employees]\n );\n const openJob = useMemo(\n () => jobPostings.find((job) => job.status === 'active'),\n [jobPostings]\n );\n // Prefer real employee skills; skillMatch falls back to the candidate pool\n // when no employee has skills recorded.\n const skills = useMemo(\n () => (openJob ? skillMatch(openJob, employees, candidates) : null),\n [openJob, employees, candidates]\n );\n\n if (!initialized) {\n return (\n <div className=\"flex min-h-[60vh] items-center justify-center p-6\">\n <Spinner size=\"lg\" />\n </div>\n );\n }\n\n const weekRangeCaption =\n matrix.hasData && matrix.weekRanges.length > 0\n ? `${matrix.weekRanges[0]} → ${matrix.weekRanges[matrix.weekRanges.length - 1]}`\n : null;\n\n return (\n <div className=\"space-y-6 p-6\">\n <Button variant=\"ghost\" size=\"sm\" onClick={() => navigate('../projects/projects-page')}>\n <ArrowLeft className=\"mr-1 h-4 w-4\" />\n Back to projects\n </Button>\n\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between\">\n <div>\n <p className=\"text-sm text-text-secondary\">Skills-based staffing & utilization</p>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Resourcing & Utilization</GradientText>\n </h1>\n <PagePurpose className=\"mt-1\">\n See who is over- or under-utilized week by week, how people are allocated across\n projects, and which candidates best match an open role — all from real logged time.\n </PagePurpose>\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-3 lg:w-auto\">\n <StatCard\n label=\"Avg utilization\"\n value={`${kpis.avgUtilization}%`}\n icon={<TrendingUp className=\"h-4 w-4\" />}\n />\n <StatCard label=\"Bench\" value={kpis.bench} icon={<Users className=\"h-4 w-4\" />} />\n <StatCard\n label=\"Over-allocated\"\n value={kpis.overAllocated}\n icon={<AlertTriangle className=\"h-4 w-4\" />}\n />\n </div>\n </div>\n\n <div className=\"grid gap-4 lg:grid-cols-3\">\n {/* Utilization heatmap */}\n <Card className=\"lg:col-span-2\">\n <CardHeader>\n <CardTitle className=\"text-base\">Utilization heatmap</CardTitle>\n <CardDescription>\n {weekRangeCaption\n ? `Weekly utilization (Σ logged hours ÷ 40h capacity) · ${weekRangeCaption}`\n : 'Weekly utilization from logged time'}\n </CardDescription>\n </CardHeader>\n <CardContent>\n {matrix.hasData ? (\n <div className=\"space-y-3\">\n <Heatmap\n rows={matrix.rows}\n colLabels={matrix.weekLabels}\n showValues\n valueFormatter={(value) => `${value}%`}\n title=\"Per-person weekly utilization\"\n />\n <ul className=\"flex flex-wrap gap-x-3 gap-y-1 text-xs text-text-muted\">\n {matrix.weekLabels.map((label, index) => (\n <li key={label}>\n <span className=\"font-medium text-text-secondary\">{label}</span>{' '}\n {matrix.weekRanges[index]}\n </li>\n ))}\n </ul>\n </div>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No time entries yet\"\n description=\"Once your team logs time, per-person weekly utilization will appear here. Nothing is shown for weeks without real entries.\"\n />\n )}\n </CardContent>\n </Card>\n\n {/* Skills match for open role */}\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Skills match for open role</CardTitle>\n {skills ? (\n <CardDescription>\n {skills.roleTitle} ·{' '}\n {skills.source === 'employee' ? 'internal team' : 'candidate pool'}\n </CardDescription>\n ) : null}\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {!openJob ? (\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No active roles\"\n description=\"Publish a job posting to match candidates against its required skills.\"\n />\n ) : skills && skills.requiredSkills.length > 0 ? (\n <>\n <div className=\"flex flex-wrap gap-1.5\">\n {skills.requiredSkills.map((skill) => (\n <Badge key={skill} variant=\"outline\" className=\"text-[11px]\">\n {skill}\n </Badge>\n ))}\n </div>\n {skills.matches.length > 0 ? (\n <ol className=\"space-y-3\">\n {skills.matches.map((match, index) => (\n <li\n key={match.id}\n className=\"rounded-[var(--radius-card)] border border-border-subtle bg-bg-surface p-3\"\n >\n <div className=\"flex items-baseline justify-between gap-2\">\n <span className=\"text-sm font-medium text-text-primary\">\n {index + 1}. {match.name}\n </span>\n <span className=\"text-sm font-semibold tabular-nums text-status-success-text\">\n {match.matchPercent}% match\n </span>\n </div>\n <div className=\"mt-2 flex flex-wrap gap-1\">\n {match.matchedSkills.map((skill) => (\n <Badge key={skill} variant=\"secondary\" className=\"text-[10px]\">\n {skill}\n </Badge>\n ))}\n </div>\n {match.detail ? (\n <p className=\"mt-2 text-xs text-text-muted\">{match.detail}</p>\n ) : null}\n </li>\n ))}\n </ol>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title=\"No people match yet\"\n description=\"No recorded skills overlap this role’s required skills.\"\n />\n )}\n </>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No skill tags on this role\"\n description=\"Add recognised skill keywords to this role’s requirements to rank candidate matches.\"\n />\n )}\n </CardContent>\n </Card>\n </div>\n\n {/* People allocation this week */}\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">People allocation</CardTitle>\n <CardDescription>\n {allocation.length > 0\n ? `Project split for the latest week with logged time (${allocation[0].weekLabel})`\n : 'Per-person project allocation from logged time'}\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-5\">\n {allocation.length > 0 ? (\n allocation.map((row) => (\n <AllocationBar\n key={row.employeeId}\n segments={row.segments}\n label={row.name}\n rightLabel={`${row.utilization}% utilized`}\n />\n ))\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No allocation to show\"\n description=\"No time entries have been logged yet, so there is nothing to allocate across projects.\"\n />\n )}\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,128 +1,183 @@
|
|
|
1
1
|
import { useCrewFoundryStore as e } from "../store/crewfoundryStore.js";
|
|
2
2
|
import { formatEnumLabel as t } from "../utils/formatEnumLabel.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
3
|
+
import { getInitials as n } from "../components/viz/lib/format.js";
|
|
4
|
+
import { AvatarStack as r } from "../components/viz/AvatarStack.js";
|
|
5
|
+
import "../components/viz/index.js";
|
|
6
|
+
import { BarChart3 as i, Calendar as a, FolderKanban as o, Settings as s, Star as c, Users as l } from "lucide-react";
|
|
7
|
+
import { useNavigate as u, useSearchParams as d } from "react-router-dom";
|
|
8
|
+
import { jsx as f, jsxs as p } from "react/jsx-runtime";
|
|
9
|
+
import { Badge as m, Card as h, CardContent as g, CardHeader as _, CardTitle as v, EmphasisPanel as y, GradientText as b, IllustratedEmptyState as x, NextSteps as S, PagePurpose as C, ResponsiveTable as w, Spinner as T } from "@burdenoff/fe-libs/ui";
|
|
7
10
|
//#region src/crewfoundry/pages/TeamsTeamDetailPagePage.tsx
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
function E({ name: e, src: t, size: r = 32 }) {
|
|
12
|
+
return t ? /* @__PURE__ */ f("img", {
|
|
13
|
+
src: t,
|
|
14
|
+
alt: e,
|
|
15
|
+
width: r,
|
|
16
|
+
height: r,
|
|
17
|
+
loading: "lazy",
|
|
18
|
+
className: "shrink-0 rounded-full object-cover",
|
|
19
|
+
style: {
|
|
20
|
+
width: r,
|
|
21
|
+
height: r
|
|
22
|
+
}
|
|
23
|
+
}) : /* @__PURE__ */ f("span", {
|
|
24
|
+
"aria-hidden": "true",
|
|
25
|
+
className: "inline-flex shrink-0 items-center justify-center rounded-full bg-bg-elevated font-medium text-text-secondary",
|
|
26
|
+
style: {
|
|
27
|
+
width: r,
|
|
28
|
+
height: r,
|
|
29
|
+
fontSize: Math.round(r * .36)
|
|
30
|
+
},
|
|
31
|
+
children: n(e)
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function D() {
|
|
35
|
+
let n = u(), [D] = d(), { teams: O, employees: k, projects: A, initialized: j } = e(), M = D.get("id"), N = O.find((e) => e.id === M) ?? O[0];
|
|
36
|
+
if (!j && !N) return /* @__PURE__ */ f("div", {
|
|
11
37
|
className: "flex min-h-[60vh] items-center justify-center p-6",
|
|
12
|
-
children: /* @__PURE__ */
|
|
38
|
+
children: /* @__PURE__ */ f(T, { size: "lg" })
|
|
13
39
|
});
|
|
14
|
-
if (!
|
|
40
|
+
if (!N) return /* @__PURE__ */ f("div", {
|
|
15
41
|
className: "p-6",
|
|
16
|
-
children: /* @__PURE__ */
|
|
42
|
+
children: /* @__PURE__ */ f(x, {
|
|
17
43
|
illustration: "empty-search",
|
|
18
44
|
title: "Team not found",
|
|
19
45
|
description: "This team is no longer available."
|
|
20
46
|
})
|
|
21
47
|
});
|
|
22
|
-
let
|
|
23
|
-
|
|
48
|
+
let P = k.filter((e) => N.members.includes(e.id)), F = A.filter((e) => e.teamId === N.id), I = F.filter((e) => e.status === "active").length, L = k.find((e) => e.id === N.leadId), R = P.map((e) => ({
|
|
49
|
+
name: `${e.firstName} ${e.lastName}`.trim(),
|
|
50
|
+
avatarUrl: e.avatar,
|
|
51
|
+
role: e.position
|
|
52
|
+
}));
|
|
53
|
+
return /* @__PURE__ */ p("div", {
|
|
24
54
|
className: "space-y-6 p-6 max-w-4xl",
|
|
25
55
|
children: [
|
|
26
|
-
/* @__PURE__ */
|
|
56
|
+
/* @__PURE__ */ p("div", {
|
|
27
57
|
className: "flex items-center justify-between",
|
|
28
|
-
children: [/* @__PURE__ */
|
|
29
|
-
/* @__PURE__ */
|
|
58
|
+
children: [/* @__PURE__ */ p("div", { children: [
|
|
59
|
+
/* @__PURE__ */ f("h1", {
|
|
30
60
|
className: "text-2xl font-semibold",
|
|
31
|
-
children: /* @__PURE__ */
|
|
61
|
+
children: /* @__PURE__ */ f(b, { children: N.name })
|
|
32
62
|
}),
|
|
33
|
-
/* @__PURE__ */
|
|
63
|
+
/* @__PURE__ */ f("p", {
|
|
34
64
|
className: "text-sm text-text-secondary",
|
|
35
|
-
children:
|
|
65
|
+
children: N.description
|
|
36
66
|
}),
|
|
37
|
-
/* @__PURE__ */
|
|
67
|
+
/* @__PURE__ */ f(C, {
|
|
38
68
|
className: "mt-2",
|
|
39
69
|
children: "A complete view of one team — who's on it, what they're working on, and how they're performing — so you can support the group and keep its projects on track."
|
|
70
|
+
}),
|
|
71
|
+
/* @__PURE__ */ p("div", {
|
|
72
|
+
className: "mt-3 flex flex-wrap items-center gap-x-4 gap-y-2",
|
|
73
|
+
children: [/* @__PURE__ */ p("span", {
|
|
74
|
+
className: "flex items-center gap-2 text-xs text-text-secondary",
|
|
75
|
+
children: [
|
|
76
|
+
"Led by",
|
|
77
|
+
/* @__PURE__ */ f(E, {
|
|
78
|
+
name: L ? `${L.firstName} ${L.lastName}` : N.leadName,
|
|
79
|
+
src: L?.avatar,
|
|
80
|
+
size: 22
|
|
81
|
+
}),
|
|
82
|
+
/* @__PURE__ */ f("span", {
|
|
83
|
+
className: "font-medium text-text-primary",
|
|
84
|
+
children: L ? `${L.firstName} ${L.lastName}` : N.leadName || "—"
|
|
85
|
+
})
|
|
86
|
+
]
|
|
87
|
+
}), R.length > 0 ? /* @__PURE__ */ f(r, {
|
|
88
|
+
people: R,
|
|
89
|
+
max: 6,
|
|
90
|
+
size: 26
|
|
91
|
+
}) : null]
|
|
40
92
|
})
|
|
41
|
-
] }), /* @__PURE__ */
|
|
42
|
-
variant:
|
|
43
|
-
children: t(
|
|
93
|
+
] }), /* @__PURE__ */ f(m, {
|
|
94
|
+
variant: N.status === "active" ? "default" : "secondary",
|
|
95
|
+
children: t(N.status)
|
|
44
96
|
})]
|
|
45
97
|
}),
|
|
46
|
-
/* @__PURE__ */
|
|
98
|
+
/* @__PURE__ */ f(S, {
|
|
47
99
|
storageKey: "crewfoundry-team-details",
|
|
48
100
|
steps: [
|
|
49
101
|
{
|
|
50
102
|
id: "analytics",
|
|
51
103
|
label: "View team analytics",
|
|
52
104
|
description: "See productivity and workload trends for this team.",
|
|
53
|
-
icon: /* @__PURE__ */
|
|
54
|
-
onClick: () =>
|
|
105
|
+
icon: /* @__PURE__ */ f(i, { className: "h-4 w-4" }),
|
|
106
|
+
onClick: () => n("../teams/team-analytics-page")
|
|
55
107
|
},
|
|
56
108
|
{
|
|
57
109
|
id: "settings",
|
|
58
110
|
label: "Adjust team settings",
|
|
59
111
|
description: "Manage membership, permissions, and preferences.",
|
|
60
|
-
icon: /* @__PURE__ */
|
|
61
|
-
onClick: () =>
|
|
112
|
+
icon: /* @__PURE__ */ f(s, { className: "h-4 w-4" }),
|
|
113
|
+
onClick: () => n(`../teams/team-settings-page?id=${encodeURIComponent(N.id)}`)
|
|
62
114
|
},
|
|
63
115
|
{
|
|
64
116
|
id: "directory",
|
|
65
117
|
label: "Browse all teams",
|
|
66
118
|
description: "Explore how the rest of the organization is structured.",
|
|
67
|
-
icon: /* @__PURE__ */
|
|
68
|
-
onClick: () =>
|
|
119
|
+
icon: /* @__PURE__ */ f(l, { className: "h-4 w-4" }),
|
|
120
|
+
onClick: () => n("../teams/team-directory-page")
|
|
69
121
|
}
|
|
70
122
|
]
|
|
71
123
|
}),
|
|
72
|
-
/* @__PURE__ */
|
|
124
|
+
/* @__PURE__ */ p("div", {
|
|
73
125
|
className: "grid gap-4 md:grid-cols-4",
|
|
74
126
|
children: [
|
|
75
|
-
/* @__PURE__ */
|
|
127
|
+
/* @__PURE__ */ f(h, { children: /* @__PURE__ */ p(g, {
|
|
76
128
|
className: "p-4 text-center",
|
|
77
129
|
children: [
|
|
78
|
-
/* @__PURE__ */
|
|
79
|
-
/* @__PURE__ */
|
|
130
|
+
/* @__PURE__ */ f(l, { className: "h-5 w-5 mx-auto text-text-secondary mb-1" }),
|
|
131
|
+
/* @__PURE__ */ f("p", {
|
|
80
132
|
className: "text-lg font-bold",
|
|
81
|
-
children:
|
|
133
|
+
children: N.memberCount
|
|
82
134
|
}),
|
|
83
|
-
/* @__PURE__ */
|
|
135
|
+
/* @__PURE__ */ f("p", {
|
|
84
136
|
className: "text-xs text-text-secondary",
|
|
85
137
|
children: "Members"
|
|
86
138
|
})
|
|
87
139
|
]
|
|
88
140
|
}) }),
|
|
89
|
-
/* @__PURE__ */
|
|
141
|
+
/* @__PURE__ */ f(h, { children: /* @__PURE__ */ p(g, {
|
|
90
142
|
className: "p-4 text-center",
|
|
91
143
|
children: [
|
|
92
|
-
/* @__PURE__ */
|
|
93
|
-
/* @__PURE__ */
|
|
144
|
+
/* @__PURE__ */ f(o, { className: "h-5 w-5 mx-auto text-text-secondary mb-1" }),
|
|
145
|
+
/* @__PURE__ */ p("p", {
|
|
94
146
|
className: "text-lg font-bold",
|
|
95
|
-
children:
|
|
147
|
+
children: [I, /* @__PURE__ */ p("span", {
|
|
148
|
+
className: "text-sm font-normal text-text-secondary",
|
|
149
|
+
children: ["/", F.length]
|
|
150
|
+
})]
|
|
96
151
|
}),
|
|
97
|
-
/* @__PURE__ */
|
|
152
|
+
/* @__PURE__ */ f("p", {
|
|
98
153
|
className: "text-xs text-text-secondary",
|
|
99
|
-
children: "Projects"
|
|
154
|
+
children: "Active Projects"
|
|
100
155
|
})
|
|
101
156
|
]
|
|
102
157
|
}) }),
|
|
103
|
-
/* @__PURE__ */
|
|
158
|
+
/* @__PURE__ */ f(h, { children: /* @__PURE__ */ p(g, {
|
|
104
159
|
className: "p-4 text-center",
|
|
105
160
|
children: [
|
|
106
|
-
/* @__PURE__ */
|
|
107
|
-
/* @__PURE__ */
|
|
161
|
+
/* @__PURE__ */ f(c, { className: "h-5 w-5 mx-auto text-status-warning-text mb-1" }),
|
|
162
|
+
/* @__PURE__ */ f("p", {
|
|
108
163
|
className: "text-lg font-bold",
|
|
109
|
-
children:
|
|
164
|
+
children: N.leadName
|
|
110
165
|
}),
|
|
111
|
-
/* @__PURE__ */
|
|
166
|
+
/* @__PURE__ */ f("p", {
|
|
112
167
|
className: "text-xs text-text-secondary",
|
|
113
168
|
children: "Lead"
|
|
114
169
|
})
|
|
115
170
|
]
|
|
116
171
|
}) }),
|
|
117
|
-
/* @__PURE__ */
|
|
172
|
+
/* @__PURE__ */ f(h, { children: /* @__PURE__ */ p(g, {
|
|
118
173
|
className: "p-4 text-center",
|
|
119
174
|
children: [
|
|
120
|
-
/* @__PURE__ */
|
|
121
|
-
/* @__PURE__ */
|
|
175
|
+
/* @__PURE__ */ f(a, { className: "h-5 w-5 mx-auto text-text-secondary mb-1" }),
|
|
176
|
+
/* @__PURE__ */ f("p", {
|
|
122
177
|
className: "text-lg font-bold",
|
|
123
|
-
children:
|
|
178
|
+
children: N.createdAt
|
|
124
179
|
}),
|
|
125
|
-
/* @__PURE__ */
|
|
180
|
+
/* @__PURE__ */ f("p", {
|
|
126
181
|
className: "text-xs text-text-secondary",
|
|
127
182
|
children: "Created"
|
|
128
183
|
})
|
|
@@ -130,33 +185,40 @@ function w() {
|
|
|
130
185
|
}) })
|
|
131
186
|
]
|
|
132
187
|
}),
|
|
133
|
-
/* @__PURE__ */
|
|
188
|
+
/* @__PURE__ */ p(y, { children: [/* @__PURE__ */ f("div", {
|
|
134
189
|
className: "mb-2 text-lg font-semibold",
|
|
135
190
|
children: "Team Members"
|
|
136
|
-
}),
|
|
191
|
+
}), P.length === 0 ? /* @__PURE__ */ f(x, {
|
|
137
192
|
illustration: "empty-people",
|
|
138
193
|
title: "No members yet",
|
|
139
194
|
description: "Add people to this team to start collaborating."
|
|
140
|
-
}) : /* @__PURE__ */
|
|
195
|
+
}) : /* @__PURE__ */ f(w, {
|
|
141
196
|
columns: [
|
|
142
197
|
{
|
|
143
198
|
key: "name",
|
|
144
199
|
header: "Name",
|
|
145
200
|
priority: "primary",
|
|
146
|
-
cell: (e) => /* @__PURE__ */
|
|
147
|
-
className: "
|
|
148
|
-
children: [
|
|
149
|
-
e.firstName
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
201
|
+
cell: (e) => /* @__PURE__ */ p("span", {
|
|
202
|
+
className: "flex items-center gap-2.5",
|
|
203
|
+
children: [/* @__PURE__ */ f(E, {
|
|
204
|
+
name: `${e.firstName} ${e.lastName}`,
|
|
205
|
+
src: e.avatar,
|
|
206
|
+
size: 30
|
|
207
|
+
}), /* @__PURE__ */ p("span", {
|
|
208
|
+
className: "font-medium text-text-primary",
|
|
209
|
+
children: [
|
|
210
|
+
e.firstName,
|
|
211
|
+
" ",
|
|
212
|
+
e.lastName
|
|
213
|
+
]
|
|
214
|
+
})]
|
|
153
215
|
})
|
|
154
216
|
},
|
|
155
217
|
{
|
|
156
218
|
key: "position",
|
|
157
219
|
header: "Position",
|
|
158
220
|
priority: "secondary",
|
|
159
|
-
cell: (e) => /* @__PURE__ */
|
|
221
|
+
cell: (e) => /* @__PURE__ */ f("span", {
|
|
160
222
|
className: "text-text-secondary",
|
|
161
223
|
children: e.position
|
|
162
224
|
})
|
|
@@ -165,61 +227,61 @@ function w() {
|
|
|
165
227
|
key: "performance",
|
|
166
228
|
header: "Performance",
|
|
167
229
|
priority: "secondary",
|
|
168
|
-
cell: (e) => /* @__PURE__ */
|
|
230
|
+
cell: (e) => /* @__PURE__ */ p("span", {
|
|
169
231
|
className: "flex items-center gap-1 md:justify-center",
|
|
170
|
-
children: [/* @__PURE__ */
|
|
232
|
+
children: [/* @__PURE__ */ f(c, { className: "h-3 w-3 text-status-warning-text" }), e.performanceScore]
|
|
171
233
|
})
|
|
172
234
|
},
|
|
173
235
|
{
|
|
174
236
|
key: "status",
|
|
175
237
|
header: "Status",
|
|
176
238
|
priority: "primary",
|
|
177
|
-
cell: (e) => /* @__PURE__ */
|
|
239
|
+
cell: (e) => /* @__PURE__ */ f(m, {
|
|
178
240
|
variant: e.status === "active" ? "default" : "secondary",
|
|
179
241
|
className: "text-[10px]",
|
|
180
242
|
children: t(e.status)
|
|
181
243
|
})
|
|
182
244
|
}
|
|
183
245
|
],
|
|
184
|
-
data:
|
|
246
|
+
data: P,
|
|
185
247
|
rowKey: (e) => e.id
|
|
186
248
|
})] }),
|
|
187
|
-
|
|
249
|
+
F.length > 0 && /* @__PURE__ */ p(h, { children: [/* @__PURE__ */ f(_, { children: /* @__PURE__ */ f(v, { children: "Team Projects" }) }), /* @__PURE__ */ f(g, {
|
|
188
250
|
className: "space-y-3",
|
|
189
|
-
children:
|
|
251
|
+
children: F.map((e) => /* @__PURE__ */ p("div", {
|
|
190
252
|
role: "button",
|
|
191
253
|
tabIndex: 0,
|
|
192
254
|
"aria-label": `Open ${e.name} project detail`,
|
|
193
|
-
onClick: () =>
|
|
255
|
+
onClick: () => n(`../projects/project-details-page?id=${encodeURIComponent(e.id)}`),
|
|
194
256
|
onKeyDown: (t) => {
|
|
195
|
-
(t.key === "Enter" || t.key === " ") && (t.preventDefault(),
|
|
257
|
+
(t.key === "Enter" || t.key === " ") && (t.preventDefault(), n(`../projects/project-details-page?id=${encodeURIComponent(e.id)}`));
|
|
196
258
|
},
|
|
197
259
|
className: "flex items-center justify-between rounded border p-3 cursor-pointer hover:bg-bg-sunken/50 focus-visible:outline-none focus-visible:shadow-[var(--shadow-focus)]",
|
|
198
|
-
children: [/* @__PURE__ */
|
|
260
|
+
children: [/* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("p", {
|
|
199
261
|
className: "text-sm font-medium",
|
|
200
262
|
children: e.name
|
|
201
|
-
}), /* @__PURE__ */
|
|
263
|
+
}), /* @__PURE__ */ p("p", {
|
|
202
264
|
className: "text-xs text-text-secondary",
|
|
203
265
|
children: [
|
|
204
266
|
e.startDate,
|
|
205
267
|
" — ",
|
|
206
268
|
e.endDate
|
|
207
269
|
]
|
|
208
|
-
})] }), /* @__PURE__ */
|
|
270
|
+
})] }), /* @__PURE__ */ p("div", {
|
|
209
271
|
className: "flex items-center gap-2",
|
|
210
272
|
children: [
|
|
211
|
-
/* @__PURE__ */
|
|
273
|
+
/* @__PURE__ */ f("div", {
|
|
212
274
|
className: "w-24 h-2 bg-bg-sunken rounded overflow-hidden",
|
|
213
|
-
children: /* @__PURE__ */
|
|
275
|
+
children: /* @__PURE__ */ f("div", {
|
|
214
276
|
className: "h-2 bg-action-primary-bg rounded",
|
|
215
277
|
style: { width: `${e.progress}%` }
|
|
216
278
|
})
|
|
217
279
|
}),
|
|
218
|
-
/* @__PURE__ */
|
|
280
|
+
/* @__PURE__ */ p("span", {
|
|
219
281
|
className: "text-xs",
|
|
220
282
|
children: [e.progress, "%"]
|
|
221
283
|
}),
|
|
222
|
-
/* @__PURE__ */
|
|
284
|
+
/* @__PURE__ */ f(m, {
|
|
223
285
|
variant: e.status === "active" ? "default" : "secondary",
|
|
224
286
|
className: "text-[10px]",
|
|
225
287
|
children: t(e.status)
|
|
@@ -232,6 +294,6 @@ function w() {
|
|
|
232
294
|
});
|
|
233
295
|
}
|
|
234
296
|
//#endregion
|
|
235
|
-
export {
|
|
297
|
+
export { D as TeamsTeamDetailPagePage };
|
|
236
298
|
|
|
237
299
|
//# sourceMappingURL=TeamsTeamDetailPagePage.js.map
|