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