@burdenoff/microfe-crewfoundry 2026.624.1 → 2026.625.2
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 +100 -76
- package/dist/crewfoundry/pages/AdminAdminPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsAnalyticsPagePage.js +118 -94
- package/dist/crewfoundry/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsEmployeeAnalyticsPagePage.js +105 -81
- package/dist/crewfoundry/pages/AnalyticsEmployeeAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js +134 -110
- package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.js +121 -97
- package/dist/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js +28 -15
- package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsCallsPagePage.js +74 -54
- package/dist/crewfoundry/pages/CommunicationsCallsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsDirectoryPagePage.js +87 -69
- package/dist/crewfoundry/pages/CommunicationsDirectoryPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsFilesPagePage.js +99 -80
- package/dist/crewfoundry/pages/CommunicationsFilesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.js +87 -68
- package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsTeamsPagePage.js +31 -15
- package/dist/crewfoundry/pages/CommunicationsTeamsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsVideoCallsPagePage.js +81 -61
- package/dist/crewfoundry/pages/CommunicationsVideoCallsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js +71 -51
- package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyCompanyDashboardPagePage.js +82 -58
- package/dist/crewfoundry/pages/CompanyCompanyDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyCompanyProfilePagePage.js +46 -39
- package/dist/crewfoundry/pages/CompanyCompanyProfilePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js +80 -62
- package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardAnalyticsPagePage.js +104 -80
- package/dist/crewfoundry/pages/DashboardAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardDashboardPagePage.js +219 -151
- package/dist/crewfoundry/pages/DashboardDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardEnhancedDashboardPagePage.js +183 -142
- package/dist/crewfoundry/pages/DashboardEnhancedDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardExecutiveDashboardPage.js +121 -97
- package/dist/crewfoundry/pages/DashboardExecutiveDashboardPage.js.map +1 -1
- package/dist/crewfoundry/pages/HelpApiDocumentationPagePage.js +57 -46
- package/dist/crewfoundry/pages/HelpApiDocumentationPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringApplicationsPagePage.js +91 -67
- package/dist/crewfoundry/pages/HiringApplicationsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringCandidatesPagePage.js +112 -93
- package/dist/crewfoundry/pages/HiringCandidatesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringCreateJobPagePage.js +55 -48
- package/dist/crewfoundry/pages/HiringCreateJobPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringInterviewsPagePage.js +70 -50
- package/dist/crewfoundry/pages/HiringInterviewsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js +140 -97
- package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringJobsPagePage.js +101 -85
- package/dist/crewfoundry/pages/HiringJobsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrDepartmentsPagePage.js +68 -48
- package/dist/crewfoundry/pages/HrDepartmentsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrEmployeesPagePage.js +99 -71
- package/dist/crewfoundry/pages/HrEmployeesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js +97 -73
- package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrPayrollPagePage.js +100 -76
- package/dist/crewfoundry/pages/HrPayrollPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js +93 -69
- package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningAssessmentsPagePage.js +65 -45
- package/dist/crewfoundry/pages/LearningAssessmentsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCertificatesPagePage.js +93 -75
- package/dist/crewfoundry/pages/LearningCertificatesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js +117 -72
- package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js +66 -54
- package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCoursesPagePage.js +36 -20
- package/dist/crewfoundry/pages/LearningCoursesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningAnalyticsPagePage.js +90 -66
- package/dist/crewfoundry/pages/LearningLearningAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPagePage.js +81 -57
- package/dist/crewfoundry/pages/LearningLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js +90 -65
- package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js +33 -17
- package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningMyLearningPagePage.js +83 -59
- package/dist/crewfoundry/pages/LearningMyLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceBookingManagementPagePage.js +68 -48
- package/dist/crewfoundry/pages/MarketplaceBookingManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceEarningsPagePage.js +80 -60
- package/dist/crewfoundry/pages/MarketplaceEarningsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.js +153 -118
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js +42 -37
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js +67 -47
- package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js +104 -85
- package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMessagingPagePage.js +76 -57
- package/dist/crewfoundry/pages/MarketplaceMessagingPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.js +78 -60
- package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.js +138 -93
- package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js +98 -53
- package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsEscrowPagePage.js +69 -49
- package/dist/crewfoundry/pages/PaymentsEscrowPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js +84 -65
- package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.js +76 -52
- package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsPaymentMethodsPagePage.js +58 -40
- package/dist/crewfoundry/pages/PaymentsPaymentMethodsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsSecurityPagePage.js +85 -65
- package/dist/crewfoundry/pages/PaymentsSecurityPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsCreateProjectPagePage.js +36 -29
- package/dist/crewfoundry/pages/ProjectsCreateProjectPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsProjectDetailsPagePage.js +108 -73
- package/dist/crewfoundry/pages/ProjectsProjectDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js +43 -27
- package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamAnalyticsPagePage.js +67 -47
- package/dist/crewfoundry/pages/TeamsTeamAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamDetailPagePage.js +108 -63
- package/dist/crewfoundry/pages/TeamsTeamDetailPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamDirectoryPagePage.js +83 -64
- package/dist/crewfoundry/pages/TeamsTeamDirectoryPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamSettingsPagePage.js +185 -144
- package/dist/crewfoundry/pages/TeamsTeamSettingsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamsPagePage.js +40 -24
- package/dist/crewfoundry/pages/TeamsTeamsPagePage.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,84 +1,102 @@
|
|
|
1
1
|
import { useCrewFoundryStore as e } from "../store/crewfoundryStore.js";
|
|
2
2
|
import { Mail as t, Plus as n, Star as r } from "lucide-react";
|
|
3
3
|
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
4
|
-
import { Badge as o, Button as s,
|
|
4
|
+
import { Badge as o, Button as s, CardContent as c, CardHeader as l, CardTitle as u, GlassCard as d, GradientText as f, IllustratedEmptyState as p, PagePurpose as m } from "@burdenoff/fe-libs/ui";
|
|
5
5
|
//#region src/crewfoundry/pages/CompanyVendorManagementPagePage.tsx
|
|
6
|
-
function
|
|
7
|
-
let { vendors:
|
|
6
|
+
function h() {
|
|
7
|
+
let { vendors: h } = e(), g = h.reduce((e, t) => e + t.contractValue, 0);
|
|
8
8
|
return /* @__PURE__ */ a("div", {
|
|
9
9
|
className: "space-y-6 p-6",
|
|
10
10
|
children: [/* @__PURE__ */ a("div", {
|
|
11
11
|
className: "flex items-center justify-between",
|
|
12
|
-
children: [/* @__PURE__ */ a("div", { children: [
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
12
|
+
children: [/* @__PURE__ */ a("div", { children: [
|
|
13
|
+
/* @__PURE__ */ i("h1", {
|
|
14
|
+
className: "text-2xl font-semibold",
|
|
15
|
+
children: /* @__PURE__ */ i(f, { children: "Vendor Management" })
|
|
16
|
+
}),
|
|
17
|
+
/* @__PURE__ */ a("p", {
|
|
18
|
+
className: "text-sm text-text-secondary",
|
|
19
|
+
children: [
|
|
20
|
+
h.length,
|
|
21
|
+
" vendors · $",
|
|
22
|
+
(g / 1e3).toFixed(0),
|
|
23
|
+
"K total contract value"
|
|
24
|
+
]
|
|
25
|
+
}),
|
|
26
|
+
/* @__PURE__ */ i(m, {
|
|
27
|
+
className: "mt-1",
|
|
28
|
+
children: "Track the vendors and suppliers you depend on — contracts, contacts, and status — to manage external relationships in one place."
|
|
29
|
+
})
|
|
30
|
+
] }), /* @__PURE__ */ a(s, {
|
|
24
31
|
size: "sm",
|
|
25
32
|
children: [/* @__PURE__ */ i(n, { className: "h-4 w-4 mr-1" }), "Add Vendor"]
|
|
26
33
|
})]
|
|
27
34
|
}), /* @__PURE__ */ i("div", {
|
|
28
35
|
className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
|
|
29
|
-
children:
|
|
30
|
-
className: "
|
|
31
|
-
children:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
36
|
+
children: h.length === 0 ? /* @__PURE__ */ i("div", {
|
|
37
|
+
className: "col-span-full",
|
|
38
|
+
children: /* @__PURE__ */ i(p, {
|
|
39
|
+
illustration: "empty-data",
|
|
40
|
+
title: "No vendors yet",
|
|
41
|
+
description: "Add a vendor to start tracking external relationships."
|
|
42
|
+
})
|
|
43
|
+
}) : h.map((e, n) => /* @__PURE__ */ a(d, {
|
|
44
|
+
treatment: n === 0 ? "glass" : "plain",
|
|
45
|
+
glow: n === 0,
|
|
46
|
+
children: [/* @__PURE__ */ i(l, { children: /* @__PURE__ */ a("div", {
|
|
47
|
+
className: "flex items-center justify-between",
|
|
48
|
+
children: [/* @__PURE__ */ i(u, {
|
|
49
|
+
className: "text-base",
|
|
50
|
+
children: e.name
|
|
51
|
+
}), /* @__PURE__ */ i(o, {
|
|
52
|
+
variant: e.status === "active" ? "default" : "secondary",
|
|
44
53
|
className: "text-[10px]",
|
|
45
|
-
children: e.
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
className: "
|
|
57
|
-
children: "
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
})] })]
|
|
62
|
-
}),
|
|
63
|
-
/* @__PURE__ */ a("div", {
|
|
64
|
-
className: "flex items-center justify-between pt-2 border-t mt-2",
|
|
65
|
-
children: [/* @__PURE__ */ a("div", {
|
|
66
|
-
className: "flex items-center gap-1 text-xs text-text-secondary",
|
|
67
|
-
children: [/* @__PURE__ */ i(t, { className: "h-3 w-3" }), e.email]
|
|
68
|
-
}), /* @__PURE__ */ a("div", {
|
|
69
|
-
className: "flex items-center gap-1",
|
|
70
|
-
children: [/* @__PURE__ */ i(r, { className: "h-3 w-3 text-yellow-500" }), /* @__PURE__ */ i("span", {
|
|
54
|
+
children: e.status
|
|
55
|
+
})]
|
|
56
|
+
}) }), /* @__PURE__ */ i(c, { children: /* @__PURE__ */ a("div", {
|
|
57
|
+
className: "space-y-2",
|
|
58
|
+
children: [
|
|
59
|
+
/* @__PURE__ */ i(o, {
|
|
60
|
+
variant: "outline",
|
|
61
|
+
className: "text-[10px]",
|
|
62
|
+
children: e.category
|
|
63
|
+
}),
|
|
64
|
+
/* @__PURE__ */ a("div", {
|
|
65
|
+
className: "grid grid-cols-2 gap-2 mt-2",
|
|
66
|
+
children: [/* @__PURE__ */ a("div", { children: [/* @__PURE__ */ i("p", {
|
|
67
|
+
className: "text-xs text-text-secondary",
|
|
68
|
+
children: "Contact"
|
|
69
|
+
}), /* @__PURE__ */ i("p", {
|
|
71
70
|
className: "text-sm",
|
|
72
|
-
children: e.
|
|
71
|
+
children: e.contactPerson
|
|
72
|
+
})] }), /* @__PURE__ */ a("div", { children: [/* @__PURE__ */ i("p", {
|
|
73
|
+
className: "text-xs text-text-secondary",
|
|
74
|
+
children: "Contract Value"
|
|
75
|
+
}), /* @__PURE__ */ a("p", {
|
|
76
|
+
className: "text-sm font-medium",
|
|
77
|
+
children: ["$", e.contractValue.toLocaleString()]
|
|
78
|
+
})] })]
|
|
79
|
+
}),
|
|
80
|
+
/* @__PURE__ */ a("div", {
|
|
81
|
+
className: "flex items-center justify-between pt-2 border-t mt-2",
|
|
82
|
+
children: [/* @__PURE__ */ a("div", {
|
|
83
|
+
className: "flex items-center gap-1 text-xs text-text-secondary",
|
|
84
|
+
children: [/* @__PURE__ */ i(t, { className: "h-3 w-3" }), e.email]
|
|
85
|
+
}), /* @__PURE__ */ a("div", {
|
|
86
|
+
className: "flex items-center gap-1",
|
|
87
|
+
children: [/* @__PURE__ */ i(r, { className: "h-3 w-3 text-status-warning-text" }), /* @__PURE__ */ i("span", {
|
|
88
|
+
className: "text-sm",
|
|
89
|
+
children: e.rating
|
|
90
|
+
})]
|
|
73
91
|
})]
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
]
|
|
77
|
-
}
|
|
92
|
+
})
|
|
93
|
+
]
|
|
94
|
+
}) })]
|
|
95
|
+
}, e.id))
|
|
78
96
|
})]
|
|
79
97
|
});
|
|
80
98
|
}
|
|
81
99
|
//#endregion
|
|
82
|
-
export {
|
|
100
|
+
export { h as CompanyVendorManagementPagePage };
|
|
83
101
|
|
|
84
102
|
//# sourceMappingURL=CompanyVendorManagementPagePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CompanyVendorManagementPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/CompanyVendorManagementPagePage.tsx"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"CompanyVendorManagementPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/CompanyVendorManagementPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Button,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { DollarSign, Mail, Phone, Plus, Star } from 'lucide-react';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\nexport function CompanyVendorManagementPagePage() {\n const { vendors } = useCrewFoundryStore();\n const totalContractValue = vendors.reduce((s, v) => s + v.contractValue, 0);\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>Vendor Management</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {vendors.length} vendors · ${(totalContractValue / 1000).toFixed(0)}K total contract\n value\n </p>\n <PagePurpose className=\"mt-1\">Track the vendors and suppliers you depend on — contracts, contacts, and status — to manage external relationships in one place.</PagePurpose>\n </div>\n <Button size=\"sm\">\n <Plus className=\"h-4 w-4 mr-1\" />\n Add Vendor\n </Button>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {vendors.length === 0 ? (\n <div className=\"col-span-full\">\n <IllustratedEmptyState illustration=\"empty-data\" title=\"No vendors yet\" description=\"Add a vendor to start tracking external relationships.\" />\n </div>\n ) : (\n vendors.map((vendor, _idx) => (\n <GlassCard key={vendor.id} treatment={_idx === 0 ? 'glass' : 'plain'} glow={_idx === 0}>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">{vendor.name}</CardTitle>\n <Badge\n variant={vendor.status === 'active' ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {vendor.status}\n </Badge>\n </div>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-2\">\n <Badge variant=\"outline\" className=\"text-[10px]\">\n {vendor.category}\n </Badge>\n <div className=\"grid grid-cols-2 gap-2 mt-2\">\n <div>\n <p className=\"text-xs text-text-secondary\">Contact</p>\n <p className=\"text-sm\">{vendor.contactPerson}</p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">Contract Value</p>\n <p className=\"text-sm font-medium\">${vendor.contractValue.toLocaleString()}</p>\n </div>\n </div>\n <div className=\"flex items-center justify-between pt-2 border-t mt-2\">\n <div className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <Mail className=\"h-3 w-3\" />\n {vendor.email}\n </div>\n <div className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-status-warning-text\" />\n <span className=\"text-sm\">{vendor.rating}</span>\n </div>\n </div>\n </div>\n </CardContent>\n </GlassCard>\n )))}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AAcA,SAAgB,IAAkC;CAChD,IAAM,EAAE,eAAY,GAAqB,EACnC,IAAqB,EAAQ,QAAQ,GAAG,MAAM,IAAI,EAAE,eAAe,EAAE;AAE3E,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD,EAAA,UAAA;IACE,kBAAC,MAAD;KAAI,WAAU;eACZ,kBAAC,GAAD,EAAA,UAAc,qBAAgC,CAAA;KAC3C,CAAA;IACL,kBAAC,KAAD;KAAG,WAAU;eAAb;MACG,EAAQ;MAAO;OAAc,IAAqB,KAAM,QAAQ,EAAE;MAAC;MAElE;;IACJ,kBAAC,GAAD;KAAa,WAAU;eAAO;KAA8I,CAAA;IACxK,EAAA,CAAA,EACN,kBAAC,GAAD;IAAQ,MAAK;cAAb,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,aAE1B;MACL;MACN,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAQ,WAAW,IAClB,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KAAuB,cAAa;KAAa,OAAM;KAAiB,aAAY;KAA2D,CAAA;IAC3I,CAAA,GAEN,EAAQ,KAAK,GAAQ,MACrB,kBAAC,GAAD;IAA2B,WAAW,MAAS,IAAI,UAAU;IAAS,MAAM,MAAS;cAArF,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MAAW,WAAU;gBAAa,EAAO;MAAiB,CAAA,EAC1D,kBAAC,GAAD;MACE,SAAS,EAAO,WAAW,WAAW,YAAY;MAClD,WAAU;gBAET,EAAO;MACF,CAAA,CACJ;QACK,CAAA,EACb,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OAAO,SAAQ;OAAU,WAAU;iBAChC,EAAO;OACF,CAAA;MACR,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBAA8B;QAAW,CAAA,EACtD,kBAAC,KAAD;QAAG,WAAU;kBAAW,EAAO;QAAkB,CAAA,CAC7C,EAAA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBAA8B;QAAkB,CAAA,EAC7D,kBAAC,KAAD;QAAG,WAAU;kBAAb,CAAmC,KAAE,EAAO,cAAc,gBAAgB,CAAK;UAC3E,EAAA,CAAA,CACF;;MACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAC3B,EAAO,MACJ;WACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD,EAAM,WAAU,oCAAqC,CAAA,EACrD,kBAAC,QAAD;SAAM,WAAU;mBAAW,EAAO;SAAc,CAAA,CAC5C;UACF;;MACF;QACM,CAAA,CACJ;MAvCI,EAAO,GAuCX,CACZ;GACE,CAAA,CACF"}
|
|
@@ -2,11 +2,11 @@ import { useCrewFoundryStore as e } from "../store/crewfoundryStore.js";
|
|
|
2
2
|
import { useMemo as t } from "react";
|
|
3
3
|
import { BarChart3 as n, DollarSign as r, Target as i, TrendingDown as a, TrendingUp as o, Users as s } from "lucide-react";
|
|
4
4
|
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
5
|
-
import { Badge as u, Card as d, CardContent as f, CardDescription as p, CardHeader as m, CardTitle as h } from "@burdenoff/fe-libs/ui";
|
|
5
|
+
import { Badge as u, Card as d, CardContent as f, CardDescription as p, CardHeader as m, CardTitle as h, GlassCard as g, GradientText as _, PagePurpose as v } from "@burdenoff/fe-libs/ui";
|
|
6
6
|
//#region src/crewfoundry/pages/DashboardAnalyticsPagePage.tsx
|
|
7
|
-
function
|
|
8
|
-
let { employees:
|
|
9
|
-
let e = [...
|
|
7
|
+
function y() {
|
|
8
|
+
let { employees: y, departments: b, jobPostings: x, payrollRecords: S, projects: C } = e(), w = t(() => {
|
|
9
|
+
let e = [...y].sort((e, t) => e.startDate.localeCompare(t.startDate));
|
|
10
10
|
return e.slice(-6).map((t, n) => {
|
|
11
11
|
let r = new Date(t.startDate).toLocaleString("en-US", { month: "short" });
|
|
12
12
|
return {
|
|
@@ -16,26 +16,26 @@ function g() {
|
|
|
16
16
|
headcount: Math.max(1, e.length - (5 - n))
|
|
17
17
|
};
|
|
18
18
|
});
|
|
19
|
-
}, [
|
|
19
|
+
}, [y]), T = Math.max(1, ...w.map((e) => e.headcount)), E = S.reduce((e, t) => e + t.netPay, 0), D = Math.round(y.reduce((e, t) => e + t.salary, 0) / Math.max(y.length, 1)), O = x.filter((e) => e.status === "active").length, k = y.reduce((e, t) => {
|
|
20
20
|
let n = t.type.replace("_", " ");
|
|
21
21
|
return e[n] = (e[n] || 0) + 1, e;
|
|
22
|
-
}, {}),
|
|
22
|
+
}, {}), A = t(() => {
|
|
23
23
|
let e = [
|
|
24
24
|
{
|
|
25
25
|
category: "Payroll",
|
|
26
|
-
amount:
|
|
26
|
+
amount: E
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
category: "Recruitment",
|
|
30
|
-
amount:
|
|
30
|
+
amount: x.reduce((e, t) => e + t.applicants * 120, 0)
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
33
|
category: "Project Delivery",
|
|
34
|
-
amount:
|
|
34
|
+
amount: C.reduce((e, t) => e + t.spent, 0)
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
37
|
category: "Department Ops",
|
|
38
|
-
amount:
|
|
38
|
+
amount: b.reduce((e, t) => e + t.budget * .03, 0)
|
|
39
39
|
}
|
|
40
40
|
], t = e.reduce((e, t) => e + t.amount, 0) || 1;
|
|
41
41
|
return e.map((e) => ({
|
|
@@ -43,76 +43,100 @@ function g() {
|
|
|
43
43
|
percentage: Math.round(e.amount / t * 100)
|
|
44
44
|
}));
|
|
45
45
|
}, [
|
|
46
|
-
_,
|
|
47
|
-
v,
|
|
48
46
|
b,
|
|
49
|
-
|
|
47
|
+
x,
|
|
48
|
+
C,
|
|
49
|
+
E
|
|
50
50
|
]);
|
|
51
51
|
return /* @__PURE__ */ l("div", {
|
|
52
52
|
className: "space-y-6 p-6",
|
|
53
53
|
children: [
|
|
54
|
-
/* @__PURE__ */ l("div", { children: [
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
/* @__PURE__ */ l("div", { children: [
|
|
55
|
+
/* @__PURE__ */ c("h1", {
|
|
56
|
+
className: "text-2xl font-semibold",
|
|
57
|
+
children: /* @__PURE__ */ c(_, { children: "Dashboard Analytics" })
|
|
58
|
+
}),
|
|
59
|
+
/* @__PURE__ */ c("p", {
|
|
60
|
+
className: "text-sm text-text-secondary",
|
|
61
|
+
children: "Workforce analytics and operational insights"
|
|
62
|
+
}),
|
|
63
|
+
/* @__PURE__ */ c(v, {
|
|
64
|
+
className: "mt-1",
|
|
65
|
+
children: "A consolidated analytics view across your organization — use it to monitor the metrics that matter most without digging through separate reports."
|
|
66
|
+
})
|
|
67
|
+
] }),
|
|
61
68
|
/* @__PURE__ */ l("div", {
|
|
62
69
|
className: "grid gap-4 md:grid-cols-2 lg:grid-cols-4",
|
|
63
70
|
children: [
|
|
64
|
-
/* @__PURE__ */ l(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
className: "
|
|
85
|
-
children: [
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}), /* @__PURE__ */
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
})
|
|
71
|
+
/* @__PURE__ */ l(g, {
|
|
72
|
+
treatment: "glass",
|
|
73
|
+
glow: !0,
|
|
74
|
+
className: "border-border-seam",
|
|
75
|
+
children: [/* @__PURE__ */ l(m, {
|
|
76
|
+
className: "flex flex-row items-center justify-between space-y-0 pb-2",
|
|
77
|
+
children: [/* @__PURE__ */ c(h, {
|
|
78
|
+
className: "text-sm font-medium",
|
|
79
|
+
children: "Total Headcount"
|
|
80
|
+
}), /* @__PURE__ */ c(s, { className: "h-4 w-4 text-text-secondary" })]
|
|
81
|
+
}), /* @__PURE__ */ l(f, { children: [/* @__PURE__ */ c("div", {
|
|
82
|
+
className: "text-2xl font-bold tabular-nums",
|
|
83
|
+
children: y.length
|
|
84
|
+
}), /* @__PURE__ */ l("div", {
|
|
85
|
+
className: "flex items-center text-xs text-status-success-text",
|
|
86
|
+
children: [/* @__PURE__ */ c(o, { className: "mr-1 h-3 w-3" }), "Live workforce count"]
|
|
87
|
+
})] })]
|
|
88
|
+
}),
|
|
89
|
+
/* @__PURE__ */ l(g, {
|
|
90
|
+
treatment: "metric",
|
|
91
|
+
className: "border-border-seam",
|
|
92
|
+
children: [/* @__PURE__ */ l(m, {
|
|
93
|
+
className: "flex flex-row items-center justify-between space-y-0 pb-2",
|
|
94
|
+
children: [/* @__PURE__ */ c(h, {
|
|
95
|
+
className: "text-sm font-medium",
|
|
96
|
+
children: "Avg Salary"
|
|
97
|
+
}), /* @__PURE__ */ c(r, { className: "h-4 w-4 text-text-secondary" })]
|
|
98
|
+
}), /* @__PURE__ */ l(f, { children: [/* @__PURE__ */ l("div", {
|
|
99
|
+
className: "text-2xl font-bold tabular-nums",
|
|
100
|
+
children: ["$", D.toLocaleString()]
|
|
101
|
+
}), /* @__PURE__ */ l("div", {
|
|
102
|
+
className: "flex items-center text-xs text-status-success-text",
|
|
103
|
+
children: [/* @__PURE__ */ c(o, { className: "mr-1 h-3 w-3" }), "Updated from employee records"]
|
|
104
|
+
})] })]
|
|
105
|
+
}),
|
|
106
|
+
/* @__PURE__ */ l(g, {
|
|
107
|
+
treatment: "metric",
|
|
108
|
+
className: "border-border-seam",
|
|
109
|
+
children: [/* @__PURE__ */ l(m, {
|
|
110
|
+
className: "flex flex-row items-center justify-between space-y-0 pb-2",
|
|
111
|
+
children: [/* @__PURE__ */ c(h, {
|
|
112
|
+
className: "text-sm font-medium",
|
|
113
|
+
children: "Attrition Rate"
|
|
114
|
+
}), /* @__PURE__ */ c(a, { className: "h-4 w-4 text-text-secondary" })]
|
|
115
|
+
}), /* @__PURE__ */ l(f, { children: [/* @__PURE__ */ l("div", {
|
|
116
|
+
className: "text-2xl font-bold tabular-nums",
|
|
117
|
+
children: [Math.round(y.filter((e) => e.status === "terminated").length / Math.max(y.length, 1) * 100), "%"]
|
|
118
|
+
}), /* @__PURE__ */ l("div", {
|
|
119
|
+
className: "flex items-center text-xs text-status-success-text",
|
|
120
|
+
children: [/* @__PURE__ */ c(a, { className: "mr-1 h-3 w-3" }), "Calculated from status mix"]
|
|
121
|
+
})] })]
|
|
122
|
+
}),
|
|
123
|
+
/* @__PURE__ */ l(g, {
|
|
124
|
+
treatment: "metric",
|
|
125
|
+
className: "border-border-seam",
|
|
126
|
+
children: [/* @__PURE__ */ l(m, {
|
|
127
|
+
className: "flex flex-row items-center justify-between space-y-0 pb-2",
|
|
128
|
+
children: [/* @__PURE__ */ c(h, {
|
|
129
|
+
className: "text-sm font-medium",
|
|
130
|
+
children: "Open Roles"
|
|
131
|
+
}), /* @__PURE__ */ c(i, { className: "h-4 w-4 text-text-secondary" })]
|
|
132
|
+
}), /* @__PURE__ */ l(f, { children: [/* @__PURE__ */ c("div", {
|
|
133
|
+
className: "text-2xl font-bold tabular-nums",
|
|
134
|
+
children: O
|
|
135
|
+
}), /* @__PURE__ */ c("p", {
|
|
136
|
+
className: "text-xs text-text-secondary",
|
|
137
|
+
children: "Based on active job postings"
|
|
138
|
+
})] })]
|
|
139
|
+
})
|
|
116
140
|
]
|
|
117
141
|
}),
|
|
118
142
|
/* @__PURE__ */ l("div", {
|
|
@@ -121,7 +145,7 @@ function g() {
|
|
|
121
145
|
className: "col-span-4",
|
|
122
146
|
children: [/* @__PURE__ */ l(m, { children: [/* @__PURE__ */ c(h, { children: "Headcount Trend" }), /* @__PURE__ */ c(p, { children: "Monthly headcount over recent months" })] }), /* @__PURE__ */ c(f, {
|
|
123
147
|
className: "space-y-3",
|
|
124
|
-
children:
|
|
148
|
+
children: w.map((e, t) => /* @__PURE__ */ l("div", {
|
|
125
149
|
className: "flex items-center gap-3",
|
|
126
150
|
children: [
|
|
127
151
|
/* @__PURE__ */ c("span", {
|
|
@@ -132,7 +156,7 @@ function g() {
|
|
|
132
156
|
className: "h-4 flex-1 overflow-hidden rounded bg-bg-sunken",
|
|
133
157
|
children: /* @__PURE__ */ c("div", {
|
|
134
158
|
className: "h-4 rounded bg-action-primary-bg",
|
|
135
|
-
style: { width: `${Math.round(e.headcount /
|
|
159
|
+
style: { width: `${Math.round(e.headcount / T * 100)}%` }
|
|
136
160
|
})
|
|
137
161
|
}),
|
|
138
162
|
/* @__PURE__ */ c("span", {
|
|
@@ -158,7 +182,7 @@ function g() {
|
|
|
158
182
|
className: "col-span-3",
|
|
159
183
|
children: [/* @__PURE__ */ l(m, { children: [/* @__PURE__ */ c(h, { children: "Employee Types" }), /* @__PURE__ */ c(p, { children: "Distribution by employment type" })] }), /* @__PURE__ */ c(f, {
|
|
160
184
|
className: "space-y-4",
|
|
161
|
-
children: Object.entries(
|
|
185
|
+
children: Object.entries(k).sort(([, e], [, t]) => t - e).map(([e, t]) => /* @__PURE__ */ l("div", {
|
|
162
186
|
className: "flex items-center justify-between",
|
|
163
187
|
children: [/* @__PURE__ */ l("div", {
|
|
164
188
|
className: "flex items-center gap-2",
|
|
@@ -175,7 +199,7 @@ function g() {
|
|
|
175
199
|
className: "text-xs text-text-secondary",
|
|
176
200
|
children: [
|
|
177
201
|
"(",
|
|
178
|
-
Math.round(t / Math.max(
|
|
202
|
+
Math.round(t / Math.max(y.length, 1) * 100),
|
|
179
203
|
"%)"
|
|
180
204
|
]
|
|
181
205
|
})]
|
|
@@ -186,7 +210,7 @@ function g() {
|
|
|
186
210
|
}),
|
|
187
211
|
/* @__PURE__ */ l(d, { children: [/* @__PURE__ */ l(m, { children: [/* @__PURE__ */ c(h, { children: "Workforce Cost Breakdown" }), /* @__PURE__ */ c(p, { children: "Operational costs by category" })] }), /* @__PURE__ */ l(f, { children: [/* @__PURE__ */ c("div", {
|
|
188
212
|
className: "space-y-3",
|
|
189
|
-
children:
|
|
213
|
+
children: A.map((e) => /* @__PURE__ */ l("div", {
|
|
190
214
|
className: "flex items-center gap-4",
|
|
191
215
|
children: [
|
|
192
216
|
/* @__PURE__ */ c("span", {
|
|
@@ -223,7 +247,7 @@ function g() {
|
|
|
223
247
|
className: "font-bold",
|
|
224
248
|
children: [
|
|
225
249
|
"$",
|
|
226
|
-
(
|
|
250
|
+
(A.reduce((e, t) => e + t.amount, 0) / 1e3).toFixed(0),
|
|
227
251
|
"K"
|
|
228
252
|
]
|
|
229
253
|
})]
|
|
@@ -246,7 +270,7 @@ function g() {
|
|
|
246
270
|
children: "Status"
|
|
247
271
|
})
|
|
248
272
|
]
|
|
249
|
-
}),
|
|
273
|
+
}), b.map((e) => /* @__PURE__ */ l("div", {
|
|
250
274
|
className: "grid grid-cols-5 gap-2 border-b py-2 text-sm last:border-0",
|
|
251
275
|
children: [
|
|
252
276
|
/* @__PURE__ */ c("span", {
|
|
@@ -282,6 +306,6 @@ function g() {
|
|
|
282
306
|
});
|
|
283
307
|
}
|
|
284
308
|
//#endregion
|
|
285
|
-
export {
|
|
309
|
+
export { y as DashboardAnalyticsPagePage };
|
|
286
310
|
|
|
287
311
|
//# sourceMappingURL=DashboardAnalyticsPagePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DashboardAnalyticsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/DashboardAnalyticsPagePage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { BarChart3, DollarSign, Target, TrendingDown, TrendingUp, Users } from 'lucide-react';\nimport {\n Badge,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\ntype MonthlyTrend = {\n month: string;\n hires: number;\n attrition: number;\n headcount: number;\n};\n\ntype CostItem = {\n category: string;\n amount: number;\n percentage: number;\n};\n\nexport function DashboardAnalyticsPagePage() {\n const { employees, departments, jobPostings, payrollRecords, projects } = useCrewFoundryStore();\n\n const monthlyTrends = useMemo<MonthlyTrend[]>(() => {\n const sorted = [...employees].sort((a, b) => a.startDate.localeCompare(b.startDate));\n return sorted.slice(-6).map((employee, index) => {\n const month = new Date(employee.startDate).toLocaleString('en-US', { month: 'short' });\n const hires = sorted.filter(\n (item) =>\n new Date(item.startDate).toLocaleString('en-US', { month: 'short' }) === month &&\n item.status === 'active'\n ).length;\n const attrition = sorted.filter(\n (item) =>\n new Date(item.startDate).toLocaleString('en-US', { month: 'short' }) === month &&\n item.status === 'terminated'\n ).length;\n return {\n month,\n hires,\n attrition,\n headcount: Math.max(1, sorted.length - (5 - index)),\n };\n });\n }, [employees]);\n\n const maxTrend = Math.max(1, ...monthlyTrends.map((trend) => trend.headcount));\n\n const totalPayroll = payrollRecords.reduce((sum, record) => sum + record.netPay, 0);\n const avgSalary = Math.round(\n employees.reduce((sum, employee) => sum + employee.salary, 0) / Math.max(employees.length, 1)\n );\n const activePostings = jobPostings.filter((posting) => posting.status === 'active').length;\n\n const typeDistribution = employees.reduce<Record<string, number>>((acc, employee) => {\n const key = employee.type.replace('_', ' ');\n acc[key] = (acc[key] || 0) + 1;\n return acc;\n }, {});\n\n const costBreakdown = useMemo<CostItem[]>(() => {\n const costs: Array<{ category: string; amount: number }> = [\n { category: 'Payroll', amount: totalPayroll },\n {\n category: 'Recruitment',\n amount: jobPostings.reduce((sum, posting) => sum + posting.applicants * 120, 0),\n },\n {\n category: 'Project Delivery',\n amount: projects.reduce((sum, project) => sum + project.spent, 0),\n },\n {\n category: 'Department Ops',\n amount: departments.reduce((sum, department) => sum + department.budget * 0.03, 0),\n },\n ];\n const total = costs.reduce((sum, item) => sum + item.amount, 0) || 1;\n return costs.map((item) => ({\n ...item,\n percentage: Math.round((item.amount / total) * 100),\n }));\n }, [departments, jobPostings, projects, totalPayroll]);\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Dashboard Analytics</h1>\n <p className=\"text-sm text-text-secondary\">\n Workforce analytics and operational insights\n </p>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Total Headcount</CardTitle>\n <Users className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold\">{employees.length}</div>\n <div className=\"flex items-center text-xs text-green-600\">\n <TrendingUp className=\"mr-1 h-3 w-3\" />\n Live workforce count\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Avg Salary</CardTitle>\n <DollarSign className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold\">${avgSalary.toLocaleString()}</div>\n <div className=\"flex items-center text-xs text-green-600\">\n <TrendingUp className=\"mr-1 h-3 w-3\" />\n Updated from employee records\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Attrition Rate</CardTitle>\n <TrendingDown className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold\">\n {Math.round(\n (employees.filter((employee) => employee.status === 'terminated').length /\n Math.max(employees.length, 1)) *\n 100\n )}\n %\n </div>\n <div className=\"flex items-center text-xs text-green-600\">\n <TrendingDown className=\"mr-1 h-3 w-3\" />\n Calculated from status mix\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Open Roles</CardTitle>\n <Target className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold\">{activePostings}</div>\n <p className=\"text-xs text-text-secondary\">Based on active job postings</p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-7\">\n <Card className=\"col-span-4\">\n <CardHeader>\n <CardTitle>Headcount Trend</CardTitle>\n <CardDescription>Monthly headcount over recent months</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {monthlyTrends.map((trend, index) => (\n <div key={`${trend.month}-${index}`} className=\"flex items-center gap-3\">\n <span className=\"w-10 text-sm text-text-secondary\">{trend.month}</span>\n <div className=\"h-4 flex-1 overflow-hidden rounded bg-bg-sunken\">\n <div\n className=\"h-4 rounded bg-action-primary-bg\"\n style={{ width: `${Math.round((trend.headcount / maxTrend) * 100)}%` }}\n />\n </div>\n <span className=\"w-10 text-right text-sm font-medium\">{trend.headcount}</span>\n <div className=\"flex w-24 gap-1\">\n <Badge variant=\"default\" className=\"text-[10px]\">\n +{trend.hires}\n </Badge>\n <Badge variant=\"destructive\" className=\"text-[10px]\">\n -{trend.attrition}\n </Badge>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card className=\"col-span-3\">\n <CardHeader>\n <CardTitle>Employee Types</CardTitle>\n <CardDescription>Distribution by employment type</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {Object.entries(typeDistribution)\n .sort(([, a], [, b]) => b - a)\n .map(([type, count]) => (\n <div key={type} className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4 text-text-secondary\" />\n <span className=\"text-sm capitalize\">{type}</span>\n </div>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium\">{count}</span>\n <span className=\"text-xs text-text-secondary\">\n ({Math.round((count / Math.max(employees.length, 1)) * 100)}%)\n </span>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Workforce Cost Breakdown</CardTitle>\n <CardDescription>Operational costs by category</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {costBreakdown.map((item) => (\n <div key={item.category} className=\"flex items-center gap-4\">\n <span className=\"w-48 text-sm text-text-secondary\">{item.category}</span>\n <div className=\"h-3 flex-1 overflow-hidden rounded bg-bg-sunken\">\n <div\n className=\"h-3 rounded bg-action-primary-bg/80\"\n style={{ width: `${item.percentage}%` }}\n />\n </div>\n <span className=\"w-24 text-right text-sm font-medium\">\n ${(item.amount / 1000).toFixed(0)}K\n </span>\n <span className=\"w-10 text-right text-xs text-text-secondary\">\n {item.percentage}%\n </span>\n </div>\n ))}\n </div>\n <div className=\"mt-4 flex justify-between border-t pt-4 text-sm\">\n <span className=\"font-medium\">Total Monthly Cost</span>\n <span className=\"font-bold\">\n ${(costBreakdown.reduce((sum, item) => sum + item.amount, 0) / 1000).toFixed(0)}K\n </span>\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Department Overview</CardTitle>\n <CardDescription>Key metrics by department</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"mb-2 grid grid-cols-5 gap-2 border-b pb-2 text-xs font-medium text-text-secondary\">\n <span>Department</span>\n <span>Head</span>\n <span className=\"text-right\">Employees</span>\n <span className=\"text-right\">Budget</span>\n <span className=\"text-right\">Status</span>\n </div>\n {departments.map((department) => (\n <div\n key={department.id}\n className=\"grid grid-cols-5 gap-2 border-b py-2 text-sm last:border-0\"\n >\n <span className=\"font-medium\">{department.name}</span>\n <span className=\"text-text-secondary\">{department.headName}</span>\n <span className=\"text-right\">{department.employeeCount}</span>\n <span className=\"text-right\">${(department.budget / 1000000).toFixed(1)}M</span>\n <div className=\"text-right\">\n <Badge variant={department.status === 'active' ? 'default' : 'secondary'}>\n {department.status}\n </Badge>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DashboardAnalyticsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/DashboardAnalyticsPagePage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { BarChart3, DollarSign, Target, TrendingDown, TrendingUp, Users } from 'lucide-react';\nimport {\n Badge,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\ntype MonthlyTrend = {\n month: string;\n hires: number;\n attrition: number;\n headcount: number;\n};\n\ntype CostItem = {\n category: string;\n amount: number;\n percentage: number;\n};\n\nexport function DashboardAnalyticsPagePage() {\n const { employees, departments, jobPostings, payrollRecords, projects } = useCrewFoundryStore();\n\n const monthlyTrends = useMemo<MonthlyTrend[]>(() => {\n const sorted = [...employees].sort((a, b) => a.startDate.localeCompare(b.startDate));\n return sorted.slice(-6).map((employee, index) => {\n const month = new Date(employee.startDate).toLocaleString('en-US', { month: 'short' });\n const hires = sorted.filter(\n (item) =>\n new Date(item.startDate).toLocaleString('en-US', { month: 'short' }) === month &&\n item.status === 'active'\n ).length;\n const attrition = sorted.filter(\n (item) =>\n new Date(item.startDate).toLocaleString('en-US', { month: 'short' }) === month &&\n item.status === 'terminated'\n ).length;\n return {\n month,\n hires,\n attrition,\n headcount: Math.max(1, sorted.length - (5 - index)),\n };\n });\n }, [employees]);\n\n const maxTrend = Math.max(1, ...monthlyTrends.map((trend) => trend.headcount));\n\n const totalPayroll = payrollRecords.reduce((sum, record) => sum + record.netPay, 0);\n const avgSalary = Math.round(\n employees.reduce((sum, employee) => sum + employee.salary, 0) / Math.max(employees.length, 1)\n );\n const activePostings = jobPostings.filter((posting) => posting.status === 'active').length;\n\n const typeDistribution = employees.reduce<Record<string, number>>((acc, employee) => {\n const key = employee.type.replace('_', ' ');\n acc[key] = (acc[key] || 0) + 1;\n return acc;\n }, {});\n\n const costBreakdown = useMemo<CostItem[]>(() => {\n const costs: Array<{ category: string; amount: number }> = [\n { category: 'Payroll', amount: totalPayroll },\n {\n category: 'Recruitment',\n amount: jobPostings.reduce((sum, posting) => sum + posting.applicants * 120, 0),\n },\n {\n category: 'Project Delivery',\n amount: projects.reduce((sum, project) => sum + project.spent, 0),\n },\n {\n category: 'Department Ops',\n amount: departments.reduce((sum, department) => sum + department.budget * 0.03, 0),\n },\n ];\n const total = costs.reduce((sum, item) => sum + item.amount, 0) || 1;\n return costs.map((item) => ({\n ...item,\n percentage: Math.round((item.amount / total) * 100),\n }));\n }, [departments, jobPostings, projects, totalPayroll]);\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Dashboard Analytics</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Workforce analytics and operational insights</p>\n <PagePurpose className=\"mt-1\">A consolidated analytics view across your organization — use it to monitor the metrics that matter most without digging through separate reports.</PagePurpose>\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 Headcount</CardTitle>\n <Users className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{employees.length}</div>\n <div className=\"flex items-center text-xs text-status-success-text\">\n <TrendingUp className=\"mr-1 h-3 w-3\" />\n Live workforce count\n </div>\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 Salary</CardTitle>\n <DollarSign className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">${avgSalary.toLocaleString()}</div>\n <div className=\"flex items-center text-xs text-status-success-text\">\n <TrendingUp className=\"mr-1 h-3 w-3\" />\n Updated from employee records\n </div>\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\">Attrition Rate</CardTitle>\n <TrendingDown className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n {Math.round(\n (employees.filter((employee) => employee.status === 'terminated').length /\n Math.max(employees.length, 1)) *\n 100\n )}\n %\n </div>\n <div className=\"flex items-center text-xs text-status-success-text\">\n <TrendingDown className=\"mr-1 h-3 w-3\" />\n Calculated from status mix\n </div>\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\">Open Roles</CardTitle>\n <Target className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{activePostings}</div>\n <p className=\"text-xs text-text-secondary\">Based on active job postings</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>Headcount Trend</CardTitle>\n <CardDescription>Monthly headcount over recent months</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {monthlyTrends.map((trend, index) => (\n <div key={`${trend.month}-${index}`} className=\"flex items-center gap-3\">\n <span className=\"w-10 text-sm text-text-secondary\">{trend.month}</span>\n <div className=\"h-4 flex-1 overflow-hidden rounded bg-bg-sunken\">\n <div\n className=\"h-4 rounded bg-action-primary-bg\"\n style={{ width: `${Math.round((trend.headcount / maxTrend) * 100)}%` }}\n />\n </div>\n <span className=\"w-10 text-right text-sm font-medium\">{trend.headcount}</span>\n <div className=\"flex w-24 gap-1\">\n <Badge variant=\"default\" className=\"text-[10px]\">\n +{trend.hires}\n </Badge>\n <Badge variant=\"destructive\" className=\"text-[10px]\">\n -{trend.attrition}\n </Badge>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card className=\"col-span-3\">\n <CardHeader>\n <CardTitle>Employee Types</CardTitle>\n <CardDescription>Distribution by employment type</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {Object.entries(typeDistribution)\n .sort(([, a], [, b]) => b - a)\n .map(([type, count]) => (\n <div key={type} className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4 text-text-secondary\" />\n <span className=\"text-sm capitalize\">{type}</span>\n </div>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium\">{count}</span>\n <span className=\"text-xs text-text-secondary\">\n ({Math.round((count / Math.max(employees.length, 1)) * 100)}%)\n </span>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Workforce Cost Breakdown</CardTitle>\n <CardDescription>Operational costs by category</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {costBreakdown.map((item) => (\n <div key={item.category} className=\"flex items-center gap-4\">\n <span className=\"w-48 text-sm text-text-secondary\">{item.category}</span>\n <div className=\"h-3 flex-1 overflow-hidden rounded bg-bg-sunken\">\n <div\n className=\"h-3 rounded bg-action-primary-bg/80\"\n style={{ width: `${item.percentage}%` }}\n />\n </div>\n <span className=\"w-24 text-right text-sm font-medium\">\n ${(item.amount / 1000).toFixed(0)}K\n </span>\n <span className=\"w-10 text-right text-xs text-text-secondary\">\n {item.percentage}%\n </span>\n </div>\n ))}\n </div>\n <div className=\"mt-4 flex justify-between border-t pt-4 text-sm\">\n <span className=\"font-medium\">Total Monthly Cost</span>\n <span className=\"font-bold\">\n ${(costBreakdown.reduce((sum, item) => sum + item.amount, 0) / 1000).toFixed(0)}K\n </span>\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Department Overview</CardTitle>\n <CardDescription>Key metrics by department</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"mb-2 grid grid-cols-5 gap-2 border-b pb-2 text-xs font-medium text-text-secondary\">\n <span>Department</span>\n <span>Head</span>\n <span className=\"text-right\">Employees</span>\n <span className=\"text-right\">Budget</span>\n <span className=\"text-right\">Status</span>\n </div>\n {departments.map((department) => (\n <div\n key={department.id}\n className=\"grid grid-cols-5 gap-2 border-b py-2 text-sm last:border-0\"\n >\n <span className=\"font-medium\">{department.name}</span>\n <span className=\"text-text-secondary\">{department.headName}</span>\n <span className=\"text-right\">{department.employeeCount}</span>\n <span className=\"text-right\">${(department.budget / 1000000).toFixed(1)}M</span>\n <div className=\"text-right\">\n <Badge variant={department.status === 'active' ? 'default' : 'secondary'}>\n {department.status}\n </Badge>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":";;;;;;AA4BA,SAAgB,IAA6B;CAC3C,IAAM,EAAE,cAAW,gBAAa,gBAAa,mBAAgB,gBAAa,GAAqB,EAEzF,IAAgB,QAA8B;EAClD,IAAM,IAAS,CAAC,GAAG,EAAU,CAAC,MAAM,GAAG,MAAM,EAAE,UAAU,cAAc,EAAE,UAAU,CAAC;AACpF,SAAO,EAAO,MAAM,GAAG,CAAC,KAAK,GAAU,MAAU;GAC/C,IAAM,IAAQ,IAAI,KAAK,EAAS,UAAU,CAAC,eAAe,SAAS,EAAE,OAAO,SAAS,CAAC;AAWtF,UAAO;IACL;IACA,OAZY,EAAO,QAClB,MACC,IAAI,KAAK,EAAK,UAAU,CAAC,eAAe,SAAS,EAAE,OAAO,SAAS,CAAC,KAAK,KACzE,EAAK,WAAW,SACnB,CAAC;IASA,WARgB,EAAO,QACtB,MACC,IAAI,KAAK,EAAK,UAAU,CAAC,eAAe,SAAS,EAAE,OAAO,SAAS,CAAC,KAAK,KACzE,EAAK,WAAW,aACnB,CAAC;IAKA,WAAW,KAAK,IAAI,GAAG,EAAO,UAAU,IAAI,GAAO;IACpD;IACD;IACD,CAAC,EAAU,CAAC,EAET,IAAW,KAAK,IAAI,GAAG,GAAG,EAAc,KAAK,MAAU,EAAM,UAAU,CAAC,EAExE,IAAe,EAAe,QAAQ,GAAK,MAAW,IAAM,EAAO,QAAQ,EAAE,EAC7E,IAAY,KAAK,MACrB,EAAU,QAAQ,GAAK,MAAa,IAAM,EAAS,QAAQ,EAAE,GAAG,KAAK,IAAI,EAAU,QAAQ,EAAE,CAC9F,EACK,IAAiB,EAAY,QAAQ,MAAY,EAAQ,WAAW,SAAS,CAAC,QAE9E,IAAmB,EAAU,QAAgC,GAAK,MAAa;EACnF,IAAM,IAAM,EAAS,KAAK,QAAQ,KAAK,IAAI;AAE3C,SADA,EAAI,MAAQ,EAAI,MAAQ,KAAK,GACtB;IACN,EAAE,CAAC,EAEA,IAAgB,QAA0B;EAC9C,IAAM,IAAqD;GACzD;IAAE,UAAU;IAAW,QAAQ;IAAc;GAC7C;IACE,UAAU;IACV,QAAQ,EAAY,QAAQ,GAAK,MAAY,IAAM,EAAQ,aAAa,KAAK,EAAE;IAChF;GACD;IACE,UAAU;IACV,QAAQ,EAAS,QAAQ,GAAK,MAAY,IAAM,EAAQ,OAAO,EAAE;IAClE;GACD;IACE,UAAU;IACV,QAAQ,EAAY,QAAQ,GAAK,MAAe,IAAM,EAAW,SAAS,KAAM,EAAE;IACnF;GACF,EACK,IAAQ,EAAM,QAAQ,GAAK,MAAS,IAAM,EAAK,QAAQ,EAAE,IAAI;AACnE,SAAO,EAAM,KAAK,OAAU;GAC1B,GAAG;GACH,YAAY,KAAK,MAAO,EAAK,SAAS,IAAS,IAAI;GACpD,EAAE;IACF;EAAC;EAAa;EAAa;EAAU;EAAa,CAAC;AAEtD,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD,EAAA,UAAA;IACE,kBAAC,MAAD;KAAI,WAAU;eACZ,kBAAC,GAAD,EAAA,UAAc,uBAAkC,CAAA;KAC7C,CAAA;IACL,kBAAC,KAAD;KAAG,WAAU;eAA8B;KAAgD,CAAA;IAC3F,kBAAC,GAAD;KAAa,WAAU;eAAO;KAA+J,CAAA;IACzL,EAAA,CAAA;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MAAW,WAAU;MAAQ,MAAA;MAAK,WAAU;gBAA5C,CACE,kBAAC,GAAD;OAAY,WAAU;iBAAtB,CACE,kBAAC,GAAD;QAAW,WAAU;kBAAsB;QAA2B,CAAA,EACtE,kBAAC,GAAD,EAAO,WAAU,+BAAgC,CAAA,CACtC;UACb,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAmC,EAAU;OAAa,CAAA,EACzE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAY,WAAU,gBAAiB,CAAA,EAAA,uBAEnC;SACM,EAAA,CAAA,CACJ;;KACZ,kBAAC,GAAD;MAAW,WAAU;MAAS,WAAU;gBAAxC,CACE,kBAAC,GAAD;OAAY,WAAU;iBAAtB,CACE,kBAAC,GAAD;QAAW,WAAU;kBAAsB;QAAsB,CAAA,EACjE,kBAAC,GAAD,EAAY,WAAU,+BAAgC,CAAA,CAC3C;UACb,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CAAiD,KAAE,EAAU,gBAAgB,CAAO;UACpF,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAY,WAAU,gBAAiB,CAAA,EAAA,gCAEnC;SACM,EAAA,CAAA,CACJ;;KACZ,kBAAC,GAAD;MAAW,WAAU;MAAS,WAAU;gBAAxC,CACE,kBAAC,GAAD;OAAY,WAAU;iBAAtB,CACE,kBAAC,GAAD;QAAW,WAAU;kBAAsB;QAA0B,CAAA,EACrE,kBAAC,GAAD,EAAc,WAAU,+BAAgC,CAAA,CAC7C;UACb,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACG,KAAK,MACH,EAAU,QAAQ,MAAa,EAAS,WAAW,aAAa,CAAC,SAChE,KAAK,IAAI,EAAU,QAAQ,EAAE,GAC7B,IACH,EAAC,IAEE;UACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAc,WAAU,gBAAiB,CAAA,EAAA,6BAErC;SACM,EAAA,CAAA,CACJ;;KACZ,kBAAC,GAAD;MAAW,WAAU;MAAS,WAAU;gBAAxC,CACE,kBAAC,GAAD;OAAY,WAAU;iBAAtB,CACE,kBAAC,GAAD;QAAW,WAAU;kBAAsB;QAAsB,CAAA,EACjE,kBAAC,GAAD,EAAQ,WAAU,+BAAgC,CAAA,CACvC;UACb,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAmC;OAAqB,CAAA,EACvE,kBAAC,KAAD;OAAG,WAAU;iBAA8B;OAAgC,CAAA,CAC/D,EAAA,CAAA,CACJ;;KACR;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAM,WAAU;eAAhB,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAW,mBAA2B,CAAA,EACtC,kBAAC,GAAD,EAAA,UAAiB,wCAAsD,CAAA,CAC5D,EAAA,CAAA,EACb,kBAAC,GAAD;MAAa,WAAU;gBACpB,EAAc,KAAK,GAAO,MACzB,kBAAC,OAAD;OAAqC,WAAU;iBAA/C;QACE,kBAAC,QAAD;SAAM,WAAU;mBAAoC,EAAM;SAAa,CAAA;QACvE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,OAAD;UACE,WAAU;UACV,OAAO,EAAE,OAAO,GAAG,KAAK,MAAO,EAAM,YAAY,IAAY,IAAI,CAAC,IAAI;UACtE,CAAA;SACE,CAAA;QACN,kBAAC,QAAD;SAAM,WAAU;mBAAuC,EAAM;SAAiB,CAAA;QAC9E,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAO,SAAQ;UAAU,WAAU;oBAAnC,CAAiD,KAC7C,EAAM,MACF;aACR,kBAAC,GAAD;UAAO,SAAQ;UAAc,WAAU;oBAAvC,CAAqD,KACjD,EAAM,UACF;YACJ;;QACF;SAjBI,GAAG,EAAM,MAAM,GAAG,IAiBtB,CACN;MACU,CAAA,CACT;QAEP,kBAAC,GAAD;KAAM,WAAU;eAAhB,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAW,kBAA0B,CAAA,EACrC,kBAAC,GAAD,EAAA,UAAiB,mCAAiD,CAAA,CACvD,EAAA,CAAA,EACb,kBAAC,GAAD;MAAa,WAAU;gBACpB,OAAO,QAAQ,EAAiB,CAC9B,MAAM,GAAG,IAAI,GAAG,OAAO,IAAI,EAAE,CAC7B,KAAK,CAAC,GAAM,OACX,kBAAC,OAAD;OAAgB,WAAU;iBAA1B,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD,EAAW,WAAU,+BAAgC,CAAA,EACrD,kBAAC,QAAD;SAAM,WAAU;mBAAsB;SAAY,CAAA,CAC9C;WACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAuB;SAAa,CAAA,EACpD,kBAAC,QAAD;SAAM,WAAU;mBAAhB;UAA8C;UAC1C,KAAK,MAAO,IAAQ,KAAK,IAAI,EAAU,QAAQ,EAAE,GAAI,IAAI;UAAC;UACvD;WACH;UACF;SAXI,EAWJ,CACN;MACQ,CAAA,CACT;OACH;;GAEN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAW,4BAAoC,CAAA,EAC/C,kBAAC,GAAD,EAAA,UAAiB,iCAA+C,CAAA,CACrD,EAAA,CAAA,EACb,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAc,KAAK,MAClB,kBAAC,OAAD;KAAyB,WAAU;eAAnC;MACE,kBAAC,QAAD;OAAM,WAAU;iBAAoC,EAAK;OAAgB,CAAA;MACzE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,OAAD;QACE,WAAU;QACV,OAAO,EAAE,OAAO,GAAG,EAAK,WAAW,IAAI;QACvC,CAAA;OACE,CAAA;MACN,kBAAC,QAAD;OAAM,WAAU;iBAAhB;QAAsD;SACjD,EAAK,SAAS,KAAM,QAAQ,EAAE;QAAC;QAC7B;;MACP,kBAAC,QAAD;OAAM,WAAU;iBAAhB,CACG,EAAK,YAAW,IACZ;;MACH;OAdI,EAAK,SAcT,CACN;IACE,CAAA,EACN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,QAAD;KAAM,WAAU;eAAc;KAAyB,CAAA,EACvD,kBAAC,QAAD;KAAM,WAAU;eAAhB;MAA4B;OACvB,EAAc,QAAQ,GAAK,MAAS,IAAM,EAAK,QAAQ,EAAE,GAAG,KAAM,QAAQ,EAAE;MAAC;MAC3E;OACH;MACM,EAAA,CAAA,CACT,EAAA,CAAA;GAEP,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAW,uBAA+B,CAAA,EAC1C,kBAAC,GAAD,EAAA,UAAiB,6BAA2C,CAAA,CACjD,EAAA,CAAA,EACb,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,QAAD,EAAA,UAAM,cAAiB,CAAA;KACvB,kBAAC,QAAD,EAAA,UAAM,QAAW,CAAA;KACjB,kBAAC,QAAD;MAAM,WAAU;gBAAa;MAAgB,CAAA;KAC7C,kBAAC,QAAD;MAAM,WAAU;gBAAa;MAAa,CAAA;KAC1C,kBAAC,QAAD;MAAM,WAAU;gBAAa;MAAa,CAAA;KACtC;OACL,EAAY,KAAK,MAChB,kBAAC,OAAD;IAEE,WAAU;cAFZ;KAIE,kBAAC,QAAD;MAAM,WAAU;gBAAe,EAAW;MAAY,CAAA;KACtD,kBAAC,QAAD;MAAM,WAAU;gBAAuB,EAAW;MAAgB,CAAA;KAClE,kBAAC,QAAD;MAAM,WAAU;gBAAc,EAAW;MAAqB,CAAA;KAC9D,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OAA6B;QAAG,EAAW,SAAS,KAAS,QAAQ,EAAE;OAAC;OAAQ;;KAChF,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD;OAAO,SAAS,EAAW,WAAW,WAAW,YAAY;iBAC1D,EAAW;OACN,CAAA;MACJ,CAAA;KACF;MAZC,EAAW,GAYZ,CACN,CACU,EAAA,CAAA,CACT,EAAA,CAAA;GACH"}
|