@burdenoff/microfe-crewfoundry 2026.703.1 → 2026.704.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/pages/AdminAdminPagePage.js +117 -164
- package/dist/crewfoundry/pages/AdminAdminPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsAnalyticsPagePage.js +99 -82
- package/dist/crewfoundry/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsEmployeeAnalyticsPagePage.js +160 -192
- package/dist/crewfoundry/pages/AnalyticsEmployeeAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js +238 -231
- package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.js +196 -166
- package/dist/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsCallsPagePage.js +45 -147
- package/dist/crewfoundry/pages/CommunicationsCallsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsDirectoryPagePage.js +20 -11
- package/dist/crewfoundry/pages/CommunicationsDirectoryPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsFilesPagePage.js +45 -152
- package/dist/crewfoundry/pages/CommunicationsFilesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.js +50 -138
- package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsTeamsPagePage.js +149 -67
- package/dist/crewfoundry/pages/CommunicationsTeamsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsVideoCallsPagePage.js +50 -201
- package/dist/crewfoundry/pages/CommunicationsVideoCallsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js +205 -136
- package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyCompanyDashboardPagePage.js +25 -31
- package/dist/crewfoundry/pages/CompanyCompanyDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyCompanyProfilePagePage.js +118 -104
- package/dist/crewfoundry/pages/CompanyCompanyProfilePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js +117 -58
- package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardAnalyticsPagePage.js +90 -94
- package/dist/crewfoundry/pages/DashboardAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardDashboardPagePage.js +210 -191
- package/dist/crewfoundry/pages/DashboardDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardEnhancedDashboardPagePage.js +213 -180
- package/dist/crewfoundry/pages/DashboardEnhancedDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardExecutiveDashboardPage.js +133 -264
- package/dist/crewfoundry/pages/DashboardExecutiveDashboardPage.js.map +1 -1
- package/dist/crewfoundry/pages/HelpApiDocumentationPagePage.js +279 -212
- package/dist/crewfoundry/pages/HelpApiDocumentationPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringApplicationsPagePage.js +131 -105
- package/dist/crewfoundry/pages/HiringApplicationsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringCandidatesPagePage.js +167 -148
- package/dist/crewfoundry/pages/HiringCandidatesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringCreateJobPagePage.js +3 -3
- package/dist/crewfoundry/pages/HiringCreateJobPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringInterviewsPagePage.js +13 -5
- package/dist/crewfoundry/pages/HiringInterviewsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js +151 -152
- package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringJobsPagePage.js +38 -21
- package/dist/crewfoundry/pages/HiringJobsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrDepartmentsPagePage.js +3 -3
- package/dist/crewfoundry/pages/HrDepartmentsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrEmployeesPagePage.js +134 -127
- package/dist/crewfoundry/pages/HrEmployeesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js +163 -132
- package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrPayrollPagePage.js +202 -160
- package/dist/crewfoundry/pages/HrPayrollPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js +104 -91
- package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningAssessmentsPagePage.js +29 -147
- package/dist/crewfoundry/pages/LearningAssessmentsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCertificatesPagePage.js +22 -106
- package/dist/crewfoundry/pages/LearningCertificatesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js +74 -74
- package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js +101 -97
- package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCoursesPagePage.js +64 -47
- package/dist/crewfoundry/pages/LearningCoursesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningAnalyticsPagePage.js +111 -121
- package/dist/crewfoundry/pages/LearningLearningAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPagePage.js +131 -110
- package/dist/crewfoundry/pages/LearningLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js +65 -64
- package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js +58 -43
- package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningMyLearningPagePage.js +66 -82
- package/dist/crewfoundry/pages/LearningMyLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceBookingManagementPagePage.js +38 -88
- package/dist/crewfoundry/pages/MarketplaceBookingManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceEarningsPagePage.js +59 -65
- package/dist/crewfoundry/pages/MarketplaceEarningsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.js +209 -323
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js +175 -220
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js +74 -59
- package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js +96 -80
- package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMessagingPagePage.js +28 -100
- package/dist/crewfoundry/pages/MarketplaceMessagingPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.js +27 -20
- package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.js +73 -73
- package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js +62 -56
- package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsEscrowPagePage.js +40 -138
- package/dist/crewfoundry/pages/PaymentsEscrowPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js +126 -93
- package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.js +99 -77
- package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsSecurityPagePage.js +73 -74
- package/dist/crewfoundry/pages/PaymentsSecurityPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsCreateProjectPagePage.js +1 -1
- package/dist/crewfoundry/pages/ProjectsCreateProjectPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsProjectDetailsPagePage.js +122 -122
- package/dist/crewfoundry/pages/ProjectsProjectDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js +130 -113
- package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamAnalyticsPagePage.js +68 -59
- package/dist/crewfoundry/pages/TeamsTeamAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamDetailPagePage.js +73 -62
- package/dist/crewfoundry/pages/TeamsTeamDetailPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamDirectoryPagePage.js +82 -58
- package/dist/crewfoundry/pages/TeamsTeamDirectoryPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamSettingsPagePage.js +153 -153
- package/dist/crewfoundry/pages/TeamsTeamSettingsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamsPagePage.js +63 -48
- package/dist/crewfoundry/pages/TeamsTeamsPagePage.js.map +1 -1
- package/dist/crewfoundry/store/crewfoundryStore.js +35 -3
- package/dist/crewfoundry/store/crewfoundryStore.js.map +1 -1
- package/package.json +1 -1
|
@@ -4,9 +4,10 @@ import { getNumber as r, getText as i, newClientId as a } from "../components/fo
|
|
|
4
4
|
import { useMemo as o } from "react";
|
|
5
5
|
import { CheckCircle as s, Clock as c, LogIn as l, LogOut as u, Timer as d } from "lucide-react";
|
|
6
6
|
import { jsx as f, jsxs as p } from "react/jsx-runtime";
|
|
7
|
-
import { Badge as m, Button as h, Card as g, CardContent as _, CardDescription as v, CardHeader as y, CardTitle as b, GlassCard as x, GradientText as S, PagePurpose as C } from "@burdenoff/fe-libs/ui";
|
|
7
|
+
import { Badge as m, Button as h, Card as g, CardContent as _, CardDescription as v, CardHeader as y, CardTitle as b, GlassCard as x, GradientText as S, PagePurpose as C, Pagination as w } from "@burdenoff/fe-libs/ui";
|
|
8
|
+
import { usePagination as T } from "@burdenoff/fe-libs/shared/hooks";
|
|
8
9
|
//#region src/crewfoundry/pages/HrTimeTrackingPagePage.tsx
|
|
9
|
-
var
|
|
10
|
+
var E = [
|
|
10
11
|
{
|
|
11
12
|
label: "Pending",
|
|
12
13
|
value: "pending"
|
|
@@ -19,7 +20,7 @@ var w = [
|
|
|
19
20
|
label: "Rejected",
|
|
20
21
|
value: "rejected"
|
|
21
22
|
}
|
|
22
|
-
],
|
|
23
|
+
], D = [
|
|
23
24
|
{
|
|
24
25
|
name: "clockOut",
|
|
25
26
|
label: "Clock-out time",
|
|
@@ -42,20 +43,20 @@ var w = [
|
|
|
42
43
|
name: "status",
|
|
43
44
|
label: "Status",
|
|
44
45
|
type: "select",
|
|
45
|
-
options:
|
|
46
|
+
options: E
|
|
46
47
|
}
|
|
47
48
|
];
|
|
48
|
-
function
|
|
49
|
-
let { timeEntries:
|
|
49
|
+
function O() {
|
|
50
|
+
let { timeEntries: O, employees: k, addTimeEntry: A, updateTimeEntry: j } = e(), M = n(), N = n(), P = o(() => k.map((e) => ({
|
|
50
51
|
label: `${e.firstName} ${e.lastName}`,
|
|
51
52
|
value: e.id
|
|
52
|
-
})), [
|
|
53
|
+
})), [k]), F = o(() => [
|
|
53
54
|
{
|
|
54
55
|
name: "employeeId",
|
|
55
56
|
label: "Employee",
|
|
56
57
|
type: "select",
|
|
57
58
|
required: !0,
|
|
58
|
-
options:
|
|
59
|
+
options: P,
|
|
59
60
|
colSpan: 2
|
|
60
61
|
},
|
|
61
62
|
{
|
|
@@ -97,11 +98,11 @@ function E() {
|
|
|
97
98
|
name: "status",
|
|
98
99
|
label: "Status",
|
|
99
100
|
type: "select",
|
|
100
|
-
options:
|
|
101
|
+
options: E
|
|
101
102
|
}
|
|
102
|
-
], [
|
|
103
|
-
let t = i(e, "employeeId"), n =
|
|
104
|
-
await
|
|
103
|
+
], [P]), I = O.reduce((e, t) => e + t.totalHours, 0), L = O.reduce((e, t) => e + t.overtime, 0), R = O.filter((e) => e.status === "approved").length, z = O.filter((e) => e.status === "pending").length, B = O.reduce((e, t) => (e[t.project] = (e[t.project] || 0) + t.totalHours, e), {}), V = Math.max(...Object.values(B), 1), H = o(() => [...O].sort((e, t) => t.date.localeCompare(e.date)), [O]), { page: U, pageSize: W, offset: G, setPage: K } = T({ initialPageSize: 8 }), q = H.slice(G, G + W), J = async (e) => {
|
|
104
|
+
let t = i(e, "employeeId"), n = k.find((e) => e.id === t);
|
|
105
|
+
await A({
|
|
105
106
|
id: a(),
|
|
106
107
|
employeeId: t,
|
|
107
108
|
employeeName: n ? `${n.firstName} ${n.lastName}` : "",
|
|
@@ -113,19 +114,19 @@ function E() {
|
|
|
113
114
|
project: i(e, "project"),
|
|
114
115
|
status: i(e, "status") || "pending"
|
|
115
116
|
});
|
|
116
|
-
},
|
|
117
|
-
let t =
|
|
118
|
-
t && await
|
|
117
|
+
}, Y = async (e) => {
|
|
118
|
+
let t = N.context;
|
|
119
|
+
t && await j(t.id, {
|
|
119
120
|
clockOut: i(e, "clockOut"),
|
|
120
121
|
totalHours: r(e, "totalHours"),
|
|
121
122
|
overtime: r(e, "overtime"),
|
|
122
123
|
status: i(e, "status") || t.status
|
|
123
124
|
});
|
|
124
|
-
},
|
|
125
|
-
clockOut:
|
|
126
|
-
totalHours:
|
|
127
|
-
overtime:
|
|
128
|
-
status:
|
|
125
|
+
}, X = N.context ? {
|
|
126
|
+
clockOut: N.context.clockOut,
|
|
127
|
+
totalHours: N.context.totalHours,
|
|
128
|
+
overtime: N.context.overtime,
|
|
129
|
+
status: N.context.status
|
|
129
130
|
} : void 0;
|
|
130
131
|
return /* @__PURE__ */ p("div", {
|
|
131
132
|
className: "space-y-6 p-6",
|
|
@@ -147,7 +148,7 @@ function E() {
|
|
|
147
148
|
})
|
|
148
149
|
] }), /* @__PURE__ */ p(h, {
|
|
149
150
|
size: "sm",
|
|
150
|
-
onClick: () =>
|
|
151
|
+
onClick: () => M.openModal(),
|
|
151
152
|
children: [/* @__PURE__ */ f(d, { className: "h-4 w-4 mr-1" }), "Add Entry"]
|
|
152
153
|
})]
|
|
153
154
|
}),
|
|
@@ -166,10 +167,10 @@ function E() {
|
|
|
166
167
|
}), /* @__PURE__ */ f(c, { className: "h-4 w-4 text-text-secondary" })]
|
|
167
168
|
}), /* @__PURE__ */ p(_, { children: [/* @__PURE__ */ f("div", {
|
|
168
169
|
className: "text-2xl font-bold tabular-nums",
|
|
169
|
-
children:
|
|
170
|
+
children: I.toFixed(1)
|
|
170
171
|
}), /* @__PURE__ */ f("p", {
|
|
171
172
|
className: "text-xs text-text-secondary",
|
|
172
|
-
children: "
|
|
173
|
+
children: "All logged hours"
|
|
173
174
|
})] })]
|
|
174
175
|
}),
|
|
175
176
|
/* @__PURE__ */ p(x, {
|
|
@@ -183,7 +184,7 @@ function E() {
|
|
|
183
184
|
}), /* @__PURE__ */ f(d, { className: "h-4 w-4 text-status-warning-text" })]
|
|
184
185
|
}), /* @__PURE__ */ p(_, { children: [/* @__PURE__ */ p("div", {
|
|
185
186
|
className: "text-2xl font-bold tabular-nums",
|
|
186
|
-
children: [
|
|
187
|
+
children: [L.toFixed(1), "h"]
|
|
187
188
|
}), /* @__PURE__ */ f("p", {
|
|
188
189
|
className: "text-xs text-text-secondary",
|
|
189
190
|
children: "Extra hours logged"
|
|
@@ -200,7 +201,7 @@ function E() {
|
|
|
200
201
|
}), /* @__PURE__ */ f(s, { className: "h-4 w-4 text-status-success-text" })]
|
|
201
202
|
}), /* @__PURE__ */ p(_, { children: [/* @__PURE__ */ f("div", {
|
|
202
203
|
className: "text-2xl font-bold tabular-nums",
|
|
203
|
-
children:
|
|
204
|
+
children: R
|
|
204
205
|
}), /* @__PURE__ */ f("p", {
|
|
205
206
|
className: "text-xs text-text-secondary",
|
|
206
207
|
children: "time entries"
|
|
@@ -217,7 +218,7 @@ function E() {
|
|
|
217
218
|
}), /* @__PURE__ */ f(c, { className: "h-4 w-4 text-status-warning-text" })]
|
|
218
219
|
}), /* @__PURE__ */ p(_, { children: [/* @__PURE__ */ f("div", {
|
|
219
220
|
className: "text-2xl font-bold tabular-nums",
|
|
220
|
-
children:
|
|
221
|
+
children: z
|
|
221
222
|
}), /* @__PURE__ */ f("p", {
|
|
222
223
|
className: "text-xs text-text-secondary",
|
|
223
224
|
children: "awaiting approval"
|
|
@@ -229,7 +230,7 @@ function E() {
|
|
|
229
230
|
className: "grid gap-4 md:grid-cols-2",
|
|
230
231
|
children: [/* @__PURE__ */ p(g, { children: [/* @__PURE__ */ p(y, { children: [/* @__PURE__ */ f(b, { children: "Hours by Project" }), /* @__PURE__ */ f(v, { children: "Time allocation across active projects" })] }), /* @__PURE__ */ f(_, {
|
|
231
232
|
className: "space-y-3",
|
|
232
|
-
children: Object.entries(
|
|
233
|
+
children: Object.entries(B).sort(([, e], [, t]) => t - e).map(([e, t]) => /* @__PURE__ */ p("div", {
|
|
233
234
|
className: "flex items-center gap-3",
|
|
234
235
|
children: [
|
|
235
236
|
/* @__PURE__ */ f("span", {
|
|
@@ -240,7 +241,7 @@ function E() {
|
|
|
240
241
|
className: "flex-1 h-4 bg-bg-sunken rounded overflow-hidden",
|
|
241
242
|
children: /* @__PURE__ */ f("div", {
|
|
242
243
|
className: "h-4 bg-action-primary-bg rounded",
|
|
243
|
-
style: { width: `${Math.round(t /
|
|
244
|
+
style: { width: `${Math.round(t / V * 100)}%` }
|
|
244
245
|
})
|
|
245
246
|
}),
|
|
246
247
|
/* @__PURE__ */ p("span", {
|
|
@@ -249,84 +250,96 @@ function E() {
|
|
|
249
250
|
})
|
|
250
251
|
]
|
|
251
252
|
}, e))
|
|
252
|
-
})] }), /* @__PURE__ */ p(g, { children: [/* @__PURE__ */ p(y, { children: [/* @__PURE__ */ f(b, { children: "
|
|
253
|
+
})] }), /* @__PURE__ */ p(g, { children: [/* @__PURE__ */ p(y, { children: [/* @__PURE__ */ f(b, { children: "Recent Activity" }), /* @__PURE__ */ f(v, { children: "Latest employee clock-in/out entries" })] }), /* @__PURE__ */ p(_, {
|
|
253
254
|
className: "space-y-3",
|
|
254
|
-
children:
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
255
|
+
children: [
|
|
256
|
+
q.map((e) => /* @__PURE__ */ p("div", {
|
|
257
|
+
className: "flex items-center justify-between rounded border p-3",
|
|
258
|
+
children: [/* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("p", {
|
|
259
|
+
className: "text-sm font-medium",
|
|
260
|
+
children: e.employeeName
|
|
261
|
+
}), /* @__PURE__ */ f("p", {
|
|
262
|
+
className: "text-xs text-text-secondary",
|
|
263
|
+
children: e.project
|
|
264
|
+
})] }), /* @__PURE__ */ p("div", {
|
|
265
|
+
className: "flex items-center gap-3",
|
|
266
|
+
children: [
|
|
267
|
+
/* @__PURE__ */ p("div", {
|
|
268
|
+
className: "text-right text-xs",
|
|
269
|
+
children: [/* @__PURE__ */ p("div", {
|
|
270
|
+
className: "flex items-center gap-1",
|
|
271
|
+
children: [/* @__PURE__ */ f(l, { className: "h-3 w-3 text-status-success-text" }), e.clockIn]
|
|
272
|
+
}), /* @__PURE__ */ p("div", {
|
|
273
|
+
className: "flex items-center gap-1",
|
|
274
|
+
children: [/* @__PURE__ */ f(u, { className: "h-3 w-3 text-status-error-text" }), e.clockOut]
|
|
275
|
+
})]
|
|
276
|
+
}),
|
|
277
|
+
/* @__PURE__ */ p("div", {
|
|
278
|
+
className: "text-right",
|
|
279
|
+
children: [/* @__PURE__ */ p("p", {
|
|
280
|
+
className: "text-sm font-medium",
|
|
281
|
+
children: [e.totalHours, "h"]
|
|
282
|
+
}), e.overtime > 0 && /* @__PURE__ */ p("p", {
|
|
283
|
+
className: "text-xs text-status-warning-text",
|
|
284
|
+
children: [
|
|
285
|
+
"+",
|
|
286
|
+
e.overtime,
|
|
287
|
+
"h OT"
|
|
288
|
+
]
|
|
289
|
+
})]
|
|
290
|
+
}),
|
|
291
|
+
/* @__PURE__ */ f(m, {
|
|
292
|
+
variant: e.status === "approved" ? "default" : "secondary",
|
|
293
|
+
className: "text-[10px]",
|
|
294
|
+
children: e.status
|
|
295
|
+
}),
|
|
296
|
+
/* @__PURE__ */ f(h, {
|
|
297
|
+
variant: "ghost",
|
|
298
|
+
size: "sm",
|
|
299
|
+
className: "h-8 w-8 p-0",
|
|
300
|
+
"aria-label": `Clock out ${e.employeeName}`,
|
|
301
|
+
onClick: () => N.openModal(e),
|
|
302
|
+
children: /* @__PURE__ */ f(u, { className: "h-4 w-4" })
|
|
303
|
+
})
|
|
304
|
+
]
|
|
305
|
+
})]
|
|
306
|
+
}, e.id)),
|
|
307
|
+
H.length === 0 && /* @__PURE__ */ f("p", {
|
|
308
|
+
className: "py-6 text-center text-sm text-text-secondary",
|
|
309
|
+
children: "No time entries logged yet."
|
|
310
|
+
}),
|
|
311
|
+
H.length > W && /* @__PURE__ */ f(w, {
|
|
312
|
+
page: U,
|
|
313
|
+
pageSize: W,
|
|
314
|
+
totalItems: H.length,
|
|
315
|
+
onPageChange: K
|
|
316
|
+
})
|
|
317
|
+
]
|
|
305
318
|
})] })]
|
|
306
319
|
}),
|
|
307
320
|
/* @__PURE__ */ f(t, {
|
|
308
|
-
open:
|
|
309
|
-
onOpenChange:
|
|
321
|
+
open: M.open,
|
|
322
|
+
onOpenChange: M.onOpenChange,
|
|
310
323
|
title: "Add time entry",
|
|
311
324
|
description: "Log a work session for a team member.",
|
|
312
|
-
fields:
|
|
325
|
+
fields: F,
|
|
313
326
|
submitLabel: "Add entry",
|
|
314
|
-
onSubmit:
|
|
327
|
+
onSubmit: J
|
|
315
328
|
}),
|
|
316
329
|
/* @__PURE__ */ f(t, {
|
|
317
|
-
open:
|
|
318
|
-
onOpenChange:
|
|
330
|
+
open: N.open,
|
|
331
|
+
onOpenChange: N.onOpenChange,
|
|
319
332
|
title: "Clock out",
|
|
320
|
-
description:
|
|
321
|
-
fields:
|
|
322
|
-
initialValues:
|
|
333
|
+
description: N.context ? `Record clock-out for ${N.context.employeeName}.` : void 0,
|
|
334
|
+
fields: D,
|
|
335
|
+
initialValues: X,
|
|
323
336
|
submitLabel: "Save",
|
|
324
|
-
onSubmit:
|
|
337
|
+
onSubmit: Y
|
|
325
338
|
})
|
|
326
339
|
]
|
|
327
340
|
});
|
|
328
341
|
}
|
|
329
342
|
//#endregion
|
|
330
|
-
export {
|
|
343
|
+
export { O as HrTimeTrackingPagePage };
|
|
331
344
|
|
|
332
345
|
//# sourceMappingURL=HrTimeTrackingPagePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HrTimeTrackingPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/HrTimeTrackingPagePage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { CheckCircle, Clock, LogIn, LogOut, Timer } from 'lucide-react';\nimport { useCrewFoundryStore, type TimeEntry } from '../store/crewfoundryStore';\nimport { FormModal, useFormModal, type FieldConfig, type FormValues } from '../components/forms/FormModal';\nimport { getNumber, getText, newClientId } from '../components/forms/formValues';\n\nconst STATUS_OPTIONS = [\n { label: 'Pending', value: 'pending' },\n { label: 'Approved', value: 'approved' },\n { label: 'Rejected', value: 'rejected' },\n];\n\nconst CLOCK_OUT_FIELDS: FieldConfig[] = [\n { name: 'clockOut', label: 'Clock-out time', type: 'text', required: true, placeholder: 'e.g. 17:30' },\n { name: 'totalHours', label: 'Total hours', type: 'number', required: true },\n { name: 'overtime', label: 'Overtime hours', type: 'number' },\n { name: 'status', label: 'Status', type: 'select', options: STATUS_OPTIONS },\n];\n\nexport function HrTimeTrackingPagePage() {\n const { timeEntries, employees, addTimeEntry, updateTimeEntry } = useCrewFoundryStore();\n const createForm = useFormModal<void>();\n const clockOutForm = useFormModal<TimeEntry>();\n\n const employeeOptions = useMemo(\n () =>\n employees.map((employee) => ({\n label: `${employee.firstName} ${employee.lastName}`,\n value: employee.id,\n })),\n [employees]\n );\n\n const entryFields = useMemo<FieldConfig[]>(\n () => [\n {\n name: 'employeeId',\n label: 'Employee',\n type: 'select',\n required: true,\n options: employeeOptions,\n colSpan: 2,\n },\n { name: 'date', label: 'Date', type: 'date', required: true },\n { name: 'project', label: 'Project', type: 'text', required: true },\n { name: 'clockIn', label: 'Clock-in time', type: 'text', required: true, placeholder: 'e.g. 09:00' },\n { name: 'clockOut', label: 'Clock-out time', type: 'text', placeholder: 'e.g. 17:30' },\n { name: 'totalHours', label: 'Total hours', type: 'number' },\n { name: 'overtime', label: 'Overtime hours', type: 'number' },\n { name: 'status', label: 'Status', type: 'select', options: STATUS_OPTIONS },\n ],\n [employeeOptions]\n );\n\n const totalHours = timeEntries.reduce((s, t) => s + t.totalHours, 0);\n const totalOvertime = timeEntries.reduce((s, t) => s + t.overtime, 0);\n const approvedEntries = timeEntries.filter((t) => t.status === 'approved').length;\n const pendingEntries = timeEntries.filter((t) => t.status === 'pending').length;\n\n const projectBreakdown = timeEntries.reduce<Record<string, number>>((acc, t) => {\n acc[t.project] = (acc[t.project] || 0) + t.totalHours;\n return acc;\n }, {});\n const maxProjectHours = Math.max(...Object.values(projectBreakdown), 1);\n\n const handleCreate = async (values: FormValues) => {\n const employeeId = getText(values, 'employeeId');\n const employee = employees.find((item) => item.id === employeeId);\n const payload: TimeEntry = {\n id: newClientId(),\n employeeId,\n employeeName: employee ? `${employee.firstName} ${employee.lastName}` : '',\n date: getText(values, 'date'),\n clockIn: getText(values, 'clockIn'),\n clockOut: getText(values, 'clockOut'),\n totalHours: getNumber(values, 'totalHours'),\n overtime: getNumber(values, 'overtime'),\n project: getText(values, 'project'),\n status: (getText(values, 'status') as TimeEntry['status']) || 'pending',\n };\n await addTimeEntry(payload);\n };\n\n const handleClockOut = async (values: FormValues) => {\n const entry = clockOutForm.context;\n if (!entry) {\n return;\n }\n await updateTimeEntry(entry.id, {\n clockOut: getText(values, 'clockOut'),\n totalHours: getNumber(values, 'totalHours'),\n overtime: getNumber(values, 'overtime'),\n status: (getText(values, 'status') as TimeEntry['status']) || entry.status,\n });\n };\n\n const clockOutInitial: FormValues | undefined = clockOutForm.context\n ? {\n clockOut: clockOutForm.context.clockOut,\n totalHours: clockOutForm.context.totalHours,\n overtime: clockOutForm.context.overtime,\n status: clockOutForm.context.status,\n }\n : undefined;\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>Time Tracking</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Monitor employee work hours and overtime</p>\n <PagePurpose className=\"mt-1\">Track hours and attendance across your team — use it to monitor logged time, overtime, and timesheet approvals.</PagePurpose>\n </div>\n <Button size=\"sm\" onClick={() => createForm.openModal()}>\n <Timer className=\"h-4 w-4 mr-1\" />\n Add Entry\n </Button>\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\">Total Hours</CardTitle>\n <Clock className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{totalHours.toFixed(1)}</div>\n <p className=\"text-xs text-text-secondary\">Today</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\">Overtime</CardTitle>\n <Timer className=\"h-4 w-4 text-status-warning-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{totalOvertime.toFixed(1)}h</div>\n <p className=\"text-xs text-text-secondary\">Extra hours logged</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\">Approved</CardTitle>\n <CheckCircle className=\"h-4 w-4 text-status-success-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{approvedEntries}</div>\n <p className=\"text-xs text-text-secondary\">time entries</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\">Pending</CardTitle>\n <Clock className=\"h-4 w-4 text-status-warning-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{pendingEntries}</div>\n <p className=\"text-xs text-text-secondary\">awaiting approval</p>\n </CardContent>\n </GlassCard>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle>Hours by Project</CardTitle>\n <CardDescription>Time allocation across active projects</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(projectBreakdown)\n .sort(([, a], [, b]) => b - a)\n .map(([project, hours]) => (\n <div key={project} className=\"flex items-center gap-3\">\n <span className=\"w-48 text-sm truncate\">{project}</span>\n <div className=\"flex-1 h-4 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-4 bg-action-primary-bg rounded\"\n style={{ width: `${Math.round((hours / maxProjectHours) * 100)}%` }}\n />\n </div>\n <span className=\"text-sm font-medium w-14 text-right\">{hours.toFixed(1)}h</span>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Today's Activity</CardTitle>\n <CardDescription>Employee clock-in/out for today</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {timeEntries.map((entry) => (\n <div key={entry.id} className=\"flex items-center justify-between rounded border p-3\">\n <div>\n <p className=\"text-sm font-medium\">{entry.employeeName}</p>\n <p className=\"text-xs text-text-secondary\">{entry.project}</p>\n </div>\n <div className=\"flex items-center gap-3\">\n <div className=\"text-right text-xs\">\n <div className=\"flex items-center gap-1\">\n <LogIn className=\"h-3 w-3 text-status-success-text\" />\n {entry.clockIn}\n </div>\n <div className=\"flex items-center gap-1\">\n <LogOut className=\"h-3 w-3 text-status-error-text\" />\n {entry.clockOut}\n </div>\n </div>\n <div className=\"text-right\">\n <p className=\"text-sm font-medium\">{entry.totalHours}h</p>\n {entry.overtime > 0 && (\n <p className=\"text-xs text-status-warning-text\">+{entry.overtime}h OT</p>\n )}\n </div>\n <Badge\n variant={entry.status === 'approved' ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {entry.status}\n </Badge>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8 w-8 p-0\"\n aria-label={`Clock out ${entry.employeeName}`}\n onClick={() => clockOutForm.openModal(entry)}\n >\n <LogOut className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n\n <FormModal\n open={createForm.open}\n onOpenChange={createForm.onOpenChange}\n title=\"Add time entry\"\n description=\"Log a work session for a team member.\"\n fields={entryFields}\n submitLabel=\"Add entry\"\n onSubmit={handleCreate}\n />\n\n <FormModal\n open={clockOutForm.open}\n onOpenChange={clockOutForm.onOpenChange}\n title=\"Clock out\"\n description={\n clockOutForm.context\n ? `Record clock-out for ${clockOutForm.context.employeeName}.`\n : undefined\n }\n fields={CLOCK_OUT_FIELDS}\n initialValues={clockOutInitial}\n submitLabel=\"Save\"\n onSubmit={handleClockOut}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"HrTimeTrackingPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/HrTimeTrackingPagePage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n PagePurpose,\n Pagination,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { CheckCircle, Clock, LogIn, LogOut, Timer } from 'lucide-react';\nimport { useCrewFoundryStore, type TimeEntry } from '../store/crewfoundryStore';\nimport { FormModal, useFormModal, type FieldConfig, type FormValues } from '../components/forms/FormModal';\nimport { getNumber, getText, newClientId } from '../components/forms/formValues';\n\nconst STATUS_OPTIONS = [\n { label: 'Pending', value: 'pending' },\n { label: 'Approved', value: 'approved' },\n { label: 'Rejected', value: 'rejected' },\n];\n\nconst CLOCK_OUT_FIELDS: FieldConfig[] = [\n { name: 'clockOut', label: 'Clock-out time', type: 'text', required: true, placeholder: 'e.g. 17:30' },\n { name: 'totalHours', label: 'Total hours', type: 'number', required: true },\n { name: 'overtime', label: 'Overtime hours', type: 'number' },\n { name: 'status', label: 'Status', type: 'select', options: STATUS_OPTIONS },\n];\n\nexport function HrTimeTrackingPagePage() {\n const { timeEntries, employees, addTimeEntry, updateTimeEntry } = useCrewFoundryStore();\n const createForm = useFormModal<void>();\n const clockOutForm = useFormModal<TimeEntry>();\n\n const employeeOptions = useMemo(\n () =>\n employees.map((employee) => ({\n label: `${employee.firstName} ${employee.lastName}`,\n value: employee.id,\n })),\n [employees]\n );\n\n const entryFields = useMemo<FieldConfig[]>(\n () => [\n {\n name: 'employeeId',\n label: 'Employee',\n type: 'select',\n required: true,\n options: employeeOptions,\n colSpan: 2,\n },\n { name: 'date', label: 'Date', type: 'date', required: true },\n { name: 'project', label: 'Project', type: 'text', required: true },\n { name: 'clockIn', label: 'Clock-in time', type: 'text', required: true, placeholder: 'e.g. 09:00' },\n { name: 'clockOut', label: 'Clock-out time', type: 'text', placeholder: 'e.g. 17:30' },\n { name: 'totalHours', label: 'Total hours', type: 'number' },\n { name: 'overtime', label: 'Overtime hours', type: 'number' },\n { name: 'status', label: 'Status', type: 'select', options: STATUS_OPTIONS },\n ],\n [employeeOptions]\n );\n\n const totalHours = timeEntries.reduce((s, t) => s + t.totalHours, 0);\n const totalOvertime = timeEntries.reduce((s, t) => s + t.overtime, 0);\n const approvedEntries = timeEntries.filter((t) => t.status === 'approved').length;\n const pendingEntries = timeEntries.filter((t) => t.status === 'pending').length;\n\n const projectBreakdown = timeEntries.reduce<Record<string, number>>((acc, t) => {\n acc[t.project] = (acc[t.project] || 0) + t.totalHours;\n return acc;\n }, {});\n const maxProjectHours = Math.max(...Object.values(projectBreakdown), 1);\n\n const recentEntries = useMemo(\n () => [...timeEntries].sort((a, b) => b.date.localeCompare(a.date)),\n [timeEntries]\n );\n const { page, pageSize, offset, setPage } = usePagination({ initialPageSize: 8 });\n const activityPageItems = recentEntries.slice(offset, offset + pageSize);\n\n const handleCreate = async (values: FormValues) => {\n const employeeId = getText(values, 'employeeId');\n const employee = employees.find((item) => item.id === employeeId);\n const payload: TimeEntry = {\n id: newClientId(),\n employeeId,\n employeeName: employee ? `${employee.firstName} ${employee.lastName}` : '',\n date: getText(values, 'date'),\n clockIn: getText(values, 'clockIn'),\n clockOut: getText(values, 'clockOut'),\n totalHours: getNumber(values, 'totalHours'),\n overtime: getNumber(values, 'overtime'),\n project: getText(values, 'project'),\n status: (getText(values, 'status') as TimeEntry['status']) || 'pending',\n };\n await addTimeEntry(payload);\n };\n\n const handleClockOut = async (values: FormValues) => {\n const entry = clockOutForm.context;\n if (!entry) {\n return;\n }\n await updateTimeEntry(entry.id, {\n clockOut: getText(values, 'clockOut'),\n totalHours: getNumber(values, 'totalHours'),\n overtime: getNumber(values, 'overtime'),\n status: (getText(values, 'status') as TimeEntry['status']) || entry.status,\n });\n };\n\n const clockOutInitial: FormValues | undefined = clockOutForm.context\n ? {\n clockOut: clockOutForm.context.clockOut,\n totalHours: clockOutForm.context.totalHours,\n overtime: clockOutForm.context.overtime,\n status: clockOutForm.context.status,\n }\n : undefined;\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>Time Tracking</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Monitor employee work hours and overtime</p>\n <PagePurpose className=\"mt-1\">Track hours and attendance across your team — use it to monitor logged time, overtime, and timesheet approvals.</PagePurpose>\n </div>\n <Button size=\"sm\" onClick={() => createForm.openModal()}>\n <Timer className=\"h-4 w-4 mr-1\" />\n Add Entry\n </Button>\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\">Total Hours</CardTitle>\n <Clock className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{totalHours.toFixed(1)}</div>\n <p className=\"text-xs text-text-secondary\">All logged hours</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\">Overtime</CardTitle>\n <Timer className=\"h-4 w-4 text-status-warning-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{totalOvertime.toFixed(1)}h</div>\n <p className=\"text-xs text-text-secondary\">Extra hours logged</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\">Approved</CardTitle>\n <CheckCircle className=\"h-4 w-4 text-status-success-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{approvedEntries}</div>\n <p className=\"text-xs text-text-secondary\">time entries</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\">Pending</CardTitle>\n <Clock className=\"h-4 w-4 text-status-warning-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{pendingEntries}</div>\n <p className=\"text-xs text-text-secondary\">awaiting approval</p>\n </CardContent>\n </GlassCard>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle>Hours by Project</CardTitle>\n <CardDescription>Time allocation across active projects</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(projectBreakdown)\n .sort(([, a], [, b]) => b - a)\n .map(([project, hours]) => (\n <div key={project} className=\"flex items-center gap-3\">\n <span className=\"w-48 text-sm truncate\">{project}</span>\n <div className=\"flex-1 h-4 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-4 bg-action-primary-bg rounded\"\n style={{ width: `${Math.round((hours / maxProjectHours) * 100)}%` }}\n />\n </div>\n <span className=\"text-sm font-medium w-14 text-right\">{hours.toFixed(1)}h</span>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Recent Activity</CardTitle>\n <CardDescription>Latest employee clock-in/out entries</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {activityPageItems.map((entry) => (\n <div key={entry.id} className=\"flex items-center justify-between rounded border p-3\">\n <div>\n <p className=\"text-sm font-medium\">{entry.employeeName}</p>\n <p className=\"text-xs text-text-secondary\">{entry.project}</p>\n </div>\n <div className=\"flex items-center gap-3\">\n <div className=\"text-right text-xs\">\n <div className=\"flex items-center gap-1\">\n <LogIn className=\"h-3 w-3 text-status-success-text\" />\n {entry.clockIn}\n </div>\n <div className=\"flex items-center gap-1\">\n <LogOut className=\"h-3 w-3 text-status-error-text\" />\n {entry.clockOut}\n </div>\n </div>\n <div className=\"text-right\">\n <p className=\"text-sm font-medium\">{entry.totalHours}h</p>\n {entry.overtime > 0 && (\n <p className=\"text-xs text-status-warning-text\">+{entry.overtime}h OT</p>\n )}\n </div>\n <Badge\n variant={entry.status === 'approved' ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {entry.status}\n </Badge>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8 w-8 p-0\"\n aria-label={`Clock out ${entry.employeeName}`}\n onClick={() => clockOutForm.openModal(entry)}\n >\n <LogOut className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n ))}\n {recentEntries.length === 0 && (\n <p className=\"py-6 text-center text-sm text-text-secondary\">\n No time entries logged yet.\n </p>\n )}\n {recentEntries.length > pageSize && (\n <Pagination\n page={page}\n pageSize={pageSize}\n totalItems={recentEntries.length}\n onPageChange={setPage}\n />\n )}\n </CardContent>\n </Card>\n </div>\n\n <FormModal\n open={createForm.open}\n onOpenChange={createForm.onOpenChange}\n title=\"Add time entry\"\n description=\"Log a work session for a team member.\"\n fields={entryFields}\n submitLabel=\"Add entry\"\n onSubmit={handleCreate}\n />\n\n <FormModal\n open={clockOutForm.open}\n onOpenChange={clockOutForm.onOpenChange}\n title=\"Clock out\"\n description={\n clockOutForm.context\n ? `Record clock-out for ${clockOutForm.context.employeeName}.`\n : undefined\n }\n fields={CLOCK_OUT_FIELDS}\n initialValues={clockOutInitial}\n submitLabel=\"Save\"\n onSubmit={handleClockOut}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,155 +1,37 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
5
|
-
import { Badge as c, Button as l, Card as u, CardContent as d, CardHeader as f, CardTitle as p, GlassCard as m, GradientText as h, PagePurpose as g } from "@burdenoff/fe-libs/ui";
|
|
1
|
+
import { useNavigate as e } from "react-router-dom";
|
|
2
|
+
import { jsx as t, jsxs as n } from "react/jsx-runtime";
|
|
3
|
+
import { Button as r, GradientText as i, IllustratedEmptyState as a, PagePurpose as o } from "@burdenoff/fe-libs/ui";
|
|
6
4
|
//#region src/crewfoundry/pages/LearningAssessmentsPagePage.tsx
|
|
7
|
-
|
|
8
|
-
let
|
|
9
|
-
return
|
|
10
|
-
};
|
|
11
|
-
function y() {
|
|
12
|
-
let { courses: y } = e(), b = t(() => y.slice(0, 16).map((e, t) => {
|
|
13
|
-
let n = t % 6 == 0 ? "overdue" : t % 2 == 0 ? "available" : "completed", r = 70 + t % 4 * 5;
|
|
14
|
-
return {
|
|
15
|
-
id: e.id,
|
|
16
|
-
title: `${e.title} Assessment`,
|
|
17
|
-
course: e.title,
|
|
18
|
-
questions: Math.max(10, e.modules * 4),
|
|
19
|
-
duration: `${Math.max(15, e.modules * 8)} min`,
|
|
20
|
-
status: n,
|
|
21
|
-
score: n === "completed" ? Math.min(99, Math.round(e.rating * 20 + 20)) : void 0,
|
|
22
|
-
dueDate: v(new Date(_ + (t - 3) * 864e5 * 6).toISOString()),
|
|
23
|
-
passingScore: r
|
|
24
|
-
};
|
|
25
|
-
}), [y]);
|
|
26
|
-
return /* @__PURE__ */ s("div", {
|
|
5
|
+
function s() {
|
|
6
|
+
let s = e();
|
|
7
|
+
return /* @__PURE__ */ n("div", {
|
|
27
8
|
className: "space-y-6 p-6",
|
|
28
|
-
children: [
|
|
29
|
-
/* @__PURE__ */
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
children: /* @__PURE__ */ o(h, { children: "Assessments" })
|
|
33
|
-
}),
|
|
34
|
-
/* @__PURE__ */ o("p", {
|
|
35
|
-
className: "text-sm text-text-secondary",
|
|
36
|
-
children: "Quizzes and exams for your enrolled courses"
|
|
37
|
-
}),
|
|
38
|
-
/* @__PURE__ */ o(g, {
|
|
39
|
-
className: "mt-1",
|
|
40
|
-
children: "Create and review assessments that measure what your team has learned — use them to certify skills and identify knowledge gaps."
|
|
41
|
-
})
|
|
42
|
-
] }),
|
|
43
|
-
/* @__PURE__ */ s("div", {
|
|
44
|
-
className: "grid gap-4 md:grid-cols-3",
|
|
45
|
-
children: [
|
|
46
|
-
/* @__PURE__ */ s(m, {
|
|
47
|
-
treatment: "glass",
|
|
48
|
-
glow: !0,
|
|
49
|
-
className: "border-border-seam",
|
|
50
|
-
children: [/* @__PURE__ */ s(f, {
|
|
51
|
-
className: "flex flex-row items-center justify-between space-y-0 pb-2",
|
|
52
|
-
children: [/* @__PURE__ */ o(p, {
|
|
53
|
-
className: "text-sm font-medium",
|
|
54
|
-
children: "Available"
|
|
55
|
-
}), /* @__PURE__ */ o(i, { className: "h-4 w-4 text-status-info-text" })]
|
|
56
|
-
}), /* @__PURE__ */ o(d, { children: /* @__PURE__ */ o("div", {
|
|
57
|
-
className: "text-2xl font-bold tabular-nums",
|
|
58
|
-
children: b.filter((e) => e.status === "available").length
|
|
59
|
-
}) })]
|
|
60
|
-
}),
|
|
61
|
-
/* @__PURE__ */ s(m, {
|
|
62
|
-
treatment: "metric",
|
|
63
|
-
className: "border-border-seam",
|
|
64
|
-
children: [/* @__PURE__ */ s(f, {
|
|
65
|
-
className: "flex flex-row items-center justify-between space-y-0 pb-2",
|
|
66
|
-
children: [/* @__PURE__ */ o(p, {
|
|
67
|
-
className: "text-sm font-medium",
|
|
68
|
-
children: "Completed"
|
|
69
|
-
}), /* @__PURE__ */ o(n, { className: "h-4 w-4 text-status-success-text" })]
|
|
70
|
-
}), /* @__PURE__ */ o(d, { children: /* @__PURE__ */ o("div", {
|
|
71
|
-
className: "text-2xl font-bold tabular-nums",
|
|
72
|
-
children: b.filter((e) => e.status === "completed").length
|
|
73
|
-
}) })]
|
|
74
|
-
}),
|
|
75
|
-
/* @__PURE__ */ s(m, {
|
|
76
|
-
treatment: "metric",
|
|
77
|
-
className: "border-border-seam",
|
|
78
|
-
children: [/* @__PURE__ */ s(f, {
|
|
79
|
-
className: "flex flex-row items-center justify-between space-y-0 pb-2",
|
|
80
|
-
children: [/* @__PURE__ */ o(p, {
|
|
81
|
-
className: "text-sm font-medium",
|
|
82
|
-
children: "Overdue"
|
|
83
|
-
}), /* @__PURE__ */ o(r, { className: "h-4 w-4 text-status-error-text" })]
|
|
84
|
-
}), /* @__PURE__ */ o(d, { children: /* @__PURE__ */ o("div", {
|
|
85
|
-
className: "text-2xl font-bold tabular-nums",
|
|
86
|
-
children: b.filter((e) => e.status === "overdue").length
|
|
87
|
-
}) })]
|
|
88
|
-
})
|
|
89
|
-
]
|
|
9
|
+
children: [/* @__PURE__ */ n("div", { children: [
|
|
10
|
+
/* @__PURE__ */ t("h1", {
|
|
11
|
+
className: "text-2xl font-semibold",
|
|
12
|
+
children: /* @__PURE__ */ t(i, { children: "Assessments" })
|
|
90
13
|
}),
|
|
91
|
-
/* @__PURE__ */
|
|
92
|
-
className: "
|
|
93
|
-
children:
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}),
|
|
110
|
-
/* @__PURE__ */ s("p", {
|
|
111
|
-
className: "text-xs text-text-secondary",
|
|
112
|
-
children: [
|
|
113
|
-
"Due: ",
|
|
114
|
-
e.dueDate,
|
|
115
|
-
" · Passing: ",
|
|
116
|
-
e.passingScore,
|
|
117
|
-
"%"
|
|
118
|
-
]
|
|
119
|
-
})
|
|
120
|
-
] }), /* @__PURE__ */ s("div", {
|
|
121
|
-
className: "flex items-center gap-2",
|
|
122
|
-
children: [
|
|
123
|
-
e.status === "completed" && e.score ? /* @__PURE__ */ s(c, {
|
|
124
|
-
variant: e.score >= e.passingScore ? "default" : "destructive",
|
|
125
|
-
className: "text-[10px]",
|
|
126
|
-
children: [
|
|
127
|
-
"Score: ",
|
|
128
|
-
e.score,
|
|
129
|
-
"%"
|
|
130
|
-
]
|
|
131
|
-
}) : null,
|
|
132
|
-
/* @__PURE__ */ o(c, {
|
|
133
|
-
variant: e.status === "completed" ? "default" : e.status === "overdue" ? "destructive" : "secondary",
|
|
134
|
-
className: "text-[10px]",
|
|
135
|
-
children: e.status
|
|
136
|
-
}),
|
|
137
|
-
e.status === "available" || e.status === "overdue" ? /* @__PURE__ */ s(l, {
|
|
138
|
-
size: "sm",
|
|
139
|
-
variant: "outline",
|
|
140
|
-
disabled: !0,
|
|
141
|
-
title: "Assessment taking is coming soon",
|
|
142
|
-
"aria-label": `Take ${e.title} — coming soon`,
|
|
143
|
-
children: [/* @__PURE__ */ o(a, { className: "mr-1 h-3 w-3" }), "Coming soon"]
|
|
144
|
-
}) : null
|
|
145
|
-
]
|
|
146
|
-
})]
|
|
147
|
-
}, e.id))
|
|
148
|
-
})] })
|
|
149
|
-
]
|
|
14
|
+
/* @__PURE__ */ t("p", {
|
|
15
|
+
className: "text-sm text-text-secondary",
|
|
16
|
+
children: "Quizzes and exams for your enrolled courses"
|
|
17
|
+
}),
|
|
18
|
+
/* @__PURE__ */ t(o, {
|
|
19
|
+
className: "mt-1",
|
|
20
|
+
children: "Create and review assessments that measure what your team has learned — use them to certify skills and identify knowledge gaps."
|
|
21
|
+
})
|
|
22
|
+
] }), /* @__PURE__ */ t(a, {
|
|
23
|
+
illustration: "empty-generic",
|
|
24
|
+
title: "Assessments are coming soon",
|
|
25
|
+
description: "Course assessments — question banks, timed attempts, scoring and pass thresholds — are being built. Once available, the quizzes tied to your enrolled courses will appear here.",
|
|
26
|
+
action: /* @__PURE__ */ t(r, {
|
|
27
|
+
variant: "outline",
|
|
28
|
+
onClick: () => s("../../learning/courses-page"),
|
|
29
|
+
children: "Browse courses"
|
|
30
|
+
})
|
|
31
|
+
})]
|
|
150
32
|
});
|
|
151
33
|
}
|
|
152
34
|
//#endregion
|
|
153
|
-
export {
|
|
35
|
+
export { s as LearningAssessmentsPagePage };
|
|
154
36
|
|
|
155
37
|
//# sourceMappingURL=LearningAssessmentsPagePage.js.map
|