@burdenoff/microfe-crewfoundry 2026.1013.1 → 2026.1029.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.
Files changed (214) hide show
  1. package/dist/crewfoundry/CrewFoundryRoot.d.ts.map +1 -1
  2. package/dist/crewfoundry/CrewFoundryRoot.js +160 -256
  3. package/dist/crewfoundry/CrewFoundryRoot.js.map +1 -1
  4. package/dist/crewfoundry/CrewFoundryRoutes.d.ts.map +1 -1
  5. package/dist/crewfoundry/CrewFoundryRoutes.js +152 -192
  6. package/dist/crewfoundry/CrewFoundryRoutes.js.map +1 -1
  7. package/dist/crewfoundry/components/InlineError.d.ts +5 -0
  8. package/dist/crewfoundry/components/InlineError.d.ts.map +1 -0
  9. package/dist/crewfoundry/components/InlineError.js +17 -0
  10. package/dist/crewfoundry/components/InlineError.js.map +1 -0
  11. package/dist/crewfoundry/components/forms/FormModal.d.ts +25 -1
  12. package/dist/crewfoundry/components/forms/FormModal.d.ts.map +1 -1
  13. package/dist/crewfoundry/components/forms/FormModal.js +151 -136
  14. package/dist/crewfoundry/components/forms/FormModal.js.map +1 -1
  15. package/dist/crewfoundry/components/marketplace/BookServiceModal.d.ts +14 -0
  16. package/dist/crewfoundry/components/marketplace/BookServiceModal.d.ts.map +1 -0
  17. package/dist/crewfoundry/components/marketplace/BookServiceModal.js +75 -0
  18. package/dist/crewfoundry/components/marketplace/BookServiceModal.js.map +1 -0
  19. package/dist/crewfoundry/components/marketplace/MarketplaceTabs.d.ts +24 -0
  20. package/dist/crewfoundry/components/marketplace/MarketplaceTabs.d.ts.map +1 -0
  21. package/dist/crewfoundry/components/marketplace/MarketplaceTabs.js +47 -0
  22. package/dist/crewfoundry/components/marketplace/MarketplaceTabs.js.map +1 -0
  23. package/dist/crewfoundry/components/marketplace/ServiceListingModal.d.ts +12 -0
  24. package/dist/crewfoundry/components/marketplace/ServiceListingModal.d.ts.map +1 -0
  25. package/dist/crewfoundry/components/marketplace/ServiceListingModal.js +123 -0
  26. package/dist/crewfoundry/components/marketplace/ServiceListingModal.js.map +1 -0
  27. package/dist/crewfoundry/components/viz/CertificateCard.js +4 -118
  28. package/dist/crewfoundry/components/viz/PathSteps.js +3 -68
  29. package/dist/crewfoundry/components/viz/lib/geometry.js +5 -13
  30. package/dist/crewfoundry/components/viz/lib/geometry.js.map +1 -1
  31. package/dist/crewfoundry/lib/analytics.d.ts +1 -6
  32. package/dist/crewfoundry/lib/analytics.d.ts.map +1 -1
  33. package/dist/crewfoundry/lib/analytics.js +3 -18
  34. package/dist/crewfoundry/lib/analytics.js.map +1 -1
  35. package/dist/crewfoundry/lib/companyFinance.d.ts +3 -7
  36. package/dist/crewfoundry/lib/companyFinance.d.ts.map +1 -1
  37. package/dist/crewfoundry/lib/companyFinance.js +3 -8
  38. package/dist/crewfoundry/lib/companyFinance.js.map +1 -1
  39. package/dist/crewfoundry/lib/learning.d.ts +12 -0
  40. package/dist/crewfoundry/lib/learning.d.ts.map +1 -0
  41. package/dist/crewfoundry/lib/learning.js +26 -0
  42. package/dist/crewfoundry/lib/learning.js.map +1 -0
  43. package/dist/crewfoundry/lib/marketplace.d.ts +30 -0
  44. package/dist/crewfoundry/lib/marketplace.d.ts.map +1 -0
  45. package/dist/crewfoundry/lib/marketplace.js +91 -0
  46. package/dist/crewfoundry/lib/marketplace.js.map +1 -0
  47. package/dist/crewfoundry/lib/useEnsureLoaded.d.ts +7 -0
  48. package/dist/crewfoundry/lib/useEnsureLoaded.d.ts.map +1 -0
  49. package/dist/crewfoundry/lib/useEnsureLoaded.js +25 -0
  50. package/dist/crewfoundry/lib/useEnsureLoaded.js.map +1 -0
  51. package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.d.ts.map +1 -1
  52. package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js +12 -25
  53. package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js.map +1 -1
  54. package/dist/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.d.ts.map +1 -1
  55. package/dist/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.js +49 -49
  56. package/dist/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.js.map +1 -1
  57. package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.d.ts.map +1 -1
  58. package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js +145 -157
  59. package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js.map +1 -1
  60. package/dist/crewfoundry/pages/CompanyClientManagementPagePage.d.ts.map +1 -1
  61. package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js +14 -14
  62. package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js.map +1 -1
  63. package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.d.ts.map +1 -1
  64. package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js +14 -14
  65. package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js.map +1 -1
  66. package/dist/crewfoundry/pages/HelpApiDocumentationPagePage.d.ts.map +1 -1
  67. package/dist/crewfoundry/pages/HelpApiDocumentationPagePage.js +71 -33
  68. package/dist/crewfoundry/pages/HelpApiDocumentationPagePage.js.map +1 -1
  69. package/dist/crewfoundry/pages/HiringCreateJobPagePage.d.ts.map +1 -1
  70. package/dist/crewfoundry/pages/HiringCreateJobPagePage.js +260 -115
  71. package/dist/crewfoundry/pages/HiringCreateJobPagePage.js.map +1 -1
  72. package/dist/crewfoundry/pages/HiringJobDetailsPagePage.d.ts.map +1 -1
  73. package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js +199 -166
  74. package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js.map +1 -1
  75. package/dist/crewfoundry/pages/HiringJobsPagePage.d.ts.map +1 -1
  76. package/dist/crewfoundry/pages/HiringJobsPagePage.js +118 -104
  77. package/dist/crewfoundry/pages/HiringJobsPagePage.js.map +1 -1
  78. package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.d.ts.map +1 -1
  79. package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js +247 -173
  80. package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js.map +1 -1
  81. package/dist/crewfoundry/pages/LearningCoursesPagePage.d.ts +12 -0
  82. package/dist/crewfoundry/pages/LearningCoursesPagePage.d.ts.map +1 -1
  83. package/dist/crewfoundry/pages/LearningCoursesPagePage.js +307 -293
  84. package/dist/crewfoundry/pages/LearningCoursesPagePage.js.map +1 -1
  85. package/dist/crewfoundry/pages/LearningEnrollmentsPagePage.d.ts +2 -0
  86. package/dist/crewfoundry/pages/LearningEnrollmentsPagePage.d.ts.map +1 -0
  87. package/dist/crewfoundry/pages/LearningEnrollmentsPagePage.js +223 -0
  88. package/dist/crewfoundry/pages/LearningEnrollmentsPagePage.js.map +1 -0
  89. package/dist/crewfoundry/pages/LearningLearningAnalyticsPagePage.d.ts +4 -0
  90. package/dist/crewfoundry/pages/LearningLearningAnalyticsPagePage.d.ts.map +1 -1
  91. package/dist/crewfoundry/pages/LearningLearningAnalyticsPagePage.js +115 -160
  92. package/dist/crewfoundry/pages/LearningLearningAnalyticsPagePage.js.map +1 -1
  93. package/dist/crewfoundry/pages/MarketplaceBookingManagementPagePage.d.ts.map +1 -1
  94. package/dist/crewfoundry/pages/MarketplaceBookingManagementPagePage.js +165 -75
  95. package/dist/crewfoundry/pages/MarketplaceBookingManagementPagePage.js.map +1 -1
  96. package/dist/crewfoundry/pages/MarketplaceEscrowPagePage.d.ts +7 -0
  97. package/dist/crewfoundry/pages/MarketplaceEscrowPagePage.d.ts.map +1 -0
  98. package/dist/crewfoundry/pages/MarketplaceEscrowPagePage.js +175 -0
  99. package/dist/crewfoundry/pages/MarketplaceEscrowPagePage.js.map +1 -0
  100. package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.d.ts.map +1 -1
  101. package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js +182 -223
  102. package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js.map +1 -1
  103. package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.d.ts.map +1 -1
  104. package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.js +168 -227
  105. package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.js.map +1 -1
  106. package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.d.ts.map +1 -1
  107. package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js +127 -290
  108. package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js.map +1 -1
  109. package/dist/crewfoundry/store/crewfoundryStore.d.ts +139 -79
  110. package/dist/crewfoundry/store/crewfoundryStore.d.ts.map +1 -1
  111. package/dist/crewfoundry/store/crewfoundryStore.js +323 -244
  112. package/dist/crewfoundry/store/crewfoundryStore.js.map +1 -1
  113. package/dist/crewfoundry/utils/dateUtils.d.ts +52 -0
  114. package/dist/crewfoundry/utils/dateUtils.d.ts.map +1 -0
  115. package/dist/crewfoundry/utils/dateUtils.js +31 -0
  116. package/dist/crewfoundry/utils/dateUtils.js.map +1 -0
  117. package/dist/generated/global-types.d.ts +5145 -1627
  118. package/dist/generated/global-types.d.ts.map +1 -1
  119. package/dist/generated/wspace-types.d.ts +100373 -34602
  120. package/dist/generated/wspace-types.d.ts.map +1 -1
  121. package/dist/utils/pageContext.d.ts.map +1 -1
  122. package/dist/utils/pageContext.js +0 -1
  123. package/dist/utils/pageContext.js.map +1 -1
  124. package/package.json +1 -1
  125. package/dist/crewfoundry/components/viz/CertificateCard.js.map +0 -1
  126. package/dist/crewfoundry/components/viz/PathSteps.js.map +0 -1
  127. package/dist/crewfoundry/pages/CommunicationsCallsPagePage.d.ts +0 -2
  128. package/dist/crewfoundry/pages/CommunicationsCallsPagePage.d.ts.map +0 -1
  129. package/dist/crewfoundry/pages/CommunicationsCallsPagePage.js +0 -53
  130. package/dist/crewfoundry/pages/CommunicationsCallsPagePage.js.map +0 -1
  131. package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.d.ts +0 -2
  132. package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.d.ts.map +0 -1
  133. package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.js +0 -107
  134. package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.js.map +0 -1
  135. package/dist/crewfoundry/pages/CommunicationsVideoCallsPagePage.d.ts +0 -2
  136. package/dist/crewfoundry/pages/CommunicationsVideoCallsPagePage.d.ts.map +0 -1
  137. package/dist/crewfoundry/pages/CommunicationsVideoCallsPagePage.js +0 -62
  138. package/dist/crewfoundry/pages/CommunicationsVideoCallsPagePage.js.map +0 -1
  139. package/dist/crewfoundry/pages/LearningAssessmentsPagePage.d.ts +0 -2
  140. package/dist/crewfoundry/pages/LearningAssessmentsPagePage.d.ts.map +0 -1
  141. package/dist/crewfoundry/pages/LearningAssessmentsPagePage.js +0 -37
  142. package/dist/crewfoundry/pages/LearningAssessmentsPagePage.js.map +0 -1
  143. package/dist/crewfoundry/pages/LearningCertificatesPagePage.d.ts +0 -2
  144. package/dist/crewfoundry/pages/LearningCertificatesPagePage.d.ts.map +0 -1
  145. package/dist/crewfoundry/pages/LearningCertificatesPagePage.js +0 -111
  146. package/dist/crewfoundry/pages/LearningCertificatesPagePage.js.map +0 -1
  147. package/dist/crewfoundry/pages/LearningCourseLearningPagePage.d.ts +0 -2
  148. package/dist/crewfoundry/pages/LearningCourseLearningPagePage.d.ts.map +0 -1
  149. package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js +0 -309
  150. package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js.map +0 -1
  151. package/dist/crewfoundry/pages/LearningLearningPagePage.d.ts +0 -2
  152. package/dist/crewfoundry/pages/LearningLearningPagePage.d.ts.map +0 -1
  153. package/dist/crewfoundry/pages/LearningLearningPagePage.js +0 -200
  154. package/dist/crewfoundry/pages/LearningLearningPagePage.js.map +0 -1
  155. package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.d.ts +0 -2
  156. package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.d.ts.map +0 -1
  157. package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js +0 -190
  158. package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js.map +0 -1
  159. package/dist/crewfoundry/pages/LearningLearningPathsPagePage.d.ts +0 -2
  160. package/dist/crewfoundry/pages/LearningLearningPathsPagePage.d.ts.map +0 -1
  161. package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js +0 -230
  162. package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js.map +0 -1
  163. package/dist/crewfoundry/pages/LearningMyLearningPagePage.d.ts +0 -2
  164. package/dist/crewfoundry/pages/LearningMyLearningPagePage.d.ts.map +0 -1
  165. package/dist/crewfoundry/pages/LearningMyLearningPagePage.js +0 -293
  166. package/dist/crewfoundry/pages/LearningMyLearningPagePage.js.map +0 -1
  167. package/dist/crewfoundry/pages/MarketplaceEarningsPagePage.d.ts +0 -2
  168. package/dist/crewfoundry/pages/MarketplaceEarningsPagePage.d.ts.map +0 -1
  169. package/dist/crewfoundry/pages/MarketplaceEarningsPagePage.js +0 -163
  170. package/dist/crewfoundry/pages/MarketplaceEarningsPagePage.js.map +0 -1
  171. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.d.ts +0 -2
  172. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.d.ts.map +0 -1
  173. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.js +0 -365
  174. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.js.map +0 -1
  175. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.d.ts +0 -2
  176. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.d.ts.map +0 -1
  177. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js +0 -224
  178. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js.map +0 -1
  179. package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.d.ts +0 -2
  180. package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.d.ts.map +0 -1
  181. package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js +0 -180
  182. package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js.map +0 -1
  183. package/dist/crewfoundry/pages/MarketplaceMessagingPagePage.d.ts +0 -2
  184. package/dist/crewfoundry/pages/MarketplaceMessagingPagePage.d.ts.map +0 -1
  185. package/dist/crewfoundry/pages/MarketplaceMessagingPagePage.js +0 -38
  186. package/dist/crewfoundry/pages/MarketplaceMessagingPagePage.js.map +0 -1
  187. package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.d.ts +0 -2
  188. package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.d.ts.map +0 -1
  189. package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.js +0 -204
  190. package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.js.map +0 -1
  191. package/dist/crewfoundry/pages/PaymentsEscrowPagePage.d.ts +0 -2
  192. package/dist/crewfoundry/pages/PaymentsEscrowPagePage.d.ts.map +0 -1
  193. package/dist/crewfoundry/pages/PaymentsEscrowPagePage.js +0 -55
  194. package/dist/crewfoundry/pages/PaymentsEscrowPagePage.js.map +0 -1
  195. package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.d.ts +0 -2
  196. package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.d.ts.map +0 -1
  197. package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js +0 -335
  198. package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js.map +0 -1
  199. package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.d.ts +0 -2
  200. package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.d.ts.map +0 -1
  201. package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.js +0 -160
  202. package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.js.map +0 -1
  203. package/dist/crewfoundry/pages/PaymentsPaymentMethodsPagePage.d.ts +0 -2
  204. package/dist/crewfoundry/pages/PaymentsPaymentMethodsPagePage.d.ts.map +0 -1
  205. package/dist/crewfoundry/pages/PaymentsPaymentMethodsPagePage.js +0 -80
  206. package/dist/crewfoundry/pages/PaymentsPaymentMethodsPagePage.js.map +0 -1
  207. package/dist/crewfoundry/pages/PaymentsSecurityPagePage.d.ts +0 -2
  208. package/dist/crewfoundry/pages/PaymentsSecurityPagePage.d.ts.map +0 -1
  209. package/dist/crewfoundry/pages/PaymentsSecurityPagePage.js +0 -191
  210. package/dist/crewfoundry/pages/PaymentsSecurityPagePage.js.map +0 -1
  211. package/dist/crewfoundry/utils/courseProgress.d.ts +0 -33
  212. package/dist/crewfoundry/utils/courseProgress.d.ts.map +0 -1
  213. package/dist/crewfoundry/utils/courseProgress.js +0 -46
  214. package/dist/crewfoundry/utils/courseProgress.js.map +0 -1
@@ -0,0 +1,91 @@
1
+ //#region src/crewfoundry/lib/marketplace.ts
2
+ var e = {
3
+ requested: {
4
+ confirmed: ["provider"],
5
+ cancelled: ["provider", "client"]
6
+ },
7
+ confirmed: {
8
+ completed: ["provider"],
9
+ cancelled: ["provider", "client"]
10
+ },
11
+ completed: {},
12
+ cancelled: {}
13
+ };
14
+ function t(e, t) {
15
+ if (!t) return [];
16
+ let n = [];
17
+ return e.providerActorId === t && n.push("provider"), e.clientActorId === t && n.push("client"), n;
18
+ }
19
+ function n(n, r) {
20
+ let i = t(n, r), a = e[n.status] ?? {};
21
+ return Object.keys(a).filter((e) => (a[e] ?? []).some((e) => i.includes(e)));
22
+ }
23
+ var r = {
24
+ requested: "Requested",
25
+ confirmed: "Confirm",
26
+ completed: "Mark completed",
27
+ cancelled: "Cancel booking"
28
+ }, i = {
29
+ requested: "Requested",
30
+ confirmed: "Confirmed",
31
+ completed: "Completed",
32
+ cancelled: "Cancelled"
33
+ }, a = {
34
+ pending_hold: "Pending hold",
35
+ held: "Held",
36
+ released: "Released",
37
+ cancelled: "Cancelled"
38
+ }, o = (e) => e === "pending_hold" || e === "held", s = [
39
+ {
40
+ label: "Per hour",
41
+ value: "hour"
42
+ },
43
+ {
44
+ label: "Per day",
45
+ value: "day"
46
+ },
47
+ {
48
+ label: "Per week",
49
+ value: "week"
50
+ },
51
+ {
52
+ label: "Per month",
53
+ value: "month"
54
+ },
55
+ {
56
+ label: "Fixed price",
57
+ value: "fixed"
58
+ }
59
+ ], c = [
60
+ "USD",
61
+ "EUR",
62
+ "GBP",
63
+ "INR"
64
+ ].map((e) => ({
65
+ label: e,
66
+ value: e
67
+ }));
68
+ function l(e, t) {
69
+ try {
70
+ return new Intl.NumberFormat(void 0, {
71
+ style: "currency",
72
+ currency: t,
73
+ maximumFractionDigits: 2
74
+ }).format(e);
75
+ } catch {
76
+ return `${e.toLocaleString()} ${t}`;
77
+ }
78
+ }
79
+ function u(e) {
80
+ let t = l(e.rate, e.currency), n = e.rateUnit.trim().toLowerCase();
81
+ return n === "fixed" || n === "" ? t : `${t} / ${n}`;
82
+ }
83
+ function d(e) {
84
+ let t = /* @__PURE__ */ new Map();
85
+ for (let n of e) t.set(n.currency, (t.get(n.currency) ?? 0) + n.amount);
86
+ return t.size === 0 ? l(0, "USD") : [...t].map(([e, t]) => l(t, e)).join(" · ");
87
+ }
88
+ //#endregion
89
+ export { r as BOOKING_ACTION_LABEL, i as BOOKING_STATUS_LABEL, c as CURRENCY_OPTIONS, a as ESCROW_STATUS_LABEL, s as RATE_UNIT_OPTIONS, n as allowedBookingTransitions, l as formatMoney, u as formatRate, o as isEscrowOpen, d as sumByCurrency };
90
+
91
+ //# sourceMappingURL=marketplace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"marketplace.js","names":[],"sources":["../../../src/crewfoundry/lib/marketplace.ts"],"sourcesContent":["/**\n * Pure helpers for the talent-marketplace pages (BOFF-3401). Mirrors the\n * booking workflow enforced by wspace-crewfoundry-svc's MarketplaceService so\n * the UI only offers transitions the service will accept; the service stays\n * the authority and rejects anything else.\n */\nimport type {\n CrewBooking,\n CrewBookingStatus,\n CrewEscrowStatus,\n CrewServiceListing,\n} from '../store/crewfoundryStore';\n\nexport type BookingRole = 'provider' | 'client';\n\n/** Legal next statuses per current status, and which role may move there. */\nconst BOOKING_TRANSITIONS: Record<\n CrewBookingStatus,\n Partial<Record<CrewBookingStatus, BookingRole[]>>\n> = {\n requested: { confirmed: ['provider'], cancelled: ['provider', 'client'] },\n confirmed: { completed: ['provider'], cancelled: ['provider', 'client'] },\n completed: {},\n cancelled: {},\n};\n\n/** Roles the actor holds on a booking (both when they booked their own service). */\nexport function bookingRoles(booking: CrewBooking, actorId: string | undefined): BookingRole[] {\n if (!actorId) return [];\n const roles: BookingRole[] = [];\n if (booking.providerActorId === actorId) roles.push('provider');\n if (booking.clientActorId === actorId) roles.push('client');\n return roles;\n}\n\n/** Next statuses this actor may move the booking to, in workflow order. */\nexport function allowedBookingTransitions(\n booking: CrewBooking,\n actorId: string | undefined\n): CrewBookingStatus[] {\n const roles = bookingRoles(booking, actorId);\n const next = BOOKING_TRANSITIONS[booking.status] ?? {};\n return (Object.keys(next) as CrewBookingStatus[]).filter((status) =>\n (next[status] ?? []).some((role) => roles.includes(role))\n );\n}\n\nexport const BOOKING_ACTION_LABEL: Record<CrewBookingStatus, string> = {\n requested: 'Requested',\n confirmed: 'Confirm',\n completed: 'Mark completed',\n cancelled: 'Cancel booking',\n};\n\nexport const BOOKING_STATUS_LABEL: Record<CrewBookingStatus, string> = {\n requested: 'Requested',\n confirmed: 'Confirmed',\n completed: 'Completed',\n cancelled: 'Cancelled',\n};\n\nexport const ESCROW_STATUS_LABEL: Record<CrewEscrowStatus, string> = {\n pending_hold: 'Pending hold',\n held: 'Held',\n released: 'Released',\n cancelled: 'Cancelled',\n};\n\n/** Escrow ledger rows that can still be released or cancelled. */\nexport const isEscrowOpen = (status: CrewEscrowStatus): boolean =>\n status === 'pending_hold' || status === 'held';\n\nexport const RATE_UNIT_OPTIONS = [\n { label: 'Per hour', value: 'hour' },\n { label: 'Per day', value: 'day' },\n { label: 'Per week', value: 'week' },\n { label: 'Per month', value: 'month' },\n { label: 'Fixed price', value: 'fixed' },\n];\n\nexport const CURRENCY_OPTIONS = ['USD', 'EUR', 'GBP', 'INR'].map((code) => ({\n label: code,\n value: code,\n}));\n\n/** Formats an amount in its ISO currency, falling back to \"<amount> <code>\". */\nexport function formatMoney(amount: number, currency: string): string {\n try {\n return new Intl.NumberFormat(undefined, {\n style: 'currency',\n currency,\n maximumFractionDigits: 2,\n }).format(amount);\n } catch {\n return `${amount.toLocaleString()} ${currency}`;\n }\n}\n\n/** \"$120 / hour\", or just \"$900\" for a fixed-price listing. */\nexport function formatRate(\n item: Pick<CrewServiceListing, 'rate' | 'rateUnit' | 'currency'>\n): string {\n const money = formatMoney(item.rate, item.currency);\n const unit = item.rateUnit.trim().toLowerCase();\n return unit === 'fixed' || unit === '' ? money : `${money} / ${unit}`;\n}\n\n/** Sum of amounts grouped by currency, e.g. \"$300 · €120\" (never mixes currencies). */\nexport function sumByCurrency(rows: Array<{ amount: number; currency: string }>): string {\n const totals = new Map<string, number>();\n for (const row of rows) {\n totals.set(row.currency, (totals.get(row.currency) ?? 0) + row.amount);\n }\n if (totals.size === 0) return formatMoney(0, 'USD');\n return [...totals].map(([currency, amount]) => formatMoney(amount, currency)).join(' · ');\n}\n"],"mappings":";AAgBA,IAAM,IAGF;CACF,WAAW;EAAE,WAAW,CAAC,WAAW;EAAE,WAAW,CAAC,YAAY,SAAS;EAAE;CACzE,WAAW;EAAE,WAAW,CAAC,WAAW;EAAE,WAAW,CAAC,YAAY,SAAS;EAAE;CACzE,WAAW,EAAE;CACb,WAAW,EAAE;CACd;AAGD,SAAgB,EAAa,GAAsB,GAA4C;AAC7F,KAAI,CAAC,EAAS,QAAO,EAAE;CACvB,IAAM,IAAuB,EAAE;AAG/B,QAFI,EAAQ,oBAAoB,KAAS,EAAM,KAAK,WAAW,EAC3D,EAAQ,kBAAkB,KAAS,EAAM,KAAK,SAAS,EACpD;;AAIT,SAAgB,EACd,GACA,GACqB;CACrB,IAAM,IAAQ,EAAa,GAAS,EAAQ,EACtC,IAAO,EAAoB,EAAQ,WAAW,EAAE;AACtD,QAAQ,OAAO,KAAK,EAAK,CAAyB,QAAQ,OACvD,EAAK,MAAW,EAAE,EAAE,MAAM,MAAS,EAAM,SAAS,EAAK,CAAC,CAC1D;;AAGH,IAAa,IAA0D;CACrE,WAAW;CACX,WAAW;CACX,WAAW;CACX,WAAW;CACZ,EAEY,IAA0D;CACrE,WAAW;CACX,WAAW;CACX,WAAW;CACX,WAAW;CACZ,EAEY,IAAwD;CACnE,cAAc;CACd,MAAM;CACN,UAAU;CACV,WAAW;CACZ,EAGY,KAAgB,MAC3B,MAAW,kBAAkB,MAAW,QAE7B,IAAoB;CAC/B;EAAE,OAAO;EAAY,OAAO;EAAQ;CACpC;EAAE,OAAO;EAAW,OAAO;EAAO;CAClC;EAAE,OAAO;EAAY,OAAO;EAAQ;CACpC;EAAE,OAAO;EAAa,OAAO;EAAS;CACtC;EAAE,OAAO;EAAe,OAAO;EAAS;CACzC,EAEY,IAAmB;CAAC;CAAO;CAAO;CAAO;CAAM,CAAC,KAAK,OAAU;CAC1E,OAAO;CACP,OAAO;CACR,EAAE;AAGH,SAAgB,EAAY,GAAgB,GAA0B;AACpE,KAAI;AACF,SAAO,IAAI,KAAK,aAAa,KAAA,GAAW;GACtC,OAAO;GACP;GACA,uBAAuB;GACxB,CAAC,CAAC,OAAO,EAAO;SACX;AACN,SAAO,GAAG,EAAO,gBAAgB,CAAC,GAAG;;;AAKzC,SAAgB,EACd,GACQ;CACR,IAAM,IAAQ,EAAY,EAAK,MAAM,EAAK,SAAS,EAC7C,IAAO,EAAK,SAAS,MAAM,CAAC,aAAa;AAC/C,QAAO,MAAS,WAAW,MAAS,KAAK,IAAQ,GAAG,EAAM,KAAK;;AAIjE,SAAgB,EAAc,GAA2D;CACvF,IAAM,oBAAS,IAAI,KAAqB;AACxC,MAAK,IAAM,KAAO,EAChB,GAAO,IAAI,EAAI,WAAW,EAAO,IAAI,EAAI,SAAS,IAAI,KAAK,EAAI,OAAO;AAGxE,QADI,EAAO,SAAS,IAAU,EAAY,GAAG,MAAM,GAC5C,CAAC,GAAG,EAAO,CAAC,KAAK,CAAC,GAAU,OAAY,EAAY,GAAQ,EAAS,CAAC,CAAC,KAAK,MAAM"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Run a lazy store fetch once the module has its workspace context
3
+ * (`initialized`) and the slice has not been loaded yet. Returns the load
4
+ * error, if any, so the page can render it instead of a silent empty state.
5
+ */
6
+ export declare function useEnsureLoaded(loaded: boolean, load: () => Promise<void>): string | null;
7
+ //# sourceMappingURL=useEnsureLoaded.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useEnsureLoaded.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/lib/useEnsureLoaded.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,CAoBzF"}
@@ -0,0 +1,25 @@
1
+ import { useCrewFoundryStore as e } from "../store/crewfoundryStore.js";
2
+ import { useEffect as t, useState as n } from "react";
3
+ //#region src/crewfoundry/lib/useEnsureLoaded.ts
4
+ function r(r, i) {
5
+ let a = e((e) => e.initialized), [o, s] = n(null);
6
+ return t(() => {
7
+ if (!a || r) return;
8
+ let e = !1;
9
+ return i().then(() => {
10
+ e || s(null);
11
+ }).catch((t) => {
12
+ e || s(t instanceof Error ? t.message : "Unable to load data");
13
+ }), () => {
14
+ e = !0;
15
+ };
16
+ }, [
17
+ a,
18
+ r,
19
+ i
20
+ ]), o;
21
+ }
22
+ //#endregion
23
+ export { r as useEnsureLoaded };
24
+
25
+ //# sourceMappingURL=useEnsureLoaded.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useEnsureLoaded.js","names":[],"sources":["../../../src/crewfoundry/lib/useEnsureLoaded.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\n/**\n * Run a lazy store fetch once the module has its workspace context\n * (`initialized`) and the slice has not been loaded yet. Returns the load\n * error, if any, so the page can render it instead of a silent empty state.\n */\nexport function useEnsureLoaded(loaded: boolean, load: () => Promise<void>): string | null {\n const initialized = useCrewFoundryStore((state) => state.initialized);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n if (!initialized || loaded) return;\n let cancelled = false;\n load()\n .then(() => {\n if (!cancelled) setError(null);\n })\n .catch((err: unknown) => {\n if (!cancelled) setError(err instanceof Error ? err.message : 'Unable to load data');\n });\n return () => {\n cancelled = true;\n };\n }, [initialized, loaded, load]);\n\n return error;\n}\n"],"mappings":";;;AAQA,SAAgB,EAAgB,GAAiB,GAA0C;CACzF,IAAM,IAAc,GAAqB,MAAU,EAAM,YAAY,EAC/D,CAAC,GAAO,KAAY,EAAwB,KAAK;AAiBvD,QAfA,QAAgB;AACd,MAAI,CAAC,KAAe,EAAQ;EAC5B,IAAI,IAAY;AAQhB,SAPA,GAAM,CACH,WAAW;AACV,GAAK,KAAW,EAAS,KAAK;IAC9B,CACD,OAAO,MAAiB;AACvB,GAAK,KAAW,EAAS,aAAe,QAAQ,EAAI,UAAU,sBAAsB;IACpF,QACS;AACX,OAAY;;IAEb;EAAC;EAAa;EAAQ;EAAK,CAAC,EAExB"}
@@ -1 +1 @@
1
- {"version":3,"file":"AnalyticsFinancialAnalyticsPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.tsx"],"names":[],"mappings":"AA+BA,wBAAgB,mCAAmC,gCAoTlD"}
1
+ {"version":3,"file":"AnalyticsFinancialAnalyticsPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.tsx"],"names":[],"mappings":"AA+BA,wBAAgB,mCAAmC,gCAoSlD"}
@@ -10,7 +10,7 @@ import { Badge as f, Card as p, CardContent as m, CardDescription as h, CardHead
10
10
  import { usePagination as T } from "@burdenoff/fe-libs/shared/hooks";
11
11
  //#region src/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.tsx
12
12
  function E() {
13
- let { employees: E, departments: D, payrollRecords: O, invoices: k, projects: A } = e(), j = i(() => {
13
+ let { employees: E, departments: D, payrollRecords: O, clients: k, projects: A } = e(), j = i(() => {
14
14
  let e = O.reduce((e, t) => {
15
15
  let n = new Date(t.payDate).toLocaleString("en-US", { month: "short" }), r = e[n] || {
16
16
  gross: 0,
@@ -39,22 +39,13 @@ function E() {
39
39
  category: "Project Spend",
40
40
  amount: A.reduce((e, t) => e + t.spent, 0),
41
41
  icon: c
42
- },
43
- {
44
- category: "Collected Invoices",
45
- amount: k.filter((e) => e.status === "paid").reduce((e, t) => e + t.amount, 0),
46
- icon: o
47
42
  }
48
43
  ], t = e.reduce((e, t) => e + t.amount, 0) || 1;
49
44
  return e.map((e) => ({
50
45
  ...e,
51
46
  percentage: Math.round(e.amount / t * 100)
52
47
  }));
53
- }, [
54
- k,
55
- O,
56
- A
57
- ]), N = E.reduce((e, t) => e + t.salary, 0), P = Math.round(N / 12), F = k.reduce((e, t) => e + t.amount, 0), I = k.filter((e) => e.status === "paid").reduce((e, t) => e + t.amount, 0), L = A.reduce((e, t) => e + t.budget, 0), R = A.reduce((e, t) => e + t.spent, 0), z = j.map((e) => ({
48
+ }, [O, A]), N = E.reduce((e, t) => e + t.salary, 0), P = Math.round(N / 12), F = k.reduce((e, t) => e + (t.revenue || 0), 0), I = k.filter((e) => e.status === "active").length, L = A.reduce((e, t) => e + t.budget, 0), R = A.reduce((e, t) => e + t.spent, 0), z = j.map((e) => ({
58
49
  label: e.month,
59
50
  value: e.gross
60
51
  })), B = j.map((e) => e.net), V = M.filter((e) => e.amount > 0).map((e) => ({
@@ -68,7 +59,7 @@ function E() {
68
59
  budget: e.budget,
69
60
  headcount: t.length
70
61
  };
71
- })].sort((e, t) => t.salary - e.salary), W = D.reduce((e, t) => e + t.budget, 0), G = [...k].filter((e) => e.status !== "paid").sort((e, t) => e.dueDate.localeCompare(t.dueDate)), { page: K, pageSize: q, offset: J, setPage: Y } = T({ initialPageSize: 6 }), X = G.slice(J, J + q);
62
+ })].sort((e, t) => t.salary - e.salary), W = D.reduce((e, t) => e + t.budget, 0), G = [...k].filter((e) => (e.revenue || 0) > 0).sort((e, t) => t.revenue - e.revenue), { page: K, pageSize: q, offset: J, setPage: Y } = T({ initialPageSize: 6 }), X = G.slice(J, J + q);
72
63
  return /* @__PURE__ */ d("div", {
73
64
  className: "space-y-6 p-6",
74
65
  children: [
@@ -142,7 +133,7 @@ function E() {
142
133
  className: "flex flex-row items-center justify-between space-y-0 pb-2",
143
134
  children: [/* @__PURE__ */ u(_, {
144
135
  className: "text-sm font-medium",
145
- children: "Total Invoiced"
136
+ children: "Client Revenue"
146
137
  }), /* @__PURE__ */ u(o, { className: "h-4 w-4 text-text-secondary" })]
147
138
  }), /* @__PURE__ */ d(m, { children: [/* @__PURE__ */ d("div", {
148
139
  className: "text-2xl font-bold tabular-nums",
@@ -153,11 +144,7 @@ function E() {
153
144
  ]
154
145
  }), /* @__PURE__ */ d("p", {
155
146
  className: "text-xs text-text-secondary",
156
- children: [
157
- "$",
158
- (I / 1e3).toFixed(1),
159
- "K collected"
160
- ]
147
+ children: [I, " active clients"]
161
148
  })] })]
162
149
  }),
163
150
  /* @__PURE__ */ d(v, {
@@ -204,29 +191,29 @@ function E() {
204
191
  }) : /* @__PURE__ */ u("p", {
205
192
  className: "py-6 text-center text-sm text-text-secondary",
206
193
  children: "No tracked spend yet."
207
- }) })] }), /* @__PURE__ */ d(p, { children: [/* @__PURE__ */ d(g, { children: [/* @__PURE__ */ u(_, { children: "Outstanding Invoices" }), /* @__PURE__ */ u(h, { children: "Unpaid invoices requiring attention" })] }), /* @__PURE__ */ d(m, {
194
+ }) })] }), /* @__PURE__ */ d(p, { children: [/* @__PURE__ */ d(g, { children: [/* @__PURE__ */ u(_, { children: "Revenue by Client" }), /* @__PURE__ */ u(h, { children: "Revenue recorded against each client" })] }), /* @__PURE__ */ d(m, {
208
195
  className: "space-y-3",
209
196
  children: [
210
197
  G.length === 0 && /* @__PURE__ */ u(b, {
211
198
  illustration: "empty-data",
212
- title: "No outstanding invoices",
213
- description: "Every invoice has been paid. New unpaid invoices will appear here."
199
+ title: "No client revenue yet",
200
+ description: "Record revenue on your clients under Company → Client Management."
214
201
  }),
215
202
  X.map((e) => /* @__PURE__ */ d("div", {
216
203
  className: "flex items-center justify-between rounded border p-3",
217
204
  children: [/* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("p", {
218
205
  className: "text-sm font-medium",
219
- children: e.number
206
+ children: e.name
220
207
  }), /* @__PURE__ */ u("p", {
221
208
  className: "text-xs text-text-secondary",
222
- children: e.clientName
209
+ children: e.industry
223
210
  })] }), /* @__PURE__ */ d("div", {
224
211
  className: "text-right",
225
212
  children: [/* @__PURE__ */ d("p", {
226
213
  className: "text-sm font-medium",
227
- children: ["$", e.amount.toLocaleString()]
214
+ children: ["$", e.revenue.toLocaleString()]
228
215
  }), /* @__PURE__ */ u(f, {
229
- variant: e.status === "overdue" ? "destructive" : "secondary",
216
+ variant: "secondary",
230
217
  className: "text-[10px]",
231
218
  children: t(e.status)
232
219
  })]
@@ -1 +1 @@
1
- {"version":3,"file":"AnalyticsFinancialAnalyticsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n Pagination,\n ResponsiveTable,\n type ResponsiveTableColumn,\n Separator,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { DollarSign, FileText, Receipt, TrendingUp, Wallet } from 'lucide-react';\nimport { useMemo } from 'react';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\nimport { DonutChart, LineChart } from '../components/viz';\nimport type { LabeledValue, Segment } from '../components/viz';\n\ntype DeptCostRow = {\n name: string;\n salary: number;\n budget: number;\n headcount: number;\n};\n\nexport function AnalyticsFinancialAnalyticsPagePage() {\n const { employees, departments, payrollRecords, invoices, projects } = useCrewFoundryStore();\n\n const monthlyPayrollTrend = useMemo(() => {\n const byPeriod = payrollRecords.reduce<\n Record<string, { gross: number; net: number; deductions: number }>\n >((acc, record) => {\n const month = new Date(record.payDate).toLocaleString('en-US', { month: 'short' });\n const current = acc[month] || { gross: 0, net: 0, deductions: 0 };\n current.gross += record.grossPay;\n current.net += record.netPay;\n current.deductions += record.deductions;\n acc[month] = current;\n return acc;\n }, {});\n return Object.entries(byPeriod)\n .map(([month, value]) => ({ month, ...value }))\n .slice(-6);\n }, [payrollRecords]);\n\n // Real spend lines only (no invented budget coefficients).\n const spendByCategory = useMemo(() => {\n const items = [\n {\n category: 'Gross Payroll',\n amount: payrollRecords.reduce((sum, record) => sum + record.grossPay, 0),\n icon: Wallet,\n },\n {\n category: 'Deductions',\n amount: payrollRecords.reduce((sum, record) => sum + record.deductions, 0),\n icon: Receipt,\n },\n {\n category: 'Project Spend',\n amount: projects.reduce((sum, project) => sum + project.spent, 0),\n icon: TrendingUp,\n },\n {\n category: 'Collected Invoices',\n amount: invoices\n .filter((invoice) => invoice.status === 'paid')\n .reduce((sum, invoice) => sum + invoice.amount, 0),\n icon: FileText,\n },\n ];\n const total = items.reduce((sum, item) => sum + item.amount, 0) || 1;\n return items.map((item) => ({ ...item, percentage: Math.round((item.amount / total) * 100) }));\n }, [invoices, payrollRecords, projects]);\n\n const totalPayroll = employees.reduce((sum, e) => sum + e.salary, 0);\n const monthlyPayroll = Math.round(totalPayroll / 12);\n const totalInvoiced = invoices.reduce((sum, inv) => sum + inv.amount, 0);\n const paidInvoices = invoices\n .filter((i) => i.status === 'paid')\n .reduce((s, i) => s + i.amount, 0);\n const totalProjectBudget = projects.reduce((s, p) => s + p.budget, 0);\n const totalProjectSpent = projects.reduce((s, p) => s + p.spent, 0);\n\n const grossSeries: LabeledValue[] = monthlyPayrollTrend.map((m) => ({\n label: m.month,\n value: m.gross,\n }));\n const netCompare = monthlyPayrollTrend.map((m) => m.net);\n const spendSegments: Segment[] = spendByCategory\n .filter((category) => category.amount > 0)\n .map((category) => ({ label: category.category, value: category.amount }));\n const projectUtilization =\n totalProjectBudget > 0 ? Math.round((totalProjectSpent / totalProjectBudget) * 100) : 0;\n\n const deptCost: DeptCostRow[] = departments.map((d) => {\n const deptEmps = employees.filter((e) => e.department === d.name);\n const totalSal = deptEmps.reduce((s, e) => s + e.salary, 0);\n return { name: d.name, salary: totalSal, budget: d.budget, headcount: deptEmps.length };\n });\n const sortedDeptCost = [...deptCost].sort((a, b) => b.salary - a.salary);\n const totalDeptBudget = departments.reduce((s, d) => s + d.budget, 0);\n\n // Outstanding invoices = anything not fully paid, matching the card title.\n const outstandingInvoices = [...invoices]\n .filter((invoice) => invoice.status !== 'paid')\n .sort((a, b) => a.dueDate.localeCompare(b.dueDate));\n const {\n page: invoicePage,\n pageSize: invoicePageSize,\n offset: invoiceOffset,\n setPage: setInvoicePage,\n } = usePagination({ initialPageSize: 6 });\n const invoicePageItems = outstandingInvoices.slice(\n invoiceOffset,\n invoiceOffset + invoicePageSize\n );\n\n const deptCostColumns: ResponsiveTableColumn<DeptCostRow>[] = [\n {\n key: 'name',\n header: 'Department',\n priority: 'primary',\n cell: (d) => <span className=\"font-medium\">{d.name}</span>,\n },\n {\n key: 'headcount',\n header: 'Headcount',\n priority: 'secondary',\n cell: (d) => <span className=\"md:block md:text-right\">{d.headcount}</span>,\n },\n {\n key: 'salary',\n header: 'Total Salary',\n priority: 'secondary',\n cell: (d) => <span className=\"md:block md:text-right\">${(d.salary / 1000).toFixed(0)}K</span>,\n },\n {\n key: 'budget',\n header: 'Dept Budget',\n priority: 'secondary',\n cell: (d) => (\n <span className=\"md:block md:text-right\">${(d.budget / 1000000).toFixed(1)}M</span>\n ),\n },\n {\n key: 'ratio',\n header: 'Salary/Budget %',\n priority: 'primary',\n cell: (d) => {\n const ratio = d.budget > 0 ? Math.round((d.salary / d.budget) * 100) : 0;\n return (\n <span className=\"md:block md:text-right\">\n <Badge variant={ratio > 50 ? 'destructive' : 'default'} className=\"text-[10px]\">\n {ratio}%\n </Badge>\n </span>\n );\n },\n },\n ];\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Financial Analytics</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n Workforce financial metrics and cost analysis\n </p>\n <PagePurpose className=\"mt-1\">\n Understand the money side of your workforce — payroll, project budgets, and spend trends\n — so you can plan headcount and control costs with confidence.\n </PagePurpose>\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Monthly Payroll</CardTitle>\n <DollarSign className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${(monthlyPayroll / 1000).toFixed(0)}K\n </div>\n <p className=\"text-xs text-text-secondary\">Annual salary ÷ 12 months</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\">Annual Salary Cost</CardTitle>\n <Wallet className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${(totalPayroll / 1000000).toFixed(1)}M\n </div>\n <p className=\"text-xs text-text-secondary\">{employees.length} employees</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\">Total Invoiced</CardTitle>\n <FileText className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${(totalInvoiced / 1000).toFixed(1)}K\n </div>\n <p className=\"text-xs text-text-secondary\">\n ${(paidInvoices / 1000).toFixed(1)}K collected\n </p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Project Budget</CardTitle>\n <TrendingUp className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${(totalProjectBudget / 1000000).toFixed(1)}M\n </div>\n <p className=\"text-xs text-text-secondary\">{projectUtilization}% utilized</p>\n </CardContent>\n </GlassCard>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Payroll Trend</CardTitle>\n <CardDescription>Monthly gross payroll over the last 6 months</CardDescription>\n </CardHeader>\n <CardContent>\n {grossSeries.length > 0 ? (\n <LineChart\n series={grossSeries}\n compare={netCompare}\n seriesLabel=\"Gross pay\"\n compareLabel=\"Net pay\"\n height={220}\n valueFormatter={(value) => `$${(value / 1000).toFixed(0)}K`}\n />\n ) : (\n <p className=\"py-6 text-center text-sm text-text-secondary\">No payroll records yet.</p>\n )}\n </CardContent>\n </Card>\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle>Spend by Category</CardTitle>\n <CardDescription>Share of tracked workforce spend</CardDescription>\n </CardHeader>\n <CardContent>\n {spendSegments.length > 0 ? (\n <DonutChart\n segments={spendSegments}\n centerLabel={`$${(spendSegments.reduce((sum, s) => sum + s.value, 0) / 1000).toFixed(0)}K`}\n centerCaption=\"Total spend\"\n valueFormatter={(value) => `$${(value / 1000).toFixed(0)}K`}\n />\n ) : (\n <p className=\"py-6 text-center text-sm text-text-secondary\">No tracked spend yet.</p>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Outstanding Invoices</CardTitle>\n <CardDescription>Unpaid invoices requiring attention</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {outstandingInvoices.length === 0 && (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No outstanding invoices\"\n description=\"Every invoice has been paid. New unpaid invoices will appear here.\"\n />\n )}\n {invoicePageItems.map((inv) => (\n <div key={inv.id} className=\"flex items-center justify-between rounded border p-3\">\n <div>\n <p className=\"text-sm font-medium\">{inv.number}</p>\n <p className=\"text-xs text-text-secondary\">{inv.clientName}</p>\n </div>\n <div className=\"text-right\">\n <p className=\"text-sm font-medium\">${inv.amount.toLocaleString()}</p>\n <Badge\n variant={inv.status === 'overdue' ? 'destructive' : 'secondary'}\n className=\"text-[10px]\"\n >\n {formatEnumLabel(inv.status)}\n </Badge>\n </div>\n </div>\n ))}\n {outstandingInvoices.length > invoicePageSize && (\n <Pagination\n page={invoicePage}\n pageSize={invoicePageSize}\n totalItems={outstandingInvoices.length}\n onPageChange={setInvoicePage}\n />\n )}\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Department Cost Analysis</CardTitle>\n <CardDescription>Total salary cost and budget allocation by department</CardDescription>\n </CardHeader>\n <CardContent>\n <ResponsiveTable columns={deptCostColumns} data={sortedDeptCost} rowKey={(d) => d.name} />\n <Separator className=\"my-3\" />\n <div className=\"flex flex-wrap items-center justify-between gap-2 text-sm font-bold\">\n <span>Total</span>\n <span>{employees.length} employees</span>\n <span>${(totalPayroll / 1000000).toFixed(1)}M salary</span>\n <span>${(totalDeptBudget / 1000000).toFixed(1)}M budget</span>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AnalyticsFinancialAnalyticsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n Pagination,\n ResponsiveTable,\n type ResponsiveTableColumn,\n Separator,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { DollarSign, FileText, Receipt, TrendingUp, Wallet } from 'lucide-react';\nimport { useMemo } from 'react';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\nimport { DonutChart, LineChart } from '../components/viz';\nimport type { LabeledValue, Segment } from '../components/viz';\n\ntype DeptCostRow = {\n name: string;\n salary: number;\n budget: number;\n headcount: number;\n};\n\nexport function AnalyticsFinancialAnalyticsPagePage() {\n const { employees, departments, payrollRecords, clients, projects } = useCrewFoundryStore();\n\n const monthlyPayrollTrend = useMemo(() => {\n const byPeriod = payrollRecords.reduce<\n Record<string, { gross: number; net: number; deductions: number }>\n >((acc, record) => {\n const month = new Date(record.payDate).toLocaleString('en-US', { month: 'short' });\n const current = acc[month] || { gross: 0, net: 0, deductions: 0 };\n current.gross += record.grossPay;\n current.net += record.netPay;\n current.deductions += record.deductions;\n acc[month] = current;\n return acc;\n }, {});\n return Object.entries(byPeriod)\n .map(([month, value]) => ({ month, ...value }))\n .slice(-6);\n }, [payrollRecords]);\n\n // Real spend lines only (no invented budget coefficients).\n const spendByCategory = useMemo(() => {\n const items = [\n {\n category: 'Gross Payroll',\n amount: payrollRecords.reduce((sum, record) => sum + record.grossPay, 0),\n icon: Wallet,\n },\n {\n category: 'Deductions',\n amount: payrollRecords.reduce((sum, record) => sum + record.deductions, 0),\n icon: Receipt,\n },\n {\n category: 'Project Spend',\n amount: projects.reduce((sum, project) => sum + project.spent, 0),\n icon: TrendingUp,\n },\n ];\n const total = items.reduce((sum, item) => sum + item.amount, 0) || 1;\n return items.map((item) => ({ ...item, percentage: Math.round((item.amount / total) * 100) }));\n }, [payrollRecords, projects]);\n\n const totalPayroll = employees.reduce((sum, e) => sum + e.salary, 0);\n const monthlyPayroll = Math.round(totalPayroll / 12);\n // Client revenue is recorded on each CrewClient (there is no invoicing in\n // CrewFoundry; billing for the workspace itself lives in the Billing app).\n const totalClientRevenue = clients.reduce((sum, client) => sum + (client.revenue || 0), 0);\n const activeClientCount = clients.filter((client) => client.status === 'active').length;\n const totalProjectBudget = projects.reduce((s, p) => s + p.budget, 0);\n const totalProjectSpent = projects.reduce((s, p) => s + p.spent, 0);\n\n const grossSeries: LabeledValue[] = monthlyPayrollTrend.map((m) => ({\n label: m.month,\n value: m.gross,\n }));\n const netCompare = monthlyPayrollTrend.map((m) => m.net);\n const spendSegments: Segment[] = spendByCategory\n .filter((category) => category.amount > 0)\n .map((category) => ({ label: category.category, value: category.amount }));\n const projectUtilization =\n totalProjectBudget > 0 ? Math.round((totalProjectSpent / totalProjectBudget) * 100) : 0;\n\n const deptCost: DeptCostRow[] = departments.map((d) => {\n const deptEmps = employees.filter((e) => e.department === d.name);\n const totalSal = deptEmps.reduce((s, e) => s + e.salary, 0);\n return { name: d.name, salary: totalSal, budget: d.budget, headcount: deptEmps.length };\n });\n const sortedDeptCost = [...deptCost].sort((a, b) => b.salary - a.salary);\n const totalDeptBudget = departments.reduce((s, d) => s + d.budget, 0);\n\n const clientsByRevenue = [...clients]\n .filter((client) => (client.revenue || 0) > 0)\n .sort((a, b) => b.revenue - a.revenue);\n const {\n page: clientPage,\n pageSize: clientPageSize,\n offset: clientOffset,\n setPage: setClientPage,\n } = usePagination({ initialPageSize: 6 });\n const clientPageItems = clientsByRevenue.slice(clientOffset, clientOffset + clientPageSize);\n\n const deptCostColumns: ResponsiveTableColumn<DeptCostRow>[] = [\n {\n key: 'name',\n header: 'Department',\n priority: 'primary',\n cell: (d) => <span className=\"font-medium\">{d.name}</span>,\n },\n {\n key: 'headcount',\n header: 'Headcount',\n priority: 'secondary',\n cell: (d) => <span className=\"md:block md:text-right\">{d.headcount}</span>,\n },\n {\n key: 'salary',\n header: 'Total Salary',\n priority: 'secondary',\n cell: (d) => <span className=\"md:block md:text-right\">${(d.salary / 1000).toFixed(0)}K</span>,\n },\n {\n key: 'budget',\n header: 'Dept Budget',\n priority: 'secondary',\n cell: (d) => (\n <span className=\"md:block md:text-right\">${(d.budget / 1000000).toFixed(1)}M</span>\n ),\n },\n {\n key: 'ratio',\n header: 'Salary/Budget %',\n priority: 'primary',\n cell: (d) => {\n const ratio = d.budget > 0 ? Math.round((d.salary / d.budget) * 100) : 0;\n return (\n <span className=\"md:block md:text-right\">\n <Badge variant={ratio > 50 ? 'destructive' : 'default'} className=\"text-[10px]\">\n {ratio}%\n </Badge>\n </span>\n );\n },\n },\n ];\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Financial Analytics</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n Workforce financial metrics and cost analysis\n </p>\n <PagePurpose className=\"mt-1\">\n Understand the money side of your workforce — payroll, project budgets, and spend trends\n — so you can plan headcount and control costs with confidence.\n </PagePurpose>\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Monthly Payroll</CardTitle>\n <DollarSign className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${(monthlyPayroll / 1000).toFixed(0)}K\n </div>\n <p className=\"text-xs text-text-secondary\">Annual salary ÷ 12 months</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\">Annual Salary Cost</CardTitle>\n <Wallet className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${(totalPayroll / 1000000).toFixed(1)}M\n </div>\n <p className=\"text-xs text-text-secondary\">{employees.length} employees</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\">Client Revenue</CardTitle>\n <FileText className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${(totalClientRevenue / 1000).toFixed(1)}K\n </div>\n <p className=\"text-xs text-text-secondary\">{activeClientCount} active clients</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\">Project Budget</CardTitle>\n <TrendingUp className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${(totalProjectBudget / 1000000).toFixed(1)}M\n </div>\n <p className=\"text-xs text-text-secondary\">{projectUtilization}% utilized</p>\n </CardContent>\n </GlassCard>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Payroll Trend</CardTitle>\n <CardDescription>Monthly gross payroll over the last 6 months</CardDescription>\n </CardHeader>\n <CardContent>\n {grossSeries.length > 0 ? (\n <LineChart\n series={grossSeries}\n compare={netCompare}\n seriesLabel=\"Gross pay\"\n compareLabel=\"Net pay\"\n height={220}\n valueFormatter={(value) => `$${(value / 1000).toFixed(0)}K`}\n />\n ) : (\n <p className=\"py-6 text-center text-sm text-text-secondary\">No payroll records yet.</p>\n )}\n </CardContent>\n </Card>\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle>Spend by Category</CardTitle>\n <CardDescription>Share of tracked workforce spend</CardDescription>\n </CardHeader>\n <CardContent>\n {spendSegments.length > 0 ? (\n <DonutChart\n segments={spendSegments}\n centerLabel={`$${(spendSegments.reduce((sum, s) => sum + s.value, 0) / 1000).toFixed(0)}K`}\n centerCaption=\"Total spend\"\n valueFormatter={(value) => `$${(value / 1000).toFixed(0)}K`}\n />\n ) : (\n <p className=\"py-6 text-center text-sm text-text-secondary\">No tracked spend yet.</p>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Revenue by Client</CardTitle>\n <CardDescription>Revenue recorded against each client</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {clientsByRevenue.length === 0 && (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No client revenue yet\"\n description=\"Record revenue on your clients under Company → Client Management.\"\n />\n )}\n {clientPageItems.map((client) => (\n <div key={client.id} className=\"flex items-center justify-between rounded border p-3\">\n <div>\n <p className=\"text-sm font-medium\">{client.name}</p>\n <p className=\"text-xs text-text-secondary\">{client.industry}</p>\n </div>\n <div className=\"text-right\">\n <p className=\"text-sm font-medium\">${client.revenue.toLocaleString()}</p>\n <Badge variant=\"secondary\" className=\"text-[10px]\">\n {formatEnumLabel(client.status)}\n </Badge>\n </div>\n </div>\n ))}\n {clientsByRevenue.length > clientPageSize && (\n <Pagination\n page={clientPage}\n pageSize={clientPageSize}\n totalItems={clientsByRevenue.length}\n onPageChange={setClientPage}\n />\n )}\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Department Cost Analysis</CardTitle>\n <CardDescription>Total salary cost and budget allocation by department</CardDescription>\n </CardHeader>\n <CardContent>\n <ResponsiveTable columns={deptCostColumns} data={sortedDeptCost} rowKey={(d) => d.name} />\n <Separator className=\"my-3\" />\n <div className=\"flex flex-wrap items-center justify-between gap-2 text-sm font-bold\">\n <span>Total</span>\n <span>{employees.length} employees</span>\n <span>${(totalPayroll / 1000000).toFixed(1)}M salary</span>\n <span>${(totalDeptBudget / 1000000).toFixed(1)}M budget</span>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"AnalyticsHiringAnalyticsPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.tsx"],"names":[],"mappings":"AAsCA,wBAAgB,gCAAgC,gCAkY/C"}
1
+ {"version":3,"file":"AnalyticsHiringAnalyticsPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.tsx"],"names":[],"mappings":"AAuDA,wBAAgB,gCAAgC,gCAkY/C"}
@@ -13,20 +13,26 @@ var T = (e) => {
13
13
  let t = new Date(e);
14
14
  return `${t.getFullYear()}-${String(t.getMonth()).padStart(2, "0")}`;
15
15
  }, E = (/* @__PURE__ */ new Date("2026-03-01")).getTime();
16
- function D() {
17
- let { jobPostings: D, candidates: O, interviews: k } = e(), A = i(() => O.length === 0 && k.length === 0 ? [] : Array.from(new Set([...O.map((e) => T(e.appliedDate)), ...k.map((e) => T(e.date))])).sort().slice(-6).map((e) => {
16
+ function D(e) {
17
+ return e === 0 ? "$0" : e < 1e3 ? `$${e.toLocaleString()}` : `$${Math.round(e / 1e3)}k`;
18
+ }
19
+ function O(e, t) {
20
+ return `${D(e)} – ${D(t)}`;
21
+ }
22
+ function k() {
23
+ let { jobPostings: D, candidates: k, interviews: A } = e(), j = i(() => k.length === 0 && A.length === 0 ? [] : Array.from(new Set([...k.map((e) => T(e.appliedDate)), ...A.map((e) => T(e.date))])).sort().slice(-6).map((e) => {
18
24
  let [t, n] = e.split("-").map(Number), r = new Date(t, n, 1).toLocaleString("en-US", {
19
25
  month: "short",
20
26
  year: "2-digit"
21
- }), i = O.filter((t) => T(t.appliedDate) === e);
27
+ }), i = k.filter((t) => T(t.appliedDate) === e);
22
28
  return {
23
29
  month: r,
24
30
  applications: i.length,
25
- interviews: k.filter((t) => T(t.date) === e).length,
31
+ interviews: A.filter((t) => T(t.date) === e).length,
26
32
  hires: i.filter((e) => e.status === "hired").length
27
33
  };
28
- }), [O, k]), j = i(() => {
29
- let e = O.reduce((e, t) => {
34
+ }), [k, A]), M = i(() => {
35
+ let e = k.reduce((e, t) => {
30
36
  let n = t.source || "Unknown", r = e[n] || {
31
37
  applications: 0,
32
38
  hires: 0
@@ -39,7 +45,7 @@ function D() {
39
45
  hires: t.hires,
40
46
  conversionRate: Number((t.hires / Math.max(t.applications, 1) * 100).toFixed(1))
41
47
  })).sort((e, t) => t.conversionRate - e.conversionRate);
42
- }, [O]), M = i(() => {
48
+ }, [k]), N = i(() => {
43
49
  let e = D.filter((e) => e.status === "active");
44
50
  if (e.length === 0) return 0;
45
51
  let t = e.reduce((e, t) => {
@@ -47,54 +53,54 @@ function D() {
47
53
  return Number.isNaN(n) ? e : e + Math.max(0, (E - n) / (1e3 * 60 * 60 * 24));
48
54
  }, 0);
49
55
  return Math.round(t / e.length);
50
- }, [D]), N = D.filter((e) => e.status === "active"), P = D.reduce((e, t) => e + t.applicants, 0), F = k.filter((e) => e.status === "scheduled").length, I = k.filter((e) => e.status === "completed").length, L = O.filter((e) => e.status === "offer").length, R = O.filter((e) => e.status === "hired").length, z = O.filter((e) => [
56
+ }, [D]), P = D.filter((e) => e.status === "active"), F = D.reduce((e, t) => e + t.applicants, 0), I = A.filter((e) => e.status === "scheduled").length, L = A.filter((e) => e.status === "completed").length, R = k.filter((e) => e.status === "offer").length, z = k.filter((e) => e.status === "hired").length, B = k.filter((e) => [
51
57
  "interview",
52
58
  "assessment",
53
59
  "offer",
54
60
  "hired"
55
- ].includes(e.status)).length, B = O.filter((e) => ["offer", "hired"].includes(e.status)).length, V = Math.round(B / Math.max(z, 1) * 100), H = Math.round(R / Math.max(L + R, 1) * 100), U = A.map((e) => ({
61
+ ].includes(e.status)).length, V = k.filter((e) => ["offer", "hired"].includes(e.status)).length, H = Math.round(V / Math.max(B, 1) * 100), U = Math.round(z / Math.max(R + z, 1) * 100), W = j.map((e) => ({
56
62
  label: e.month,
57
63
  value: e.applications
58
- })), W = A.map((e) => e.hires), G = [
64
+ })), G = j.map((e) => e.hires), K = [
59
65
  {
60
66
  status: "New",
61
- count: O.filter((e) => e.status === "new").length,
67
+ count: k.filter((e) => e.status === "new").length,
62
68
  color: "bg-text-secondary"
63
69
  },
64
70
  {
65
71
  status: "Screening",
66
- count: O.filter((e) => e.status === "screening").length,
72
+ count: k.filter((e) => e.status === "screening").length,
67
73
  color: "bg-status-info-text"
68
74
  },
69
75
  {
70
76
  status: "Interview",
71
- count: O.filter((e) => e.status === "interview").length,
77
+ count: k.filter((e) => e.status === "interview").length,
72
78
  color: "bg-status-info-text"
73
79
  },
74
80
  {
75
81
  status: "Assessment",
76
- count: O.filter((e) => e.status === "assessment").length,
82
+ count: k.filter((e) => e.status === "assessment").length,
77
83
  color: "bg-action-primary-bg"
78
84
  },
79
85
  {
80
86
  status: "Offer",
81
- count: L,
87
+ count: R,
82
88
  color: "bg-status-warning-text"
83
89
  },
84
90
  {
85
91
  status: "Hired",
86
- count: R,
92
+ count: z,
87
93
  color: "bg-status-success-text"
88
94
  },
89
95
  {
90
96
  status: "Rejected",
91
- count: O.filter((e) => e.status === "rejected").length,
97
+ count: k.filter((e) => e.status === "rejected").length,
92
98
  color: "bg-status-error-text"
93
99
  }
94
- ], K = G.reduce((e, t) => e + t.count, 0), q = G.map((e) => ({
100
+ ], q = K.reduce((e, t) => e + t.count, 0), J = K.map((e) => ({
95
101
  label: e.status,
96
102
  value: e.count
97
- })), J = [...N].sort((e, t) => t.applicants - e.applicants), { page: Y, pageSize: X, offset: Z, setPage: Q } = w({ initialPageSize: 8 }), $ = J.slice(Z, Z + X);
103
+ })), Y = [...P].sort((e, t) => t.applicants - e.applicants), { page: X, pageSize: Z, offset: Q, setPage: $ } = w({ initialPageSize: 8 }), ee = Y.slice(Q, Q + Z);
98
104
  return /* @__PURE__ */ d("div", {
99
105
  className: "space-y-6 p-6",
100
106
  children: [
@@ -130,10 +136,10 @@ function D() {
130
136
  }), /* @__PURE__ */ u(a, { className: "h-4 w-4 text-text-secondary" })]
131
137
  }), /* @__PURE__ */ d(m, { children: [/* @__PURE__ */ u("div", {
132
138
  className: "text-2xl font-bold tabular-nums",
133
- children: N.length
139
+ children: P.length
134
140
  }), /* @__PURE__ */ d("p", {
135
141
  className: "text-xs text-text-secondary",
136
- children: [P, " total applicants"]
142
+ children: [F, " total applicants"]
137
143
  })] })]
138
144
  }),
139
145
  /* @__PURE__ */ d(v, {
@@ -147,7 +153,7 @@ function D() {
147
153
  }), /* @__PURE__ */ u(o, { className: "h-4 w-4 text-text-secondary" })]
148
154
  }), /* @__PURE__ */ d(m, { children: [/* @__PURE__ */ d("div", {
149
155
  className: "text-2xl font-bold tabular-nums",
150
- children: [M, " days"]
156
+ children: [N, " days"]
151
157
  }), /* @__PURE__ */ u("p", {
152
158
  className: "text-xs text-text-secondary",
153
159
  children: "Across active postings"
@@ -164,13 +170,13 @@ function D() {
164
170
  }), /* @__PURE__ */ u(s, { className: "h-4 w-4 text-text-secondary" })]
165
171
  }), /* @__PURE__ */ d(m, { children: [/* @__PURE__ */ d("div", {
166
172
  className: "text-2xl font-bold tabular-nums",
167
- children: [V, "%"]
173
+ children: [H, "%"]
168
174
  }), /* @__PURE__ */ d("p", {
169
175
  className: "text-xs text-text-secondary",
170
176
  children: [
171
- F,
172
- " scheduled, ",
173
177
  I,
178
+ " scheduled, ",
179
+ L,
174
180
  " completed"
175
181
  ]
176
182
  })] })]
@@ -186,10 +192,10 @@ function D() {
186
192
  }), /* @__PURE__ */ u(c, { className: "h-4 w-4 text-text-secondary" })]
187
193
  }), /* @__PURE__ */ d(m, { children: [/* @__PURE__ */ d("div", {
188
194
  className: "text-2xl font-bold tabular-nums",
189
- children: [H, "%"]
195
+ children: [U, "%"]
190
196
  }), /* @__PURE__ */ d("p", {
191
197
  className: "text-xs text-text-secondary",
192
- children: [R, " hired to date"]
198
+ children: [z, " hired to date"]
193
199
  })] })]
194
200
  })
195
201
  ]
@@ -198,12 +204,12 @@ function D() {
198
204
  className: "grid gap-4 md:grid-cols-7",
199
205
  children: [/* @__PURE__ */ d(p, {
200
206
  className: "col-span-4",
201
- children: [/* @__PURE__ */ d(g, { children: [/* @__PURE__ */ u(_, { children: "Monthly Hiring Activity" }), /* @__PURE__ */ u(h, { children: "Applications, interviews, and hires over time" })] }), /* @__PURE__ */ u(m, { children: U.length === 0 ? /* @__PURE__ */ u("p", {
207
+ children: [/* @__PURE__ */ d(g, { children: [/* @__PURE__ */ u(_, { children: "Monthly Hiring Activity" }), /* @__PURE__ */ u(h, { children: "Applications, interviews, and hires over time" })] }), /* @__PURE__ */ u(m, { children: W.length === 0 ? /* @__PURE__ */ u("p", {
202
208
  className: "py-6 text-center text-sm text-text-secondary",
203
209
  children: "No hiring activity recorded yet."
204
210
  }) : /* @__PURE__ */ u(n, {
205
- series: U,
206
- compare: W,
211
+ series: W,
212
+ compare: G,
207
213
  seriesLabel: "Applications",
208
214
  compareLabel: "Hires",
209
215
  height: 220,
@@ -211,8 +217,8 @@ function D() {
211
217
  }) })]
212
218
  }), /* @__PURE__ */ d(p, {
213
219
  className: "col-span-3",
214
- children: [/* @__PURE__ */ d(g, { children: [/* @__PURE__ */ u(_, { children: "Pipeline Status" }), /* @__PURE__ */ u(h, { children: "Current candidate distribution" })] }), /* @__PURE__ */ u(m, { children: K > 0 ? /* @__PURE__ */ u(r, {
215
- series: q,
220
+ children: [/* @__PURE__ */ d(g, { children: [/* @__PURE__ */ u(_, { children: "Pipeline Status" }), /* @__PURE__ */ u(h, { children: "Current candidate distribution" })] }), /* @__PURE__ */ u(m, { children: q > 0 ? /* @__PURE__ */ u(r, {
221
+ series: J,
216
222
  colorful: !0,
217
223
  height: 220,
218
224
  title: "Candidate pipeline by stage"
@@ -265,14 +271,14 @@ function D() {
265
271
  })
266
272
  }
267
273
  ],
268
- data: j,
274
+ data: M,
269
275
  rowKey: (e) => e.source,
270
276
  emptyState: /* @__PURE__ */ u("p", {
271
277
  className: "py-6 text-center text-sm text-text-secondary",
272
278
  children: "No candidate source data yet."
273
279
  })
274
280
  }) })] }),
275
- /* @__PURE__ */ d(p, { children: [/* @__PURE__ */ d(g, { children: [/* @__PURE__ */ u(_, { children: "Open Positions Detail" }), /* @__PURE__ */ u(h, { children: "Active job postings and applicant counts" })] }), /* @__PURE__ */ u(m, { children: J.length === 0 ? /* @__PURE__ */ u(b, {
281
+ /* @__PURE__ */ d(p, { children: [/* @__PURE__ */ d(g, { children: [/* @__PURE__ */ u(_, { children: "Open Positions Detail" }), /* @__PURE__ */ u(h, { children: "Active job postings and applicant counts" })] }), /* @__PURE__ */ u(m, { children: Y.length === 0 ? /* @__PURE__ */ u(b, {
276
282
  illustration: "empty-data",
277
283
  title: "No open positions",
278
284
  description: "Active job postings will appear here once you publish a role."
@@ -319,15 +325,9 @@ function D() {
319
325
  key: "salary",
320
326
  header: "Salary Range",
321
327
  priority: "secondary",
322
- cell: (e) => /* @__PURE__ */ d("span", {
328
+ cell: (e) => /* @__PURE__ */ u("span", {
323
329
  className: "md:block md:text-right text-text-secondary",
324
- children: [
325
- "$",
326
- (e.salaryMin / 1e3).toFixed(0),
327
- "K-$",
328
- (e.salaryMax / 1e3).toFixed(0),
329
- "K"
330
- ]
330
+ children: O(e.salaryMin, e.salaryMax)
331
331
  })
332
332
  },
333
333
  {
@@ -341,19 +341,19 @@ function D() {
341
341
  })
342
342
  }
343
343
  ],
344
- data: $,
344
+ data: ee,
345
345
  rowKey: (e) => e.id
346
- }), J.length > X && /* @__PURE__ */ u(S, {
347
- page: Y,
348
- pageSize: X,
349
- totalItems: J.length,
350
- onPageChange: Q,
346
+ }), Y.length > Z && /* @__PURE__ */ u(S, {
347
+ page: X,
348
+ pageSize: Z,
349
+ totalItems: Y.length,
350
+ onPageChange: $,
351
351
  className: "mt-3"
352
352
  })] }) })] })
353
353
  ]
354
354
  });
355
355
  }
356
356
  //#endregion
357
- export { D as AnalyticsHiringAnalyticsPagePage };
357
+ export { k as AnalyticsHiringAnalyticsPagePage };
358
358
 
359
359
  //# sourceMappingURL=AnalyticsHiringAnalyticsPagePage.js.map