@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
|
@@ -1,29 +1,32 @@
|
|
|
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 {
|
|
7
|
-
import {
|
|
3
|
+
import { LineChart as n } from "../components/viz/LineChart.js";
|
|
4
|
+
import { BarChart as r } from "../components/viz/BarChart.js";
|
|
5
|
+
import "../components/viz/index.js";
|
|
6
|
+
import { useMemo as i } from "react";
|
|
7
|
+
import { Briefcase as a, Clock as o, Target as s, Users as c } from "lucide-react";
|
|
8
|
+
import { Fragment as l, jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
9
|
+
import { Badge as f, Card as p, CardContent as m, CardDescription as h, CardHeader as g, CardTitle as _, GlassCard as v, GradientText as y, IllustratedEmptyState as b, PagePurpose as x, Pagination as S, ResponsiveTable as C } from "@burdenoff/fe-libs/ui";
|
|
10
|
+
import { usePagination as w } from "@burdenoff/fe-libs/shared/hooks";
|
|
8
11
|
//#region src/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.tsx
|
|
9
|
-
var
|
|
12
|
+
var T = (e) => {
|
|
10
13
|
let t = new Date(e);
|
|
11
14
|
return `${t.getFullYear()}-${String(t.getMonth()).padStart(2, "0")}`;
|
|
12
|
-
},
|
|
13
|
-
function
|
|
14
|
-
let { jobPostings:
|
|
15
|
+
}, E = (/* @__PURE__ */ new Date("2026-03-01")).getTime();
|
|
16
|
+
function D() {
|
|
17
|
+
let { jobPostings: D, candidates: O, interviews: k } = e(), A = i(() => O.length === 0 && k.length === 0 ? [] : Array.from(new Set([...O.map((e) => T(e.appliedDate)), ...k.map((e) => T(e.date))])).sort().slice(-6).map((e) => {
|
|
15
18
|
let [t, n] = e.split("-").map(Number), r = new Date(t, n, 1).toLocaleString("en-US", {
|
|
16
19
|
month: "short",
|
|
17
20
|
year: "2-digit"
|
|
18
|
-
}), i =
|
|
21
|
+
}), i = O.filter((t) => T(t.appliedDate) === e);
|
|
19
22
|
return {
|
|
20
23
|
month: r,
|
|
21
24
|
applications: i.length,
|
|
22
|
-
interviews:
|
|
25
|
+
interviews: k.filter((t) => T(t.date) === e).length,
|
|
23
26
|
hires: i.filter((e) => e.status === "hired").length
|
|
24
27
|
};
|
|
25
|
-
}), [
|
|
26
|
-
let e =
|
|
28
|
+
}), [O, k]), j = i(() => {
|
|
29
|
+
let e = O.reduce((e, t) => {
|
|
27
30
|
let n = t.source || "Unknown", r = e[n] || {
|
|
28
31
|
applications: 0,
|
|
29
32
|
hires: 0
|
|
@@ -36,236 +39,196 @@ function T() {
|
|
|
36
39
|
hires: t.hires,
|
|
37
40
|
conversionRate: Number((t.hires / Math.max(t.applications, 1) * 100).toFixed(1))
|
|
38
41
|
})).sort((e, t) => t.conversionRate - e.conversionRate);
|
|
39
|
-
}, [
|
|
40
|
-
let e =
|
|
42
|
+
}, [O]), M = i(() => {
|
|
43
|
+
let e = D.filter((e) => e.status === "active");
|
|
41
44
|
if (e.length === 0) return 0;
|
|
42
45
|
let t = e.reduce((e, t) => {
|
|
43
46
|
let n = new Date(t.postedDate).getTime();
|
|
44
|
-
return Number.isNaN(n) ? e : e + Math.max(0, (
|
|
47
|
+
return Number.isNaN(n) ? e : e + Math.max(0, (E - n) / (1e3 * 60 * 60 * 24));
|
|
45
48
|
}, 0);
|
|
46
49
|
return Math.round(t / e.length);
|
|
47
|
-
}, [
|
|
50
|
+
}, [D]), N = D.filter((e) => e.status === "active"), P = D.reduce((e, t) => e + t.applicants, 0), F = k.filter((e) => e.status === "scheduled").length, I = k.filter((e) => e.status === "completed").length, L = O.filter((e) => e.status === "offer").length, R = O.filter((e) => e.status === "hired").length, z = O.filter((e) => [
|
|
48
51
|
"interview",
|
|
49
52
|
"assessment",
|
|
50
53
|
"offer",
|
|
51
54
|
"hired"
|
|
52
|
-
].includes(e.status)).length,
|
|
55
|
+
].includes(e.status)).length, B = O.filter((e) => ["offer", "hired"].includes(e.status)).length, V = Math.round(B / Math.max(z, 1) * 100), H = Math.round(R / Math.max(L + R, 1) * 100), U = A.map((e) => ({
|
|
56
|
+
label: e.month,
|
|
57
|
+
value: e.applications
|
|
58
|
+
})), W = A.map((e) => e.hires), G = [
|
|
53
59
|
{
|
|
54
60
|
status: "New",
|
|
55
|
-
count:
|
|
61
|
+
count: O.filter((e) => e.status === "new").length,
|
|
56
62
|
color: "bg-text-secondary"
|
|
57
63
|
},
|
|
58
64
|
{
|
|
59
65
|
status: "Screening",
|
|
60
|
-
count:
|
|
66
|
+
count: O.filter((e) => e.status === "screening").length,
|
|
61
67
|
color: "bg-status-info-text"
|
|
62
68
|
},
|
|
63
69
|
{
|
|
64
70
|
status: "Interview",
|
|
65
|
-
count:
|
|
71
|
+
count: O.filter((e) => e.status === "interview").length,
|
|
66
72
|
color: "bg-status-info-text"
|
|
67
73
|
},
|
|
68
74
|
{
|
|
69
75
|
status: "Assessment",
|
|
70
|
-
count:
|
|
76
|
+
count: O.filter((e) => e.status === "assessment").length,
|
|
71
77
|
color: "bg-action-primary-bg"
|
|
72
78
|
},
|
|
73
79
|
{
|
|
74
80
|
status: "Offer",
|
|
75
|
-
count:
|
|
81
|
+
count: L,
|
|
76
82
|
color: "bg-status-warning-text"
|
|
77
83
|
},
|
|
78
84
|
{
|
|
79
85
|
status: "Hired",
|
|
80
|
-
count:
|
|
86
|
+
count: R,
|
|
81
87
|
color: "bg-status-success-text"
|
|
82
88
|
},
|
|
83
89
|
{
|
|
84
90
|
status: "Rejected",
|
|
85
|
-
count:
|
|
91
|
+
count: O.filter((e) => e.status === "rejected").length,
|
|
86
92
|
color: "bg-status-error-text"
|
|
87
93
|
}
|
|
88
|
-
],
|
|
89
|
-
|
|
94
|
+
], K = G.reduce((e, t) => e + t.count, 0), q = G.map((e) => ({
|
|
95
|
+
label: e.status,
|
|
96
|
+
value: e.count
|
|
97
|
+
})), J = [...N].sort((e, t) => t.applicants - e.applicants), { page: Y, pageSize: X, offset: Z, setPage: Q } = w({ initialPageSize: 8 }), $ = J.slice(Z, Z + X);
|
|
98
|
+
return /* @__PURE__ */ d("div", {
|
|
90
99
|
className: "space-y-6 p-6",
|
|
91
100
|
children: [
|
|
92
|
-
/* @__PURE__ */
|
|
101
|
+
/* @__PURE__ */ u("div", {
|
|
93
102
|
className: "flex items-center justify-between",
|
|
94
|
-
children: /* @__PURE__ */
|
|
95
|
-
/* @__PURE__ */
|
|
103
|
+
children: /* @__PURE__ */ d("div", { children: [
|
|
104
|
+
/* @__PURE__ */ u("h1", {
|
|
96
105
|
className: "text-2xl font-semibold",
|
|
97
|
-
children: /* @__PURE__ */
|
|
106
|
+
children: /* @__PURE__ */ u(y, { children: "Hiring Analytics" })
|
|
98
107
|
}),
|
|
99
|
-
/* @__PURE__ */
|
|
108
|
+
/* @__PURE__ */ u("p", {
|
|
100
109
|
className: "text-sm text-text-secondary",
|
|
101
110
|
children: "Recruitment performance and pipeline analysis"
|
|
102
111
|
}),
|
|
103
|
-
/* @__PURE__ */
|
|
112
|
+
/* @__PURE__ */ u(x, {
|
|
104
113
|
className: "mt-1",
|
|
105
114
|
children: "Measure how your hiring funnel performs — applications, time-to-hire, and source quality — to make recruiting faster and more effective."
|
|
106
115
|
})
|
|
107
116
|
] })
|
|
108
117
|
}),
|
|
109
|
-
/* @__PURE__ */
|
|
118
|
+
/* @__PURE__ */ d("div", {
|
|
110
119
|
className: "grid gap-4 md:grid-cols-2 lg:grid-cols-4",
|
|
111
120
|
children: [
|
|
112
|
-
/* @__PURE__ */
|
|
121
|
+
/* @__PURE__ */ d(v, {
|
|
113
122
|
treatment: "glass",
|
|
114
123
|
glow: !0,
|
|
115
124
|
className: "border-border-seam",
|
|
116
|
-
children: [/* @__PURE__ */
|
|
125
|
+
children: [/* @__PURE__ */ d(g, {
|
|
117
126
|
className: "flex flex-row items-center justify-between space-y-0 pb-2",
|
|
118
|
-
children: [/* @__PURE__ */
|
|
127
|
+
children: [/* @__PURE__ */ u(_, {
|
|
119
128
|
className: "text-sm font-medium",
|
|
120
129
|
children: "Active Postings"
|
|
121
|
-
}), /* @__PURE__ */
|
|
122
|
-
}), /* @__PURE__ */
|
|
130
|
+
}), /* @__PURE__ */ u(a, { className: "h-4 w-4 text-text-secondary" })]
|
|
131
|
+
}), /* @__PURE__ */ d(m, { children: [/* @__PURE__ */ u("div", {
|
|
123
132
|
className: "text-2xl font-bold tabular-nums",
|
|
124
|
-
children:
|
|
125
|
-
}), /* @__PURE__ */
|
|
133
|
+
children: N.length
|
|
134
|
+
}), /* @__PURE__ */ d("p", {
|
|
126
135
|
className: "text-xs text-text-secondary",
|
|
127
|
-
children: [
|
|
136
|
+
children: [P, " total applicants"]
|
|
128
137
|
})] })]
|
|
129
138
|
}),
|
|
130
|
-
/* @__PURE__ */
|
|
139
|
+
/* @__PURE__ */ d(v, {
|
|
131
140
|
treatment: "metric",
|
|
132
141
|
className: "border-border-seam",
|
|
133
|
-
children: [/* @__PURE__ */
|
|
142
|
+
children: [/* @__PURE__ */ d(g, {
|
|
134
143
|
className: "flex flex-row items-center justify-between space-y-0 pb-2",
|
|
135
|
-
children: [/* @__PURE__ */
|
|
144
|
+
children: [/* @__PURE__ */ u(_, {
|
|
136
145
|
className: "text-sm font-medium",
|
|
137
146
|
children: "Avg Days Open"
|
|
138
|
-
}), /* @__PURE__ */
|
|
139
|
-
}), /* @__PURE__ */
|
|
147
|
+
}), /* @__PURE__ */ u(o, { className: "h-4 w-4 text-text-secondary" })]
|
|
148
|
+
}), /* @__PURE__ */ d(m, { children: [/* @__PURE__ */ d("div", {
|
|
140
149
|
className: "text-2xl font-bold tabular-nums",
|
|
141
|
-
children: [
|
|
142
|
-
}), /* @__PURE__ */
|
|
150
|
+
children: [M, " days"]
|
|
151
|
+
}), /* @__PURE__ */ u("p", {
|
|
143
152
|
className: "text-xs text-text-secondary",
|
|
144
153
|
children: "Across active postings"
|
|
145
154
|
})] })]
|
|
146
155
|
}),
|
|
147
|
-
/* @__PURE__ */
|
|
156
|
+
/* @__PURE__ */ d(v, {
|
|
148
157
|
treatment: "metric",
|
|
149
158
|
className: "border-border-seam",
|
|
150
|
-
children: [/* @__PURE__ */
|
|
159
|
+
children: [/* @__PURE__ */ d(g, {
|
|
151
160
|
className: "flex flex-row items-center justify-between space-y-0 pb-2",
|
|
152
|
-
children: [/* @__PURE__ */
|
|
161
|
+
children: [/* @__PURE__ */ u(_, {
|
|
153
162
|
className: "text-sm font-medium",
|
|
154
163
|
children: "Interview to Offer"
|
|
155
|
-
}), /* @__PURE__ */
|
|
156
|
-
}), /* @__PURE__ */
|
|
164
|
+
}), /* @__PURE__ */ u(s, { className: "h-4 w-4 text-text-secondary" })]
|
|
165
|
+
}), /* @__PURE__ */ d(m, { children: [/* @__PURE__ */ d("div", {
|
|
157
166
|
className: "text-2xl font-bold tabular-nums",
|
|
158
|
-
children: [
|
|
159
|
-
}), /* @__PURE__ */
|
|
167
|
+
children: [V, "%"]
|
|
168
|
+
}), /* @__PURE__ */ d("p", {
|
|
160
169
|
className: "text-xs text-text-secondary",
|
|
161
170
|
children: [
|
|
162
|
-
|
|
171
|
+
F,
|
|
163
172
|
" scheduled, ",
|
|
164
|
-
|
|
173
|
+
I,
|
|
165
174
|
" completed"
|
|
166
175
|
]
|
|
167
176
|
})] })]
|
|
168
177
|
}),
|
|
169
|
-
/* @__PURE__ */
|
|
178
|
+
/* @__PURE__ */ d(v, {
|
|
170
179
|
treatment: "metric",
|
|
171
180
|
className: "border-border-seam",
|
|
172
|
-
children: [/* @__PURE__ */
|
|
181
|
+
children: [/* @__PURE__ */ d(g, {
|
|
173
182
|
className: "flex flex-row items-center justify-between space-y-0 pb-2",
|
|
174
|
-
children: [/* @__PURE__ */
|
|
183
|
+
children: [/* @__PURE__ */ u(_, {
|
|
175
184
|
className: "text-sm font-medium",
|
|
176
185
|
children: "Offer Acceptance"
|
|
177
|
-
}), /* @__PURE__ */ c
|
|
178
|
-
}), /* @__PURE__ */
|
|
186
|
+
}), /* @__PURE__ */ u(c, { className: "h-4 w-4 text-text-secondary" })]
|
|
187
|
+
}), /* @__PURE__ */ d(m, { children: [/* @__PURE__ */ d("div", {
|
|
179
188
|
className: "text-2xl font-bold tabular-nums",
|
|
180
|
-
children: [
|
|
181
|
-
}), /* @__PURE__ */
|
|
189
|
+
children: [H, "%"]
|
|
190
|
+
}), /* @__PURE__ */ d("p", {
|
|
182
191
|
className: "text-xs text-text-secondary",
|
|
183
|
-
children: [
|
|
192
|
+
children: [R, " hired to date"]
|
|
184
193
|
})] })]
|
|
185
194
|
})
|
|
186
195
|
]
|
|
187
196
|
}),
|
|
188
|
-
/* @__PURE__ */
|
|
197
|
+
/* @__PURE__ */ d("div", {
|
|
189
198
|
className: "grid gap-4 md:grid-cols-7",
|
|
190
|
-
children: [/* @__PURE__ */
|
|
199
|
+
children: [/* @__PURE__ */ d(p, {
|
|
191
200
|
className: "col-span-4",
|
|
192
|
-
children: [/* @__PURE__ */
|
|
193
|
-
className: "
|
|
194
|
-
children:
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
/* @__PURE__ */ c("div", {
|
|
205
|
-
className: "flex-1 h-5 bg-bg-sunken rounded overflow-hidden",
|
|
206
|
-
children: /* @__PURE__ */ c("div", {
|
|
207
|
-
className: "h-5 bg-action-primary-bg rounded",
|
|
208
|
-
style: { width: `${Math.round(e.applications / V * 100)}%` }
|
|
209
|
-
})
|
|
210
|
-
}),
|
|
211
|
-
/* @__PURE__ */ l("div", {
|
|
212
|
-
className: "flex gap-1 w-48 justify-end",
|
|
213
|
-
children: [
|
|
214
|
-
/* @__PURE__ */ l(u, {
|
|
215
|
-
variant: "outline",
|
|
216
|
-
className: "text-[10px]",
|
|
217
|
-
children: [e.applications, " apps"]
|
|
218
|
-
}),
|
|
219
|
-
/* @__PURE__ */ l(u, {
|
|
220
|
-
variant: "secondary",
|
|
221
|
-
className: "text-[10px]",
|
|
222
|
-
children: [e.interviews, " int"]
|
|
223
|
-
}),
|
|
224
|
-
/* @__PURE__ */ l(u, {
|
|
225
|
-
variant: "default",
|
|
226
|
-
className: "text-[10px]",
|
|
227
|
-
children: [e.hires, " hired"]
|
|
228
|
-
})
|
|
229
|
-
]
|
|
230
|
-
})
|
|
231
|
-
]
|
|
232
|
-
}, e.month))]
|
|
233
|
-
})]
|
|
234
|
-
}), /* @__PURE__ */ l(d, {
|
|
201
|
+
children: [/* @__PURE__ */ d(g, { children: [/* @__PURE__ */ u(_, { children: "Monthly Hiring Activity" }), /* @__PURE__ */ u(h, { children: "Applications, interviews, and hires over time" })] }), /* @__PURE__ */ u(m, { children: U.length === 0 ? /* @__PURE__ */ u("p", {
|
|
202
|
+
className: "py-6 text-center text-sm text-text-secondary",
|
|
203
|
+
children: "No hiring activity recorded yet."
|
|
204
|
+
}) : /* @__PURE__ */ u(n, {
|
|
205
|
+
series: U,
|
|
206
|
+
compare: W,
|
|
207
|
+
seriesLabel: "Applications",
|
|
208
|
+
compareLabel: "Hires",
|
|
209
|
+
height: 220,
|
|
210
|
+
valueFormatter: (e) => `${e}`
|
|
211
|
+
}) })]
|
|
212
|
+
}), /* @__PURE__ */ d(p, {
|
|
235
213
|
className: "col-span-3",
|
|
236
|
-
children: [/* @__PURE__ */
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}),
|
|
246
|
-
/* @__PURE__ */ c("div", {
|
|
247
|
-
className: "flex-1 h-3 bg-bg-sunken rounded overflow-hidden",
|
|
248
|
-
children: /* @__PURE__ */ c("div", {
|
|
249
|
-
className: `h-3 rounded ${e.color}`,
|
|
250
|
-
style: { width: `${U > 0 ? Math.max(Math.round(e.count / U * 100), 3) : 3}%` }
|
|
251
|
-
})
|
|
252
|
-
}),
|
|
253
|
-
/* @__PURE__ */ c("span", {
|
|
254
|
-
className: "text-sm font-medium w-6 text-right",
|
|
255
|
-
children: e.count
|
|
256
|
-
})
|
|
257
|
-
]
|
|
258
|
-
}, e.status))
|
|
259
|
-
})]
|
|
214
|
+
children: [/* @__PURE__ */ d(g, { children: [/* @__PURE__ */ u(_, { children: "Pipeline Status" }), /* @__PURE__ */ u(h, { children: "Current candidate distribution" })] }), /* @__PURE__ */ u(m, { children: K > 0 ? /* @__PURE__ */ u(r, {
|
|
215
|
+
series: q,
|
|
216
|
+
colorful: !0,
|
|
217
|
+
height: 220,
|
|
218
|
+
title: "Candidate pipeline by stage"
|
|
219
|
+
}) : /* @__PURE__ */ u("p", {
|
|
220
|
+
className: "py-6 text-center text-sm text-text-secondary",
|
|
221
|
+
children: "No candidates in the pipeline yet."
|
|
222
|
+
}) })]
|
|
260
223
|
})]
|
|
261
224
|
}),
|
|
262
|
-
/* @__PURE__ */
|
|
225
|
+
/* @__PURE__ */ d(p, { children: [/* @__PURE__ */ d(g, { children: [/* @__PURE__ */ u(_, { children: "Source Effectiveness" }), /* @__PURE__ */ u(h, { children: "Recruitment channel performance comparison" })] }), /* @__PURE__ */ u(m, { children: /* @__PURE__ */ u(C, {
|
|
263
226
|
columns: [
|
|
264
227
|
{
|
|
265
228
|
key: "source",
|
|
266
229
|
header: "Source",
|
|
267
230
|
priority: "primary",
|
|
268
|
-
cell: (e) => /* @__PURE__ */
|
|
231
|
+
cell: (e) => /* @__PURE__ */ u("span", {
|
|
269
232
|
className: "font-medium",
|
|
270
233
|
children: e.source
|
|
271
234
|
})
|
|
@@ -274,7 +237,7 @@ function T() {
|
|
|
274
237
|
key: "applications",
|
|
275
238
|
header: "Applications",
|
|
276
239
|
priority: "secondary",
|
|
277
|
-
cell: (e) => /* @__PURE__ */
|
|
240
|
+
cell: (e) => /* @__PURE__ */ u("span", {
|
|
278
241
|
className: "md:block md:text-right",
|
|
279
242
|
children: e.applications
|
|
280
243
|
})
|
|
@@ -283,7 +246,7 @@ function T() {
|
|
|
283
246
|
key: "hires",
|
|
284
247
|
header: "Hires",
|
|
285
248
|
priority: "secondary",
|
|
286
|
-
cell: (e) => /* @__PURE__ */
|
|
249
|
+
cell: (e) => /* @__PURE__ */ u("span", {
|
|
287
250
|
className: "md:block md:text-right",
|
|
288
251
|
children: e.hires
|
|
289
252
|
})
|
|
@@ -292,9 +255,9 @@ function T() {
|
|
|
292
255
|
key: "conversion",
|
|
293
256
|
header: "Conversion",
|
|
294
257
|
priority: "primary",
|
|
295
|
-
cell: (e) => /* @__PURE__ */
|
|
258
|
+
cell: (e) => /* @__PURE__ */ u("span", {
|
|
296
259
|
className: "md:block md:text-right",
|
|
297
|
-
children: /* @__PURE__ */
|
|
260
|
+
children: /* @__PURE__ */ d(f, {
|
|
298
261
|
variant: e.conversionRate >= 5 ? "default" : "secondary",
|
|
299
262
|
className: "text-[10px]",
|
|
300
263
|
children: [e.conversionRate, "%"]
|
|
@@ -302,24 +265,24 @@ function T() {
|
|
|
302
265
|
})
|
|
303
266
|
}
|
|
304
267
|
],
|
|
305
|
-
data:
|
|
268
|
+
data: j,
|
|
306
269
|
rowKey: (e) => e.source,
|
|
307
|
-
emptyState: /* @__PURE__ */
|
|
270
|
+
emptyState: /* @__PURE__ */ u("p", {
|
|
308
271
|
className: "py-6 text-center text-sm text-text-secondary",
|
|
309
272
|
children: "No candidate source data yet."
|
|
310
273
|
})
|
|
311
274
|
}) })] }),
|
|
312
|
-
/* @__PURE__ */
|
|
275
|
+
/* @__PURE__ */ d(p, { children: [/* @__PURE__ */ d(g, { children: [/* @__PURE__ */ u(_, { children: "Open Positions Detail" }), /* @__PURE__ */ u(h, { children: "Active job postings and applicant counts" })] }), /* @__PURE__ */ u(m, { children: J.length === 0 ? /* @__PURE__ */ u(b, {
|
|
313
276
|
illustration: "empty-data",
|
|
314
277
|
title: "No open positions",
|
|
315
278
|
description: "Active job postings will appear here once you publish a role."
|
|
316
|
-
}) : /* @__PURE__ */ l
|
|
279
|
+
}) : /* @__PURE__ */ d(l, { children: [/* @__PURE__ */ u(C, {
|
|
317
280
|
columns: [
|
|
318
281
|
{
|
|
319
282
|
key: "title",
|
|
320
283
|
header: "Position",
|
|
321
284
|
priority: "primary",
|
|
322
|
-
cell: (e) => /* @__PURE__ */
|
|
285
|
+
cell: (e) => /* @__PURE__ */ u("span", {
|
|
323
286
|
className: "font-medium",
|
|
324
287
|
children: e.title
|
|
325
288
|
})
|
|
@@ -328,7 +291,7 @@ function T() {
|
|
|
328
291
|
key: "department",
|
|
329
292
|
header: "Department",
|
|
330
293
|
priority: "secondary",
|
|
331
|
-
cell: (e) => /* @__PURE__ */
|
|
294
|
+
cell: (e) => /* @__PURE__ */ u("span", {
|
|
332
295
|
className: "text-text-secondary",
|
|
333
296
|
children: e.department
|
|
334
297
|
})
|
|
@@ -337,7 +300,7 @@ function T() {
|
|
|
337
300
|
key: "level",
|
|
338
301
|
header: "Level",
|
|
339
302
|
priority: "hidden-on-mobile",
|
|
340
|
-
cell: (e) => /* @__PURE__ */
|
|
303
|
+
cell: (e) => /* @__PURE__ */ u(f, {
|
|
341
304
|
variant: "outline",
|
|
342
305
|
className: "w-fit text-[10px]",
|
|
343
306
|
children: t(e.experienceLevel)
|
|
@@ -347,7 +310,7 @@ function T() {
|
|
|
347
310
|
key: "applicants",
|
|
348
311
|
header: "Applicants",
|
|
349
312
|
priority: "secondary",
|
|
350
|
-
cell: (e) => /* @__PURE__ */
|
|
313
|
+
cell: (e) => /* @__PURE__ */ u("span", {
|
|
351
314
|
className: "md:block md:text-right",
|
|
352
315
|
children: e.applicants
|
|
353
316
|
})
|
|
@@ -356,7 +319,7 @@ function T() {
|
|
|
356
319
|
key: "salary",
|
|
357
320
|
header: "Salary Range",
|
|
358
321
|
priority: "secondary",
|
|
359
|
-
cell: (e) => /* @__PURE__ */
|
|
322
|
+
cell: (e) => /* @__PURE__ */ d("span", {
|
|
360
323
|
className: "md:block md:text-right text-text-secondary",
|
|
361
324
|
children: [
|
|
362
325
|
"$",
|
|
@@ -371,26 +334,26 @@ function T() {
|
|
|
371
334
|
key: "status",
|
|
372
335
|
header: "Status",
|
|
373
336
|
priority: "primary",
|
|
374
|
-
cell: (e) => /* @__PURE__ */
|
|
337
|
+
cell: (e) => /* @__PURE__ */ u(f, {
|
|
375
338
|
variant: "default",
|
|
376
339
|
className: "text-[10px]",
|
|
377
340
|
children: t(e.status)
|
|
378
341
|
})
|
|
379
342
|
}
|
|
380
343
|
],
|
|
381
|
-
data:
|
|
344
|
+
data: $,
|
|
382
345
|
rowKey: (e) => e.id
|
|
383
|
-
}),
|
|
384
|
-
page:
|
|
385
|
-
pageSize:
|
|
386
|
-
totalItems:
|
|
387
|
-
onPageChange:
|
|
346
|
+
}), J.length > X && /* @__PURE__ */ u(S, {
|
|
347
|
+
page: Y,
|
|
348
|
+
pageSize: X,
|
|
349
|
+
totalItems: J.length,
|
|
350
|
+
onPageChange: Q,
|
|
388
351
|
className: "mt-3"
|
|
389
352
|
})] }) })] })
|
|
390
353
|
]
|
|
391
354
|
});
|
|
392
355
|
}
|
|
393
356
|
//#endregion
|
|
394
|
-
export {
|
|
357
|
+
export { D as AnalyticsHiringAnalyticsPagePage };
|
|
395
358
|
|
|
396
359
|
//# sourceMappingURL=AnalyticsHiringAnalyticsPagePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnalyticsHiringAnalyticsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n Pagination,\n ResponsiveTable,\n type ResponsiveTableColumn,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { Briefcase, Clock, Target, Users } from 'lucide-react';\nimport { useMemo } from 'react';\nimport { useCrewFoundryStore, type JobPosting } from '../store/crewfoundryStore';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\n\ntype SourceRow = {\n source: string;\n applications: number;\n hires: number;\n conversionRate: number;\n};\n\nconst monthKeyOf = (value: string) => {\n const date = new Date(value);\n return `${date.getFullYear()}-${String(date.getMonth()).padStart(2, '0')}`;\n};\n\n// Stable \"as of\" anchor for this dataset (avoids impure Date.now() in render).\nconst AS_OF = new Date('2026-03-01').getTime();\n\nexport function AnalyticsHiringAnalyticsPagePage() {\n const { jobPostings, candidates, interviews } = useCrewFoundryStore();\n\n // Real monthly activity derived from candidate/interview dates.\n const hiringMetrics = useMemo(() => {\n if (candidates.length === 0 && interviews.length === 0) {\n return [];\n }\n const keys = Array.from(\n new Set([\n ...candidates.map((candidate) => monthKeyOf(candidate.appliedDate)),\n ...interviews.map((interview) => monthKeyOf(interview.date)),\n ])\n )\n .sort()\n .slice(-6);\n return keys.map((key) => {\n const [year, month] = key.split('-').map(Number);\n const label = new Date(year, month, 1).toLocaleString('en-US', {\n month: 'short',\n year: '2-digit',\n });\n const monthCandidates = candidates.filter(\n (candidate) => monthKeyOf(candidate.appliedDate) === key\n );\n return {\n month: label,\n applications: monthCandidates.length,\n interviews: interviews.filter((interview) => monthKeyOf(interview.date) === key).length,\n hires: monthCandidates.filter((candidate) => candidate.status === 'hired').length,\n };\n });\n }, [candidates, interviews]);\n\n const sourceEffectiveness = useMemo<SourceRow[]>(() => {\n const grouped = candidates.reduce<Record<string, { applications: number; hires: number }>>(\n (acc, candidate) => {\n const source = candidate.source || 'Unknown';\n const value = acc[source] || { applications: 0, hires: 0 };\n value.applications += 1;\n if (candidate.status === 'hired') {\n value.hires += 1;\n }\n acc[source] = value;\n return acc;\n },\n {}\n );\n\n return Object.entries(grouped)\n .map(([source, value]) => ({\n source,\n applications: value.applications,\n hires: value.hires,\n conversionRate: Number(((value.hires / Math.max(value.applications, 1)) * 100).toFixed(1)),\n }))\n .sort((a, b) => b.conversionRate - a.conversionRate);\n }, [candidates]);\n\n // Real average number of days active postings have been open.\n const avgDaysOpen = useMemo(() => {\n const activePostings = jobPostings.filter((posting) => posting.status === 'active');\n if (activePostings.length === 0) {\n return 0;\n }\n const totalDays = activePostings.reduce((sum, posting) => {\n const posted = new Date(posting.postedDate).getTime();\n if (Number.isNaN(posted)) {\n return sum;\n }\n return sum + Math.max(0, (AS_OF - posted) / (1000 * 60 * 60 * 24));\n }, 0);\n return Math.round(totalDays / activePostings.length);\n }, [jobPostings]);\n\n const activeJobs = jobPostings.filter((j) => j.status === 'active');\n const totalApplicants = jobPostings.reduce((s, j) => s + j.applicants, 0);\n const scheduledInterviews = interviews.filter((i) => i.status === 'scheduled').length;\n const completedInterviews = interviews.filter((i) => i.status === 'completed').length;\n const offeredCandidates = candidates.filter((c) => c.status === 'offer').length;\n const hiredCandidates = candidates.filter((c) => c.status === 'hired').length;\n\n const interviewedOrBeyond = candidates.filter((c) =>\n ['interview', 'assessment', 'offer', 'hired'].includes(c.status)\n ).length;\n const offerOrBeyond = candidates.filter((c) => ['offer', 'hired'].includes(c.status)).length;\n const interviewToOffer = Math.round((offerOrBeyond / Math.max(interviewedOrBeyond, 1)) * 100);\n const offerAcceptance = Math.round(\n (hiredCandidates / Math.max(offeredCandidates + hiredCandidates, 1)) * 100\n );\n\n const maxApplications = Math.max(1, ...hiringMetrics.map((m) => m.applications));\n\n const statusPipeline = [\n {\n status: 'New',\n count: candidates.filter((c) => c.status === 'new').length,\n color: 'bg-text-secondary',\n },\n {\n status: 'Screening',\n count: candidates.filter((c) => c.status === 'screening').length,\n color: 'bg-status-info-text',\n },\n {\n status: 'Interview',\n count: candidates.filter((c) => c.status === 'interview').length,\n color: 'bg-status-info-text',\n },\n {\n status: 'Assessment',\n count: candidates.filter((c) => c.status === 'assessment').length,\n color: 'bg-action-primary-bg',\n },\n { status: 'Offer', count: offeredCandidates, color: 'bg-status-warning-text' },\n { status: 'Hired', count: hiredCandidates, color: 'bg-status-success-text' },\n {\n status: 'Rejected',\n count: candidates.filter((c) => c.status === 'rejected').length,\n color: 'bg-status-error-text',\n },\n ];\n const totalPipeline = statusPipeline.reduce((s, p) => s + p.count, 0);\n\n // Open Positions = active postings only, paginated to match the card title.\n const openPositions = [...activeJobs].sort((a, b) => b.applicants - a.applicants);\n const {\n page: jobPage,\n pageSize: jobPageSize,\n offset: jobOffset,\n setPage: setJobPage,\n } = usePagination({ initialPageSize: 8 });\n const jobPageItems = openPositions.slice(jobOffset, jobOffset + jobPageSize);\n\n const sourceColumns: ResponsiveTableColumn<SourceRow>[] = [\n {\n key: 'source',\n header: 'Source',\n priority: 'primary',\n cell: (src) => <span className=\"font-medium\">{src.source}</span>,\n },\n {\n key: 'applications',\n header: 'Applications',\n priority: 'secondary',\n cell: (src) => <span className=\"md:block md:text-right\">{src.applications}</span>,\n },\n {\n key: 'hires',\n header: 'Hires',\n priority: 'secondary',\n cell: (src) => <span className=\"md:block md:text-right\">{src.hires}</span>,\n },\n {\n key: 'conversion',\n header: 'Conversion',\n priority: 'primary',\n cell: (src) => (\n <span className=\"md:block md:text-right\">\n <Badge\n variant={src.conversionRate >= 5 ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {src.conversionRate}%\n </Badge>\n </span>\n ),\n },\n ];\n\n const jobColumns: ResponsiveTableColumn<JobPosting>[] = [\n {\n key: 'title',\n header: 'Position',\n priority: 'primary',\n cell: (job) => <span className=\"font-medium\">{job.title}</span>,\n },\n {\n key: 'department',\n header: 'Department',\n priority: 'secondary',\n cell: (job) => <span className=\"text-text-secondary\">{job.department}</span>,\n },\n {\n key: 'level',\n header: 'Level',\n priority: 'hidden-on-mobile',\n cell: (job) => (\n <Badge variant=\"outline\" className=\"w-fit text-[10px]\">\n {formatEnumLabel(job.experienceLevel)}\n </Badge>\n ),\n },\n {\n key: 'applicants',\n header: 'Applicants',\n priority: 'secondary',\n cell: (job) => <span className=\"md:block md:text-right\">{job.applicants}</span>,\n },\n {\n key: 'salary',\n header: 'Salary Range',\n priority: 'secondary',\n cell: (job) => (\n <span className=\"md:block md:text-right text-text-secondary\">\n ${(job.salaryMin / 1000).toFixed(0)}K-${(job.salaryMax / 1000).toFixed(0)}K\n </span>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n priority: 'primary',\n cell: (job) => (\n <Badge variant=\"default\" className=\"text-[10px]\">\n {formatEnumLabel(job.status)}\n </Badge>\n ),\n },\n ];\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Hiring Analytics</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n Recruitment performance and pipeline analysis\n </p>\n <PagePurpose className=\"mt-1\">\n Measure how your hiring funnel performs — applications, time-to-hire, and source quality\n — to make recruiting faster and more effective.\n </PagePurpose>\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Active Postings</CardTitle>\n <Briefcase className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{activeJobs.length}</div>\n <p className=\"text-xs text-text-secondary\">{totalApplicants} total applicants</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Avg Days Open</CardTitle>\n <Clock className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{avgDaysOpen} days</div>\n <p className=\"text-xs text-text-secondary\">Across active postings</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Interview to Offer</CardTitle>\n <Target className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{interviewToOffer}%</div>\n <p className=\"text-xs text-text-secondary\">\n {scheduledInterviews} scheduled, {completedInterviews} completed\n </p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Offer Acceptance</CardTitle>\n <Users className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{offerAcceptance}%</div>\n <p className=\"text-xs text-text-secondary\">{hiredCandidates} hired to date</p>\n </CardContent>\n </GlassCard>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-7\">\n <Card className=\"col-span-4\">\n <CardHeader>\n <CardTitle>Monthly Hiring Activity</CardTitle>\n <CardDescription>Applications, interviews, and hires over time</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {hiringMetrics.length === 0 && (\n <p className=\"py-6 text-center text-sm text-text-secondary\">\n No hiring activity recorded yet.\n </p>\n )}\n {hiringMetrics.map((m) => (\n <div key={m.month} className=\"flex items-center gap-3\">\n <span className=\"w-12 text-sm text-text-secondary\">{m.month}</span>\n <div className=\"flex-1 h-5 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-5 bg-action-primary-bg rounded\"\n style={{\n width: `${Math.round((m.applications / maxApplications) * 100)}%`,\n }}\n />\n </div>\n <div className=\"flex gap-1 w-48 justify-end\">\n <Badge variant=\"outline\" className=\"text-[10px]\">\n {m.applications} apps\n </Badge>\n <Badge variant=\"secondary\" className=\"text-[10px]\">\n {m.interviews} int\n </Badge>\n <Badge variant=\"default\" className=\"text-[10px]\">\n {m.hires} hired\n </Badge>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card className=\"col-span-3\">\n <CardHeader>\n <CardTitle>Pipeline Status</CardTitle>\n <CardDescription>Current candidate distribution</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {statusPipeline.map((stage) => (\n <div key={stage.status} className=\"flex items-center gap-3\">\n <div className={`h-3 w-3 rounded-full ${stage.color}`} />\n <span className=\"w-20 text-sm\">{formatEnumLabel(stage.status)}</span>\n <div className=\"flex-1 h-3 bg-bg-sunken rounded overflow-hidden\">\n <div\n className={`h-3 rounded ${stage.color}`}\n style={{\n width: `${totalPipeline > 0 ? Math.max(Math.round((stage.count / totalPipeline) * 100), 3) : 3}%`,\n }}\n />\n </div>\n <span className=\"text-sm font-medium w-6 text-right\">{stage.count}</span>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Source Effectiveness</CardTitle>\n <CardDescription>Recruitment channel performance comparison</CardDescription>\n </CardHeader>\n <CardContent>\n <ResponsiveTable\n columns={sourceColumns}\n data={sourceEffectiveness}\n rowKey={(src) => src.source}\n emptyState={\n <p className=\"py-6 text-center text-sm text-text-secondary\">\n No candidate source data yet.\n </p>\n }\n />\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Open Positions Detail</CardTitle>\n <CardDescription>Active job postings and applicant counts</CardDescription>\n </CardHeader>\n <CardContent>\n {openPositions.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No open positions\"\n description=\"Active job postings will appear here once you publish a role.\"\n />\n ) : (\n <>\n <ResponsiveTable columns={jobColumns} data={jobPageItems} rowKey={(job) => job.id} />\n {openPositions.length > jobPageSize && (\n <Pagination\n page={jobPage}\n pageSize={jobPageSize}\n totalItems={openPositions.length}\n onPageChange={setJobPage}\n className=\"mt-3\"\n />\n )}\n </>\n )}\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AnalyticsHiringAnalyticsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n Pagination,\n ResponsiveTable,\n type ResponsiveTableColumn,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { Briefcase, Clock, Target, Users } from 'lucide-react';\nimport { useMemo } from 'react';\nimport { useCrewFoundryStore, type JobPosting } from '../store/crewfoundryStore';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\nimport { BarChart, LineChart } from '../components/viz';\nimport type { LabeledValue, Segment } from '../components/viz';\n\ntype SourceRow = {\n source: string;\n applications: number;\n hires: number;\n conversionRate: number;\n};\n\nconst monthKeyOf = (value: string) => {\n const date = new Date(value);\n return `${date.getFullYear()}-${String(date.getMonth()).padStart(2, '0')}`;\n};\n\n// Stable \"as of\" anchor for this dataset (avoids impure Date.now() in render).\nconst AS_OF = new Date('2026-03-01').getTime();\n\nexport function AnalyticsHiringAnalyticsPagePage() {\n const { jobPostings, candidates, interviews } = useCrewFoundryStore();\n\n // Real monthly activity derived from candidate/interview dates.\n const hiringMetrics = useMemo(() => {\n if (candidates.length === 0 && interviews.length === 0) {\n return [];\n }\n const keys = Array.from(\n new Set([\n ...candidates.map((candidate) => monthKeyOf(candidate.appliedDate)),\n ...interviews.map((interview) => monthKeyOf(interview.date)),\n ])\n )\n .sort()\n .slice(-6);\n return keys.map((key) => {\n const [year, month] = key.split('-').map(Number);\n const label = new Date(year, month, 1).toLocaleString('en-US', {\n month: 'short',\n year: '2-digit',\n });\n const monthCandidates = candidates.filter(\n (candidate) => monthKeyOf(candidate.appliedDate) === key\n );\n return {\n month: label,\n applications: monthCandidates.length,\n interviews: interviews.filter((interview) => monthKeyOf(interview.date) === key).length,\n hires: monthCandidates.filter((candidate) => candidate.status === 'hired').length,\n };\n });\n }, [candidates, interviews]);\n\n const sourceEffectiveness = useMemo<SourceRow[]>(() => {\n const grouped = candidates.reduce<Record<string, { applications: number; hires: number }>>(\n (acc, candidate) => {\n const source = candidate.source || 'Unknown';\n const value = acc[source] || { applications: 0, hires: 0 };\n value.applications += 1;\n if (candidate.status === 'hired') {\n value.hires += 1;\n }\n acc[source] = value;\n return acc;\n },\n {}\n );\n\n return Object.entries(grouped)\n .map(([source, value]) => ({\n source,\n applications: value.applications,\n hires: value.hires,\n conversionRate: Number(((value.hires / Math.max(value.applications, 1)) * 100).toFixed(1)),\n }))\n .sort((a, b) => b.conversionRate - a.conversionRate);\n }, [candidates]);\n\n // Real average number of days active postings have been open.\n const avgDaysOpen = useMemo(() => {\n const activePostings = jobPostings.filter((posting) => posting.status === 'active');\n if (activePostings.length === 0) {\n return 0;\n }\n const totalDays = activePostings.reduce((sum, posting) => {\n const posted = new Date(posting.postedDate).getTime();\n if (Number.isNaN(posted)) {\n return sum;\n }\n return sum + Math.max(0, (AS_OF - posted) / (1000 * 60 * 60 * 24));\n }, 0);\n return Math.round(totalDays / activePostings.length);\n }, [jobPostings]);\n\n const activeJobs = jobPostings.filter((j) => j.status === 'active');\n const totalApplicants = jobPostings.reduce((s, j) => s + j.applicants, 0);\n const scheduledInterviews = interviews.filter((i) => i.status === 'scheduled').length;\n const completedInterviews = interviews.filter((i) => i.status === 'completed').length;\n const offeredCandidates = candidates.filter((c) => c.status === 'offer').length;\n const hiredCandidates = candidates.filter((c) => c.status === 'hired').length;\n\n const interviewedOrBeyond = candidates.filter((c) =>\n ['interview', 'assessment', 'offer', 'hired'].includes(c.status)\n ).length;\n const offerOrBeyond = candidates.filter((c) => ['offer', 'hired'].includes(c.status)).length;\n const interviewToOffer = Math.round((offerOrBeyond / Math.max(interviewedOrBeyond, 1)) * 100);\n const offerAcceptance = Math.round(\n (hiredCandidates / Math.max(offeredCandidates + hiredCandidates, 1)) * 100\n );\n\n const applicationsSeries: LabeledValue[] = hiringMetrics.map((m) => ({\n label: m.month,\n value: m.applications,\n }));\n const hiresCompare = hiringMetrics.map((m) => m.hires);\n\n const statusPipeline = [\n {\n status: 'New',\n count: candidates.filter((c) => c.status === 'new').length,\n color: 'bg-text-secondary',\n },\n {\n status: 'Screening',\n count: candidates.filter((c) => c.status === 'screening').length,\n color: 'bg-status-info-text',\n },\n {\n status: 'Interview',\n count: candidates.filter((c) => c.status === 'interview').length,\n color: 'bg-status-info-text',\n },\n {\n status: 'Assessment',\n count: candidates.filter((c) => c.status === 'assessment').length,\n color: 'bg-action-primary-bg',\n },\n { status: 'Offer', count: offeredCandidates, color: 'bg-status-warning-text' },\n { status: 'Hired', count: hiredCandidates, color: 'bg-status-success-text' },\n {\n status: 'Rejected',\n count: candidates.filter((c) => c.status === 'rejected').length,\n color: 'bg-status-error-text',\n },\n ];\n const totalPipeline = statusPipeline.reduce((s, p) => s + p.count, 0);\n const pipelineSeries: Segment[] = statusPipeline.map((stage) => ({\n label: stage.status,\n value: stage.count,\n }));\n\n // Open Positions = active postings only, paginated to match the card title.\n const openPositions = [...activeJobs].sort((a, b) => b.applicants - a.applicants);\n const {\n page: jobPage,\n pageSize: jobPageSize,\n offset: jobOffset,\n setPage: setJobPage,\n } = usePagination({ initialPageSize: 8 });\n const jobPageItems = openPositions.slice(jobOffset, jobOffset + jobPageSize);\n\n const sourceColumns: ResponsiveTableColumn<SourceRow>[] = [\n {\n key: 'source',\n header: 'Source',\n priority: 'primary',\n cell: (src) => <span className=\"font-medium\">{src.source}</span>,\n },\n {\n key: 'applications',\n header: 'Applications',\n priority: 'secondary',\n cell: (src) => <span className=\"md:block md:text-right\">{src.applications}</span>,\n },\n {\n key: 'hires',\n header: 'Hires',\n priority: 'secondary',\n cell: (src) => <span className=\"md:block md:text-right\">{src.hires}</span>,\n },\n {\n key: 'conversion',\n header: 'Conversion',\n priority: 'primary',\n cell: (src) => (\n <span className=\"md:block md:text-right\">\n <Badge\n variant={src.conversionRate >= 5 ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {src.conversionRate}%\n </Badge>\n </span>\n ),\n },\n ];\n\n const jobColumns: ResponsiveTableColumn<JobPosting>[] = [\n {\n key: 'title',\n header: 'Position',\n priority: 'primary',\n cell: (job) => <span className=\"font-medium\">{job.title}</span>,\n },\n {\n key: 'department',\n header: 'Department',\n priority: 'secondary',\n cell: (job) => <span className=\"text-text-secondary\">{job.department}</span>,\n },\n {\n key: 'level',\n header: 'Level',\n priority: 'hidden-on-mobile',\n cell: (job) => (\n <Badge variant=\"outline\" className=\"w-fit text-[10px]\">\n {formatEnumLabel(job.experienceLevel)}\n </Badge>\n ),\n },\n {\n key: 'applicants',\n header: 'Applicants',\n priority: 'secondary',\n cell: (job) => <span className=\"md:block md:text-right\">{job.applicants}</span>,\n },\n {\n key: 'salary',\n header: 'Salary Range',\n priority: 'secondary',\n cell: (job) => (\n <span className=\"md:block md:text-right text-text-secondary\">\n ${(job.salaryMin / 1000).toFixed(0)}K-${(job.salaryMax / 1000).toFixed(0)}K\n </span>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n priority: 'primary',\n cell: (job) => (\n <Badge variant=\"default\" className=\"text-[10px]\">\n {formatEnumLabel(job.status)}\n </Badge>\n ),\n },\n ];\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Hiring Analytics</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n Recruitment performance and pipeline analysis\n </p>\n <PagePurpose className=\"mt-1\">\n Measure how your hiring funnel performs — applications, time-to-hire, and source quality\n — to make recruiting faster and more effective.\n </PagePurpose>\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Active Postings</CardTitle>\n <Briefcase className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{activeJobs.length}</div>\n <p className=\"text-xs text-text-secondary\">{totalApplicants} total applicants</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Avg Days Open</CardTitle>\n <Clock className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{avgDaysOpen} days</div>\n <p className=\"text-xs text-text-secondary\">Across active postings</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Interview to Offer</CardTitle>\n <Target className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{interviewToOffer}%</div>\n <p className=\"text-xs text-text-secondary\">\n {scheduledInterviews} scheduled, {completedInterviews} completed\n </p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Offer Acceptance</CardTitle>\n <Users className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{offerAcceptance}%</div>\n <p className=\"text-xs text-text-secondary\">{hiredCandidates} hired to date</p>\n </CardContent>\n </GlassCard>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-7\">\n <Card className=\"col-span-4\">\n <CardHeader>\n <CardTitle>Monthly Hiring Activity</CardTitle>\n <CardDescription>Applications, interviews, and hires over time</CardDescription>\n </CardHeader>\n <CardContent>\n {applicationsSeries.length === 0 ? (\n <p className=\"py-6 text-center text-sm text-text-secondary\">\n No hiring activity recorded yet.\n </p>\n ) : (\n <LineChart\n series={applicationsSeries}\n compare={hiresCompare}\n seriesLabel=\"Applications\"\n compareLabel=\"Hires\"\n height={220}\n valueFormatter={(value) => `${value}`}\n />\n )}\n </CardContent>\n </Card>\n\n <Card className=\"col-span-3\">\n <CardHeader>\n <CardTitle>Pipeline Status</CardTitle>\n <CardDescription>Current candidate distribution</CardDescription>\n </CardHeader>\n <CardContent>\n {totalPipeline > 0 ? (\n <BarChart series={pipelineSeries} colorful height={220} title=\"Candidate pipeline by stage\" />\n ) : (\n <p className=\"py-6 text-center text-sm text-text-secondary\">\n No candidates in the pipeline yet.\n </p>\n )}\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Source Effectiveness</CardTitle>\n <CardDescription>Recruitment channel performance comparison</CardDescription>\n </CardHeader>\n <CardContent>\n <ResponsiveTable\n columns={sourceColumns}\n data={sourceEffectiveness}\n rowKey={(src) => src.source}\n emptyState={\n <p className=\"py-6 text-center text-sm text-text-secondary\">\n No candidate source data yet.\n </p>\n }\n />\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Open Positions Detail</CardTitle>\n <CardDescription>Active job postings and applicant counts</CardDescription>\n </CardHeader>\n <CardContent>\n {openPositions.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No open positions\"\n description=\"Active job postings will appear here once you publish a role.\"\n />\n ) : (\n <>\n <ResponsiveTable columns={jobColumns} data={jobPageItems} rowKey={(job) => job.id} />\n {openPositions.length > jobPageSize && (\n <Pagination\n page={jobPage}\n pageSize={jobPageSize}\n totalItems={openPositions.length}\n onPageChange={setJobPage}\n className=\"mt-3\"\n />\n )}\n </>\n )}\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|