@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
@@ -1 +1 @@
1
- {"version":3,"file":"HiringJobDetailsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/HiringJobDetailsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n Spinner,\n} from '@burdenoff/fe-libs/ui';\nimport { useState } from 'react';\nimport {\n Briefcase,\n Calendar,\n CheckCircle,\n MapPin,\n Pencil,\n Share2,\n Sparkles,\n Trash2,\n Users,\n XCircle,\n} from 'lucide-react';\nimport { useNavigate, useSearchParams } from 'react-router';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useCrewFoundryStore, type Candidate, type JobPosting } from '../store/crewfoundryStore';\nimport { FormModal, type FieldConfig, type FormValues } from '../components/forms/FormModal';\nimport { ConfirmDialog } from '../components/forms/ConfirmDialog';\nimport { getNumber, getTags, getText } from '../components/forms/formValues';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\nimport {\n HBarFunnel,\n RatingStars,\n SkillBar,\n STATUS,\n getInitials,\n type LabeledValue,\n} from '../components/viz';\n\nconst JOB_FIELDS: FieldConfig[] = [\n { name: 'title', label: 'Job Title', type: 'text', required: true, colSpan: 2 },\n { name: 'department', label: 'Department', type: 'text', required: true },\n { name: 'location', label: 'Location', type: 'text', required: true },\n {\n name: 'type',\n label: 'Employment Type',\n type: 'select',\n options: [\n { label: 'Full time', value: 'full_time' },\n { label: 'Part time', value: 'part_time' },\n { label: 'Contract', value: 'contract' },\n ],\n },\n {\n name: 'experienceLevel',\n label: 'Experience Level',\n type: 'select',\n options: [\n { label: 'Entry', value: 'entry' },\n { label: 'Mid', value: 'mid' },\n { label: 'Senior', value: 'senior' },\n { label: 'Lead', value: 'lead' },\n { label: 'Executive', value: 'executive' },\n ],\n },\n {\n name: 'status',\n label: 'Status',\n type: 'select',\n options: [\n { label: 'Draft', value: 'draft' },\n { label: 'Active', value: 'active' },\n { label: 'Paused', value: 'paused' },\n { label: 'Closed', value: 'closed' },\n { label: 'Filled', value: 'filled' },\n ],\n },\n { name: 'salaryMin', label: 'Salary Min ($)', type: 'number' },\n { name: 'salaryMax', label: 'Salary Max ($)', type: 'number' },\n { name: 'deadline', label: 'Application Deadline', type: 'date' },\n { name: 'description', label: 'Description', type: 'textarea', colSpan: 2 },\n { name: 'requirements', label: 'Requirements', type: 'tags' },\n { name: 'benefits', label: 'Benefits', type: 'tags' },\n];\n\nconst STAGE_BADGE: Record<\n Candidate['status'],\n 'default' | 'secondary' | 'destructive' | 'outline'\n> = {\n new: 'outline',\n screening: 'secondary',\n interview: 'secondary',\n assessment: 'secondary',\n offer: 'default',\n hired: 'default',\n rejected: 'destructive',\n};\n\nfunction InitialsAvatar({ name, size = 32 }: { name: string; size?: number }) {\n return (\n <span\n className=\"inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full border border-border-subtle bg-bg-sunken font-medium text-text-secondary\"\n style={{ width: size, height: size, fontSize: Math.round(size * 0.36) }}\n aria-hidden=\"true\"\n >\n {getInitials(name)}\n </span>\n );\n}\n\nexport function HiringJobDetailsPagePage() {\n const navigate = useNavigate();\n const [searchParams] = useSearchParams();\n const { showToast } = useGlobalUi();\n const {\n jobPostings,\n candidates,\n updateJobPosting,\n removeJobPosting,\n initialized,\n jobPostingMatches,\n jobPostingMatchesLoading,\n fetchJobPostingMatches,\n } = useCrewFoundryStore();\n const jobId = searchParams.get('id');\n const job = jobId ? jobPostings.find((j) => j.id === jobId) : jobPostings[0];\n const [editOpen, setEditOpen] = useState(false);\n const [deleteOpen, setDeleteOpen] = useState(false);\n\n if (!initialized && !job) {\n return (\n <div className=\"flex min-h-[60vh] items-center justify-center p-6\">\n <Spinner size=\"lg\" />\n </div>\n );\n }\n\n if (!job) {\n return (\n <div className=\"p-6\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No job posting found\"\n description=\"This job posting is no longer available.\"\n />\n </div>\n );\n }\n\n // Applicants for this role. CrewCandidate has no jobId link, so match on the\n // (normalized) position title — the only real linkage the schema exposes.\n const titleKey = job.title.trim().toLowerCase();\n const jobCandidates = candidates.filter((c) => c.position.trim().toLowerCase() === titleKey);\n const countBy = (status: Candidate['status']) =>\n jobCandidates.filter((c) => c.status === status).length;\n\n const interviewCount = countBy('interview');\n\n // Real-data hiring funnel: \"Applied\" = everyone who applied to this role, then\n // the count currently at each downstream stage (offer folds in hired).\n const funnelStages: LabeledValue[] = [\n { label: 'Applied', value: jobCandidates.length },\n { label: 'Screening', value: countBy('screening') },\n { label: 'Interview', value: interviewCount },\n { label: 'Assessment', value: countBy('assessment') },\n { label: 'Offer', value: countBy('offer') + countBy('hired') },\n ];\n\n const editInitial: FormValues = {\n title: job.title,\n department: job.department,\n location: job.location,\n type: job.type,\n experienceLevel: job.experienceLevel,\n status: job.status,\n salaryMin: job.salaryMin,\n salaryMax: job.salaryMax,\n deadline: job.deadline,\n description: job.description,\n requirements: job.requirements,\n benefits: job.benefits,\n };\n\n const handleEditSubmit = async (values: FormValues) => {\n const updates: Partial<JobPosting> = {\n title: getText(values, 'title'),\n department: getText(values, 'department'),\n location: getText(values, 'location'),\n type: values.type as JobPosting['type'],\n experienceLevel: values.experienceLevel as JobPosting['experienceLevel'],\n status: values.status as JobPosting['status'],\n salaryMin: getNumber(values, 'salaryMin'),\n salaryMax: getNumber(values, 'salaryMax'),\n deadline: getText(values, 'deadline'),\n description: getText(values, 'description'),\n requirements: getTags(values, 'requirements'),\n benefits: getTags(values, 'benefits'),\n };\n await updateJobPosting(job.id, updates);\n showToast('Job posting updated', 'success');\n };\n\n const handleDelete = async () => {\n await removeJobPosting(job.id);\n showToast('Job posting deleted', 'success');\n navigate('../hiring/jobs-page');\n };\n\n const handleShare = async () => {\n try {\n await navigator.clipboard.writeText(window.location.href);\n showToast('Job link copied to clipboard', 'success');\n } catch {\n showToast('Could not copy the job link', 'error');\n }\n };\n\n const viewApplications = () =>\n navigate(`../hiring/applications-page?position=${encodeURIComponent(job.title)}`);\n\n // BOFF-4195 — AI-assisted candidate matching (deterministic skill-overlap +\n // experience-fit scoring; see wspace-crewfoundry-svc matching.service.ts).\n const matches = jobPostingMatches[job.id];\n const matchesLoaded = matches !== undefined;\n const scoreTone = (score: number): string =>\n score >= 75 ? STATUS.success : score >= 45 ? STATUS.warning : STATUS.danger;\n const handleGenerateMatches = async () => {\n try {\n await fetchJobPostingMatches(job.id);\n } catch {\n showToast('Unable to generate candidate matches', 'error');\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardContent className=\"flex flex-col gap-4 p-6 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"min-w-0\">\n <div className=\"mb-1 flex flex-wrap items-center gap-2\">\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>{job.title}</GradientText>\n </h1>\n <Badge variant={job.status === 'active' ? 'default' : 'secondary'}>\n {formatEnumLabel(job.status)}\n </Badge>\n </div>\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <Briefcase className=\"h-3.5 w-3.5\" />\n {job.department}\n </span>\n <span className=\"flex items-center gap-1\">\n <MapPin className=\"h-3.5 w-3.5\" />\n {job.location}\n </span>\n <span>{formatEnumLabel(job.type)}</span>\n </div>\n <div className=\"mt-2 flex flex-wrap items-center gap-2 text-xs text-text-secondary\">\n <Badge variant=\"secondary\" className=\"text-[10px]\">\n {jobCandidates.length} applicants\n </Badge>\n {interviewCount > 0 && (\n <Badge variant=\"secondary\" className=\"text-[10px]\">\n {interviewCount} in interview\n </Badge>\n )}\n <span className=\"flex items-center gap-1\">\n <Calendar className=\"h-3 w-3\" />\n Posted {job.postedDate}\n </span>\n </div>\n <PagePurpose className=\"mt-3\">\n Everything about this role in one place — review applicants, requirements, and\n compensation to decide who to interview and keep the posting current.\n </PagePurpose>\n </div>\n\n <div className=\"flex shrink-0 flex-col items-start gap-3 lg:items-end\">\n <div className=\"text-2xl font-bold tabular-nums text-text-primary\">\n ${(job.salaryMin / 1000).toFixed(0)}k – ${(job.salaryMax / 1000).toFixed(0)}k\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={handleShare}>\n <Share2 className=\"mr-1 h-4 w-4\" />\n Share\n </Button>\n <Button variant=\"outline\" size=\"sm\" onClick={() => setEditOpen(true)}>\n <Pencil className=\"mr-1 h-4 w-4\" />\n Edit\n </Button>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n aria-label=\"Delete job posting\"\n onClick={() => setDeleteOpen(true)}\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n <Button size=\"sm\" onClick={viewApplications}>\n <Users className=\"mr-1 h-4 w-4\" />\n View Applications\n </Button>\n </div>\n </div>\n </CardContent>\n </GlassCard>\n\n {/* Two-column body */}\n <div className=\"grid gap-6 lg:grid-cols-3\">\n {/* Left: description / requirements / benefits */}\n <div className=\"space-y-6 lg:col-span-2\">\n <Card>\n <CardHeader>\n <CardTitle>Description</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm leading-relaxed text-text-secondary\">{job.description}</p>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Requirements</CardTitle>\n </CardHeader>\n <CardContent>\n {job.requirements.length === 0 ? (\n <p className=\"text-sm text-text-muted\">No requirements listed.</p>\n ) : (\n <ul className=\"space-y-2\">\n {job.requirements.map((req, idx) => (\n <li key={idx} className=\"flex items-start gap-2 text-sm\">\n <CheckCircle className=\"mt-0.5 h-4 w-4 flex-shrink-0 text-status-success-text\" />\n {req}\n </li>\n ))}\n </ul>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Benefits</CardTitle>\n </CardHeader>\n <CardContent>\n {job.benefits.length === 0 ? (\n <p className=\"text-sm text-text-muted\">No benefits listed.</p>\n ) : (\n <div className=\"flex flex-wrap gap-2\">\n {job.benefits.map((ben, idx) => (\n <Badge key={idx} variant=\"outline\" className=\"rounded-full px-3 py-1 text-xs\">\n {ben}\n </Badge>\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* BOFF-4195 — AI-assisted candidate matching (deterministic, explainable) */}\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between gap-3\">\n <div>\n <CardTitle>Suggested Matches</CardTitle>\n <p className=\"mt-1 text-xs text-text-muted\">\n Rule-based scoring — skill overlap (70%) + experience fit (30%) against this\n role&apos;s requirements. Not an AI-generated summary.\n </p>\n </div>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={handleGenerateMatches}\n disabled={jobPostingMatchesLoading}\n >\n <Sparkles className=\"mr-1 h-4 w-4\" />\n {matchesLoaded ? 'Refresh matches' : 'Generate matches'}\n </Button>\n </CardHeader>\n <CardContent>\n {jobPostingMatchesLoading ? (\n <div className=\"flex items-center justify-center py-6\">\n <Spinner size=\"md\" />\n </div>\n ) : !matchesLoaded ? (\n <p className=\"text-sm text-text-muted\">\n Click &quot;Generate matches&quot; to rank the candidate pool against this\n posting&apos;s requirements and experience level.\n </p>\n ) : matches.length === 0 ? (\n <p className=\"text-sm text-text-muted\">\n No candidates in the pool right now (excluding already-hired/rejected).\n </p>\n ) : (\n <ul className=\"space-y-4\">\n {matches.map((match) => (\n <li\n key={match.candidateId}\n className=\"rounded-[var(--radius-card)] border border-border-subtle bg-bg-surface p-3\"\n >\n <div className=\"flex items-center gap-2\">\n <InitialsAvatar\n name={`${match.candidate.firstName} ${match.candidate.lastName}`}\n size={28}\n />\n <span className=\"min-w-0 flex-1 truncate text-sm font-medium text-text-primary\">\n {match.candidate.firstName} {match.candidate.lastName}\n </span>\n <Badge\n variant={STAGE_BADGE[match.candidate.status]}\n className=\"shrink-0 text-[9px]\"\n >\n {formatEnumLabel(match.candidate.status)}\n </Badge>\n </div>\n\n <div className=\"mt-2\">\n <SkillBar\n label=\"Overall match\"\n value={match.score}\n levelLabel={`${match.score}%`}\n color={scoreTone(match.score)}\n height={6}\n />\n </div>\n <div className=\"mt-2 grid grid-cols-2 gap-2 text-xs text-text-secondary\">\n <span>Skills: {match.skillScore}%</span>\n <span>Experience: {match.experienceScore}%</span>\n </div>\n\n {(match.matchedSkills.length > 0 || match.missingSkills.length > 0) && (\n <div className=\"mt-2 flex flex-wrap gap-1\">\n {match.matchedSkills.map((skill) => (\n <Badge\n key={`m-${skill}`}\n variant=\"secondary\"\n className=\"gap-1 text-[10px]\"\n >\n <CheckCircle className=\"h-3 w-3 text-status-success-text\" />\n {skill}\n </Badge>\n ))}\n {match.missingSkills.map((skill) => (\n <Badge\n key={`x-${skill}`}\n variant=\"outline\"\n className=\"gap-1 text-[10px] text-text-muted\"\n >\n <XCircle className=\"h-3 w-3 text-status-error-text\" />\n {skill}\n </Badge>\n ))}\n </div>\n )}\n\n {/* Rationale is always visible — the ranking is never a black box. */}\n <p className=\"mt-2 text-xs leading-relaxed text-text-muted\">\n {match.rationale}\n </p>\n </li>\n ))}\n </ul>\n )}\n </CardContent>\n </Card>\n </div>\n\n {/* Right rail: candidates + funnel */}\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Candidates</CardTitle>\n </CardHeader>\n <CardContent>\n {jobCandidates.length === 0 ? (\n <p className=\"text-sm text-text-muted\">No applicants matched to this role yet.</p>\n ) : (\n <ul className=\"max-h-96 space-y-1 overflow-y-auto pr-1\">\n {jobCandidates.map((candidate) => (\n <li\n key={candidate.id}\n className=\"flex items-center gap-2 rounded-[var(--radius-input)] px-2 py-2 hover:bg-bg-sunken\"\n >\n <InitialsAvatar\n name={`${candidate.firstName} ${candidate.lastName}`}\n size={30}\n />\n <span className=\"min-w-0 flex-1 truncate text-sm font-medium text-text-primary\">\n {candidate.firstName} {candidate.lastName}\n </span>\n <Badge\n variant={STAGE_BADGE[candidate.status]}\n className=\"shrink-0 text-[9px]\"\n >\n {formatEnumLabel(candidate.status)}\n </Badge>\n <RatingStars value={candidate.rating} size={12} className=\"shrink-0\" />\n </li>\n ))}\n </ul>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Hiring funnel</CardTitle>\n </CardHeader>\n <CardContent>\n {jobCandidates.length === 0 ? (\n <p className=\"text-sm text-text-muted\">\n The funnel appears once candidates apply to this role.\n </p>\n ) : (\n <HBarFunnel\n stages={funnelStages}\n valueFormatter={(value) => String(value)}\n showConversion\n />\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n\n <FormModal\n open={editOpen}\n onOpenChange={setEditOpen}\n title=\"Edit Job Posting\"\n description=\"Update the role details, compensation, and status.\"\n fields={JOB_FIELDS}\n initialValues={editInitial}\n submitLabel=\"Save changes\"\n onSubmit={handleEditSubmit}\n />\n <ConfirmDialog\n open={deleteOpen}\n onOpenChange={setDeleteOpen}\n title=\"Delete job posting\"\n description={`Permanently remove “${job.title}”? This cannot be undone.`}\n confirmLabel=\"Delete\"\n destructive\n onConfirm={handleDelete}\n />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"HiringJobDetailsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/HiringJobDetailsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n Spinner,\n} from '@burdenoff/fe-libs/ui';\nimport { useState } from 'react';\nimport {\n Briefcase,\n Calendar,\n CheckCircle,\n Clock,\n MapPin,\n Pencil,\n Share2,\n Sparkles,\n Trash2,\n Users,\n XCircle,\n} from 'lucide-react';\nimport { useNavigate, useSearchParams } from 'react-router';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useCrewFoundryStore, type Candidate, type JobPosting } from '../store/crewfoundryStore';\nimport { FormModal, type FieldConfig, type FormValues } from '../components/forms/FormModal';\nimport { ConfirmDialog } from '../components/forms/ConfirmDialog';\nimport { getNumber, getTags, getText } from '../components/forms/formValues';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\nimport { localDayDiff, localTodayString, parseLocalDate } from '../utils/dateUtils';\nimport {\n HBarFunnel,\n RatingStars,\n SkillBar,\n STATUS,\n getInitials,\n type LabeledValue,\n} from '../components/viz';\n\nconst JOB_FIELDS: FieldConfig[] = [\n // Title and Department are always required (drafts and publishes alike).\n // Location, Type, Experience Level, and Description are only required\n // when the job is being actively published — enforced both via the\n // FormModal-level `requiredWhen` predicate (renders the `*` and blocks\n // submit) and via the submit-time guard in handleEditSubmit as a belt-\n // and-braces check. Title/department get the unconditional `required`\n // marker (FormModal's universal check fires regardless of status).\n { name: 'title', label: 'Job Title', type: 'text', required: true, colSpan: 2 },\n { name: 'department', label: 'Department', type: 'text', required: true },\n {\n name: 'location',\n label: 'Location',\n type: 'text',\n requiredWhen: (values) => String(values.status ?? '').toLowerCase() === 'active',\n },\n {\n name: 'type',\n label: 'Employment Type',\n type: 'select',\n requiredWhen: (values) => String(values.status ?? '').toLowerCase() === 'active',\n options: [\n { label: 'Full time', value: 'full_time' },\n { label: 'Part time', value: 'part_time' },\n { label: 'Contract', value: 'contract' },\n ],\n },\n {\n name: 'experienceLevel',\n label: 'Experience Level',\n type: 'select',\n requiredWhen: (values) => String(values.status ?? '').toLowerCase() === 'active',\n options: [\n { label: 'Entry', value: 'entry' },\n { label: 'Mid', value: 'mid' },\n { label: 'Senior', value: 'senior' },\n { label: 'Lead', value: 'lead' },\n { label: 'Executive', value: 'executive' },\n ],\n },\n {\n name: 'status',\n label: 'Status',\n type: 'select',\n options: [\n { label: 'Draft', value: 'draft' },\n { label: 'Active', value: 'active' },\n { label: 'Paused', value: 'paused' },\n { label: 'Closed', value: 'closed' },\n { label: 'Filled', value: 'filled' },\n ],\n },\n { name: 'salaryMin', label: 'Salary Min ($)', type: 'number' },\n { name: 'salaryMax', label: 'Salary Max ($)', type: 'number' },\n { name: 'deadline', label: 'Application Deadline', type: 'date' },\n {\n name: 'description',\n label: 'Description',\n type: 'textarea',\n colSpan: 2,\n requiredWhen: (values) => String(values.status ?? '').toLowerCase() === 'active',\n },\n { name: 'requirements', label: 'Requirements', type: 'tags' },\n { name: 'benefits', label: 'Benefits', type: 'tags' },\n];\n\nconst STAGE_BADGE: Record<\n Candidate['status'],\n 'default' | 'secondary' | 'destructive' | 'outline'\n> = {\n new: 'outline',\n screening: 'secondary',\n interview: 'secondary',\n assessment: 'secondary',\n offer: 'default',\n hired: 'default',\n rejected: 'destructive',\n};\n\nfunction InitialsAvatar({ name, size = 32 }: { name: string; size?: number }) {\n return (\n <span\n className=\"inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full border border-border-subtle bg-bg-sunken font-medium text-text-secondary\"\n style={{ width: size, height: size, fontSize: Math.round(size * 0.36) }}\n aria-hidden=\"true\"\n >\n {getInitials(name)}\n </span>\n );\n}\n\n/**\n * Format a salary value. The previous `(value / 1000).toFixed(0)` truncated\n * any value under 1000 to 0, so an entry-level posting (Min 12, Max 13)\n * displayed as the misleading \"$0k – $0k\". Apply per-value so a range can\n * legitimately span the 1000 threshold — e.g. Min 100, Max 1000 renders\n * as \"$100 – $1k\" rather than grouping them as \"$0k – $1k\". The 0 case is\n * special-cased to \"$0\" so unset salaries don't read as \"—\" or \"0k\".\n */\nfunction formatSalary(value: number): string {\n if (value === 0) return '$0';\n if (value < 1000) return `$${value.toLocaleString()}`;\n return `$${Math.round(value / 1000)}k`;\n}\n\nfunction formatSalaryRange(min: number, max: number): string {\n return `${formatSalary(min)} – ${formatSalary(max)}`;\n}\n\n/**\n * Deadline label from an ISO date-only string or null. Renders relative\n * phrasing when upcoming (\"Closes in 3 days\", \"Closes today\",\n * \"Closed yesterday\") and falls back to the raw date when unparseable.\n * Returns \"No deadline\" for null — matches the card on the\n * Jobs list page so users get a consistent read of a job's deadline.\n *\n * Compares against local calendar days (not raw timestamps) so a deadline\n * picked as \"today\" never reads as \"Closed yesterday\" for users in\n * negative-UTC offsets — `new Date('YYYY-MM-DD')` parses at UTC midnight\n * which would otherwise put it 17-20 hours in the past by local noon.\n */\nfunction formatDeadlineLabel(dateStr: string | null): string {\n if (!dateStr) {\n return 'No deadline';\n }\n const deadline = parseLocalDate(dateStr);\n if (!deadline) {\n return dateStr;\n }\n const days = localDayDiff(new Date(), deadline);\n if (days > 1) {\n return `Closes in ${days} days`;\n }\n if (days === 1) {\n return 'Closes tomorrow';\n }\n if (days === 0) {\n return 'Closes today';\n }\n if (days === -1) {\n return 'Closed yesterday';\n }\n return `Closed ${Math.abs(days)} days ago`;\n}\n\nexport function HiringJobDetailsPagePage() {\n const navigate = useNavigate();\n const [searchParams] = useSearchParams();\n const { showToast } = useGlobalUi();\n const {\n jobPostings,\n candidates,\n updateJobPosting,\n removeJobPosting,\n initialized,\n jobPostingMatches,\n jobPostingMatchesLoading,\n fetchJobPostingMatches,\n } = useCrewFoundryStore();\n const jobId = searchParams.get('id');\n const job = jobId ? jobPostings.find((j) => j.id === jobId) : jobPostings[0];\n const [editOpen, setEditOpen] = useState(false);\n const [deleteOpen, setDeleteOpen] = useState(false);\n // Mirrors the BOFF-7588 create-page validation contract for edits. The\n // showToast path here is currently a no-op (provider has no rendered\n // viewport — see the linear ticket); the modal-level errorMessage banner\n // is what the user actually sees.\n const [editError, setEditError] = useState<string | null>(null);\n // Local calendar date for the deadline `today` guard — see BOFF-7588.\n // Recomputed on every render AND on every submit (via localTodayString())\n // so the value stays current if the modal stays open across midnight.\n const today = localTodayString();\n\n if (!initialized && !job) {\n return (\n <div className=\"flex min-h-[60vh] items-center justify-center p-6\">\n <Spinner size=\"lg\" />\n </div>\n );\n }\n\n if (!job) {\n return (\n <div className=\"p-6\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No job posting found\"\n description=\"This job posting is no longer available.\"\n />\n </div>\n );\n }\n\n // Applicants for this role. CrewCandidate has no jobId link, so match on the\n // (normalized) position title — the only real linkage the schema exposes.\n const titleKey = job.title.trim().toLowerCase();\n const jobCandidates = candidates.filter((c) => c.position.trim().toLowerCase() === titleKey);\n const countBy = (status: Candidate['status']) =>\n jobCandidates.filter((c) => c.status === status).length;\n\n const interviewCount = countBy('interview');\n\n // Real-data hiring funnel: \"Applied\" = everyone who applied to this role, then\n // the count currently at each downstream stage (offer folds in hired).\n const funnelStages: LabeledValue[] = [\n { label: 'Applied', value: jobCandidates.length },\n { label: 'Screening', value: countBy('screening') },\n { label: 'Interview', value: interviewCount },\n { label: 'Assessment', value: countBy('assessment') },\n { label: 'Offer', value: countBy('offer') + countBy('hired') },\n ];\n\n const editInitial: FormValues = {\n title: job.title,\n department: job.department,\n location: job.location,\n type: job.type,\n experienceLevel: job.experienceLevel,\n status: job.status,\n salaryMin: job.salaryMin,\n salaryMax: job.salaryMax,\n deadline: job.deadline ?? '',\n description: job.description,\n requirements: job.requirements,\n benefits: job.benefits,\n };\n\n const handleEditSubmit = async (values: FormValues) => {\n // Build errors map; mirror the create-form rule set (BOFF-7588):\n // title/department/deadline always; location/type/experienceLevel/description\n // when the job is being actively published (status === 'active').\n const errors: string[] = [];\n const title = getText(values, 'title').trim();\n const department = getText(values, 'department').trim();\n const location = getText(values, 'location').trim();\n const description = getText(values, 'description').trim();\n const deadline = getText(values, 'deadline').trim();\n if (!title) errors.push('Job Title is required');\n if (!department) errors.push('Department is required');\n if (deadline && deadline < localTodayString()) {\n errors.push('Application Deadline cannot be a past date');\n }\n if (String(values.status ?? '').toLowerCase() === 'active') {\n if (!location) errors.push('Location is required');\n if (!description) errors.push('Description is required');\n }\n const salaryMinValue = getNumber(values, 'salaryMin');\n const salaryMaxValue = getNumber(values, 'salaryMax');\n // No negatives — neither side of the range can be < 0.\n if (salaryMinValue < 0) {\n errors.push('Salary Min cannot be negative');\n } else if (salaryMaxValue < 0) {\n errors.push('Salary Max cannot be negative');\n } else {\n // Symmetric rule: either both fields are 0 (unset — display as $0),\n // or both must be filled coherently (Max >= Min, no zero-on-one-side\n // asymmetry that produced misleading \"0 to Xk\" displays). Catches\n // both directions: max set without min, and min set without max.\n const bothEmpty = salaryMinValue === 0 && salaryMaxValue === 0;\n if (!bothEmpty && salaryMaxValue === 0 && salaryMinValue > 0) {\n errors.push('Provide Salary Max, or leave both empty');\n } else if (!bothEmpty && salaryMaxValue > 0 && salaryMinValue === 0) {\n errors.push('Provide Salary Min, or leave both empty');\n } else if (!bothEmpty && salaryMaxValue > 0 && salaryMaxValue < salaryMinValue) {\n errors.push('Salary Max cannot be less than Salary Min');\n }\n }\n\n if (errors.length > 0) {\n // Only the banner — FormModal's catch will also call showToast, so we\n // suppress the explicit one to avoid a double-toast when the shared\n // viewport is fixed (BOFF-7584). The banner stays visible because we\n // throw, which skips FormModal's onOpenChange(false) and keeps the\n // modal open with the banner rendered.\n setEditError(errors[0]);\n // Throw so FormModal's onSubmit await rejects and skips its\n // `onOpenChange(false)`, keeping the modal open with the banner visible.\n // Without this, FormModal closes the modal immediately after the\n // Promise resolves, and the user sees \"saved but nothing happens\"\n // because the error banner never gets a frame to render.\n throw new Error(errors[0]);\n }\n setEditError(null);\n\n const updates: Partial<JobPosting> = {\n title,\n department,\n location,\n type: values.type as JobPosting['type'],\n experienceLevel: values.experienceLevel as JobPosting['experienceLevel'],\n status: values.status as JobPosting['status'],\n salaryMin: salaryMinValue,\n salaryMax: salaryMaxValue,\n // null = \"no deadline\", matching the create-page contract (BOFF-7588).\n deadline: deadline === '' ? null : deadline,\n description,\n requirements: getTags(values, 'requirements'),\n benefits: getTags(values, 'benefits'),\n };\n await updateJobPosting(job.id, updates);\n showToast('Job posting updated', 'success');\n setEditOpen(false);\n };\n\n const handleDelete = async () => {\n await removeJobPosting(job.id);\n showToast('Job posting deleted', 'success');\n navigate('../hiring/jobs-page');\n };\n\n const handleShare = async () => {\n try {\n await navigator.clipboard.writeText(window.location.href);\n showToast('Job link copied to clipboard', 'success');\n } catch {\n showToast('Could not copy the job link', 'error');\n }\n };\n\n const viewApplications = () =>\n navigate(`../hiring/applications-page?position=${encodeURIComponent(job.title)}`);\n\n // BOFF-4195 — AI-assisted candidate matching (deterministic skill-overlap +\n // experience-fit scoring; see wspace-crewfoundry-svc matching.service.ts).\n const matches = jobPostingMatches[job.id];\n const matchesLoaded = matches !== undefined;\n const scoreTone = (score: number): string =>\n score >= 75 ? STATUS.success : score >= 45 ? STATUS.warning : STATUS.danger;\n const handleGenerateMatches = async () => {\n try {\n await fetchJobPostingMatches(job.id);\n } catch {\n showToast('Unable to generate candidate matches', 'error');\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardContent className=\"flex flex-col gap-4 p-6 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"min-w-0\">\n <div className=\"mb-1 flex flex-wrap items-center gap-2\">\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>{job.title}</GradientText>\n </h1>\n <Badge variant={job.status === 'active' ? 'default' : 'secondary'}>\n {formatEnumLabel(job.status)}\n </Badge>\n </div>\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <Briefcase className=\"h-3.5 w-3.5\" />\n {job.department}\n </span>\n <span className=\"flex items-center gap-1\">\n <MapPin className=\"h-3.5 w-3.5\" />\n {job.location}\n </span>\n <span>{formatEnumLabel(job.type)}</span>\n </div>\n <div className=\"mt-2 flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-text-secondary\">\n <Badge variant=\"secondary\" className=\"text-[10px]\">\n {jobCandidates.length} applicants\n </Badge>\n {interviewCount > 0 && (\n <Badge variant=\"secondary\" className=\"text-[10px]\">\n {interviewCount} in interview\n </Badge>\n )}\n <span className=\"flex items-center gap-1\">\n <Calendar className=\"h-3 w-3\" />\n Posted {job.postedDate}\n </span>\n <span className=\"flex items-center gap-1 text-text-secondary\">\n <Clock className=\"h-3 w-3\" />\n {formatDeadlineLabel(job.deadline)}\n </span>\n </div>\n <PagePurpose className=\"mt-3\">\n Everything about this role in one place — review applicants, requirements, and\n compensation to decide who to interview and keep the posting current.\n </PagePurpose>\n </div>\n\n <div className=\"flex shrink-0 flex-col items-start gap-3 lg:items-end\">\n <div className=\"text-2xl font-bold tabular-nums text-text-primary\">\n {formatSalaryRange(job.salaryMin, job.salaryMax)}\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={handleShare}>\n <Share2 className=\"mr-1 h-4 w-4\" />\n Share\n </Button>\n <Button variant=\"outline\" size=\"sm\" onClick={() => setEditOpen(true)}>\n <Pencil className=\"mr-1 h-4 w-4\" />\n Edit\n </Button>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n aria-label=\"Delete job posting\"\n onClick={() => setDeleteOpen(true)}\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n <Button size=\"sm\" onClick={viewApplications}>\n <Users className=\"mr-1 h-4 w-4\" />\n View Applications\n </Button>\n </div>\n </div>\n </CardContent>\n </GlassCard>\n\n {/* Two-column body */}\n <div className=\"grid gap-6 lg:grid-cols-3\">\n {/* Left: description / requirements / benefits */}\n <div className=\"space-y-6 lg:col-span-2\">\n <Card>\n <CardHeader>\n <CardTitle>Description</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm leading-relaxed text-text-secondary\">{job.description}</p>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Requirements</CardTitle>\n </CardHeader>\n <CardContent>\n {job.requirements.length === 0 ? (\n <p className=\"text-sm text-text-muted\">No requirements listed.</p>\n ) : (\n <ul className=\"space-y-2\">\n {job.requirements.map((req, idx) => (\n <li key={idx} className=\"flex items-start gap-2 text-sm\">\n <CheckCircle className=\"mt-0.5 h-4 w-4 flex-shrink-0 text-status-success-text\" />\n {req}\n </li>\n ))}\n </ul>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Benefits</CardTitle>\n </CardHeader>\n <CardContent>\n {job.benefits.length === 0 ? (\n <p className=\"text-sm text-text-muted\">No benefits listed.</p>\n ) : (\n <div className=\"flex flex-wrap gap-2\">\n {job.benefits.map((ben, idx) => (\n <Badge key={idx} variant=\"outline\" className=\"rounded-full px-3 py-1 text-xs\">\n {ben}\n </Badge>\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* BOFF-4195 — AI-assisted candidate matching (deterministic, explainable) */}\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between gap-3\">\n <div>\n <CardTitle>Suggested Matches</CardTitle>\n <p className=\"mt-1 text-xs text-text-muted\">\n Rule-based scoring — skill overlap (70%) + experience fit (30%) against this\n role&apos;s requirements. Not an AI-generated summary.\n </p>\n </div>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={handleGenerateMatches}\n disabled={jobPostingMatchesLoading}\n >\n <Sparkles className=\"mr-1 h-4 w-4\" />\n {matchesLoaded ? 'Refresh matches' : 'Generate matches'}\n </Button>\n </CardHeader>\n <CardContent>\n {jobPostingMatchesLoading ? (\n <div className=\"flex items-center justify-center py-6\">\n <Spinner size=\"md\" />\n </div>\n ) : !matchesLoaded ? (\n <p className=\"text-sm text-text-muted\">\n Click &quot;Generate matches&quot; to rank the candidate pool against this\n posting&apos;s requirements and experience level.\n </p>\n ) : matches.length === 0 ? (\n <p className=\"text-sm text-text-muted\">\n No candidates in the pool right now (excluding already-hired/rejected).\n </p>\n ) : (\n <ul className=\"space-y-4\">\n {matches.map((match) => (\n <li\n key={match.candidateId}\n className=\"rounded-[var(--radius-card)] border border-border-subtle bg-bg-surface p-3\"\n >\n <div className=\"flex items-center gap-2\">\n <InitialsAvatar\n name={`${match.candidate.firstName} ${match.candidate.lastName}`}\n size={28}\n />\n <span className=\"min-w-0 flex-1 truncate text-sm font-medium text-text-primary\">\n {match.candidate.firstName} {match.candidate.lastName}\n </span>\n <Badge\n variant={STAGE_BADGE[match.candidate.status]}\n className=\"shrink-0 text-[9px]\"\n >\n {formatEnumLabel(match.candidate.status)}\n </Badge>\n </div>\n\n <div className=\"mt-2\">\n <SkillBar\n label=\"Overall match\"\n value={match.score}\n levelLabel={`${match.score}%`}\n color={scoreTone(match.score)}\n height={6}\n />\n </div>\n <div className=\"mt-2 grid grid-cols-2 gap-2 text-xs text-text-secondary\">\n <span>Skills: {match.skillScore}%</span>\n <span>Experience: {match.experienceScore}%</span>\n </div>\n\n {(match.matchedSkills.length > 0 || match.missingSkills.length > 0) && (\n <div className=\"mt-2 flex flex-wrap gap-1\">\n {match.matchedSkills.map((skill) => (\n <Badge\n key={`m-${skill}`}\n variant=\"secondary\"\n className=\"gap-1 text-[10px]\"\n >\n <CheckCircle className=\"h-3 w-3 text-status-success-text\" />\n {skill}\n </Badge>\n ))}\n {match.missingSkills.map((skill) => (\n <Badge\n key={`x-${skill}`}\n variant=\"outline\"\n className=\"gap-1 text-[10px] text-text-muted\"\n >\n <XCircle className=\"h-3 w-3 text-status-error-text\" />\n {skill}\n </Badge>\n ))}\n </div>\n )}\n\n {/* Rationale is always visible — the ranking is never a black box. */}\n <p className=\"mt-2 text-xs leading-relaxed text-text-muted\">\n {match.rationale}\n </p>\n </li>\n ))}\n </ul>\n )}\n </CardContent>\n </Card>\n </div>\n\n {/* Right rail: candidates + funnel */}\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Candidates</CardTitle>\n </CardHeader>\n <CardContent>\n {jobCandidates.length === 0 ? (\n <p className=\"text-sm text-text-muted\">No applicants matched to this role yet.</p>\n ) : (\n <ul className=\"max-h-96 space-y-1 overflow-y-auto pr-1\">\n {jobCandidates.map((candidate) => (\n <li\n key={candidate.id}\n className=\"flex items-center gap-2 rounded-[var(--radius-input)] px-2 py-2 hover:bg-bg-sunken\"\n >\n <InitialsAvatar\n name={`${candidate.firstName} ${candidate.lastName}`}\n size={30}\n />\n <span className=\"min-w-0 flex-1 truncate text-sm font-medium text-text-primary\">\n {candidate.firstName} {candidate.lastName}\n </span>\n <Badge\n variant={STAGE_BADGE[candidate.status]}\n className=\"shrink-0 text-[9px]\"\n >\n {formatEnumLabel(candidate.status)}\n </Badge>\n <RatingStars value={candidate.rating} size={12} className=\"shrink-0\" />\n </li>\n ))}\n </ul>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Hiring funnel</CardTitle>\n </CardHeader>\n <CardContent>\n {jobCandidates.length === 0 ? (\n <p className=\"text-sm text-text-muted\">\n The funnel appears once candidates apply to this role.\n </p>\n ) : (\n <HBarFunnel\n stages={funnelStages}\n valueFormatter={(value) => String(value)}\n showConversion\n />\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n\n <FormModal\n open={editOpen}\n onOpenChange={(open) => {\n setEditOpen(open);\n setEditError(null);\n }}\n title=\"Edit Job Posting\"\n description=\"Update the role details, compensation, and status.\"\n errorMessage={editError ?? undefined}\n onValidationError={(msg) => setEditError(msg)}\n fields={JOB_FIELDS.map((field) =>\n field.name === 'deadline' ? { ...field, min: today } : field\n )}\n initialValues={editInitial}\n submitLabel=\"Save changes\"\n onSubmit={handleEditSubmit}\n />\n <ConfirmDialog\n open={deleteOpen}\n onOpenChange={setDeleteOpen}\n title=\"Delete job posting\"\n description={`Permanently remove “${job.title}”? This cannot be undone.`}\n confirmLabel=\"Delete\"\n destructive\n onConfirm={handleDelete}\n />\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"HiringJobsPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/HiringJobsPagePage.tsx"],"names":[],"mappings":"AA2DA,wBAAgB,kBAAkB,gCA0MjC"}
1
+ {"version":3,"file":"HiringJobsPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/HiringJobsPagePage.tsx"],"names":[],"mappings":"AAiHA,wBAAgB,kBAAkB,gCA+MjC"}
@@ -4,21 +4,22 @@ import { useTr as n } from "../../shared/hooks/useTr.js";
4
4
  import { AvatarStack as r } from "../components/viz/AvatarStack.js";
5
5
  import { SegmentedTabs as i } from "../components/viz/SegmentedTabs.js";
6
6
  import "../components/viz/index.js";
7
- import { useMemo as a, useState as o } from "react";
8
- import { Briefcase as s, DollarSign as c, MapPin as l, Plus as u, Search as d, Users as f } from "lucide-react";
9
- import { useNavigate as p } from "react-router";
10
- import { jsx as m, jsxs as h } from "react/jsx-runtime";
11
- import { Badge as g, Button as _, CardContent as v, CardHeader as y, CardTitle as b, GlassCard as x, GradientText as S, IllustratedEmptyState as C, Input as w, PagePurpose as T, Pagination as E } from "@burdenoff/fe-libs/ui";
12
- import { usePagination as D } from "@burdenoff/fe-libs/shared/hooks";
7
+ import { localDayDiff as a, parseLocalDate as o } from "../utils/dateUtils.js";
8
+ import { useMemo as s, useState as c } from "react";
9
+ import { Briefcase as l, DollarSign as u, MapPin as d, Plus as f, Search as p, Users as m } from "lucide-react";
10
+ import { useNavigate as h } from "react-router";
11
+ import { jsx as g, jsxs as _ } from "react/jsx-runtime";
12
+ import { Badge as v, Button as y, CardContent as b, CardHeader as x, CardTitle as S, GlassCard as C, GradientText as w, IllustratedEmptyState as T, Input as E, PagePurpose as D, Pagination as O } from "@burdenoff/fe-libs/ui";
13
+ import { usePagination as k } from "@burdenoff/fe-libs/shared/hooks";
13
14
  //#region src/crewfoundry/pages/HiringJobsPagePage.tsx
14
- var O = [
15
+ var A = [
15
16
  "active",
16
17
  "draft",
17
18
  "paused",
18
19
  "closed",
19
20
  "filled"
20
21
  ];
21
- function k(e) {
22
+ function j(e) {
22
23
  let t = new Date(e);
23
24
  if (Number.isNaN(t.getTime())) return e;
24
25
  let n = Math.floor((Date.now() - t.getTime()) / 864e5);
@@ -32,37 +33,50 @@ function k(e) {
32
33
  let r = Math.floor(n / 30);
33
34
  return r === 1 ? "1 month ago" : `${r} months ago`;
34
35
  }
35
- function A({ label: e, children: t }) {
36
- return /* @__PURE__ */ h("div", {
36
+ function M(e) {
37
+ return e === 0 ? "$0" : e < 1e3 ? `$${e.toLocaleString()}` : `$${Math.round(e / 1e3)}k`;
38
+ }
39
+ function N(e) {
40
+ if (!e) return "No deadline";
41
+ let t = o(e);
42
+ if (!t) return e;
43
+ let n = a(/* @__PURE__ */ new Date(), t);
44
+ return n > 1 ? `Closes in ${n} days` : n === 1 ? "Closes tomorrow" : n === 0 ? "Closes today" : n === -1 ? "Closed yesterday" : `Closed ${Math.abs(n)} days ago`;
45
+ }
46
+ function P(e, t) {
47
+ return `${M(e)} – ${M(t)}`;
48
+ }
49
+ function F({ label: e, children: t }) {
50
+ return /* @__PURE__ */ _("div", {
37
51
  className: "flex items-center justify-between gap-3 text-sm",
38
- children: [/* @__PURE__ */ m("span", {
52
+ children: [/* @__PURE__ */ g("span", {
39
53
  className: "text-text-secondary",
40
54
  children: e
41
- }), /* @__PURE__ */ m("span", {
55
+ }), /* @__PURE__ */ g("span", {
42
56
  className: "font-medium text-text-primary text-right",
43
57
  children: t
44
58
  })]
45
59
  });
46
60
  }
47
- function j() {
48
- let j = n(), M = p(), { jobPostings: N, candidates: P } = e(), [F, I] = o(""), [L, R] = o("all"), { page: z, pageSize: B, offset: V, setPage: H } = D({ initialPageSize: 12 }), U = a(() => {
49
- let e = F.toLowerCase();
50
- return N.filter((t) => {
51
- let n = `${t.title} ${t.department} ${t.location}`.toLowerCase().includes(e), r = L === "all" || t.status === L;
61
+ function I() {
62
+ let a = n(), o = h(), { jobPostings: M, candidates: I } = e(), [L, R] = c(""), [z, B] = c("all"), { page: V, pageSize: H, offset: U, setPage: W } = k({ initialPageSize: 12 }), G = s(() => {
63
+ let e = L.toLowerCase();
64
+ return M.filter((t) => {
65
+ let n = `${t.title} ${t.department} ${t.location}`.toLowerCase().includes(e), r = z === "all" || t.status === z;
52
66
  return n && r;
53
67
  });
54
68
  }, [
55
- N,
56
- F,
57
- L
58
- ]), W = U.slice(V, V + B), G = (e) => M(`../hiring/job-details-page?id=${encodeURIComponent(e)}`), K = N.filter((e) => e.status === "active").length, q = N.reduce((e, t) => e + t.applicants, 0), J = a(() => {
69
+ M,
70
+ L,
71
+ z
72
+ ]), K = G.slice(U, U + H), q = (e) => o(`../hiring/job-details-page?id=${encodeURIComponent(e)}`), J = M.filter((e) => e.status === "active").length, Y = M.reduce((e, t) => e + t.applicants, 0), X = s(() => {
59
73
  let e = [{
60
74
  id: "all",
61
75
  label: "All",
62
- count: N.length
76
+ count: M.length
63
77
  }];
64
- for (let n of O) {
65
- let r = N.filter((e) => e.status === n).length;
78
+ for (let n of A) {
79
+ let r = M.filter((e) => e.status === n).length;
66
80
  (r > 0 || n === "active") && e.push({
67
81
  id: n,
68
82
  label: t(n),
@@ -70,179 +84,179 @@ function j() {
70
84
  });
71
85
  }
72
86
  return e;
73
- }, [N]), Y = a(() => {
87
+ }, [M]), Z = s(() => {
74
88
  let e = /* @__PURE__ */ new Map();
75
- for (let t of N) {
76
- let n = t.title.trim().toLowerCase(), r = P.filter((e) => e.position.trim().toLowerCase() === n).map((e) => ({ name: `${e.firstName} ${e.lastName}`.trim() }));
89
+ for (let t of M) {
90
+ let n = t.title.trim().toLowerCase(), r = I.filter((e) => e.position.trim().toLowerCase() === n).map((e) => ({ name: `${e.firstName} ${e.lastName}`.trim() }));
77
91
  e.set(t.id, r);
78
92
  }
79
93
  return e;
80
- }, [N, P]);
81
- return /* @__PURE__ */ h("div", {
94
+ }, [M, I]);
95
+ return /* @__PURE__ */ _("div", {
82
96
  className: "space-y-6 p-6",
83
97
  children: [
84
- /* @__PURE__ */ h("div", {
98
+ /* @__PURE__ */ _("div", {
85
99
  className: "flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between",
86
- children: [/* @__PURE__ */ h("div", { children: [
87
- /* @__PURE__ */ m("h1", {
100
+ children: [/* @__PURE__ */ _("div", { children: [
101
+ /* @__PURE__ */ g("h1", {
88
102
  className: "text-2xl font-semibold",
89
- children: /* @__PURE__ */ m(S, { children: j("crewfoundry.hiringJobs.title", "Job Postings") })
103
+ children: /* @__PURE__ */ g(w, { children: a("crewfoundry.hiringJobs.title", "Job Postings") })
90
104
  }),
91
- /* @__PURE__ */ h("p", {
105
+ /* @__PURE__ */ _("p", {
92
106
  className: "text-sm text-text-secondary",
93
107
  children: [
94
- K,
108
+ J,
95
109
  " ",
96
- j("crewfoundry.hiringJobs.summary.activePositions", "active positions"),
110
+ a("crewfoundry.hiringJobs.summary.activePositions", "active positions"),
97
111
  " ",
98
112
  "· ",
99
- q,
113
+ Y,
100
114
  " ",
101
- j("crewfoundry.hiringJobs.summary.totalApplicants", "total applicants")
115
+ a("crewfoundry.hiringJobs.summary.totalApplicants", "total applicants")
102
116
  ]
103
117
  }),
104
- /* @__PURE__ */ m(T, {
118
+ /* @__PURE__ */ g(D, {
105
119
  className: "mt-1",
106
120
  children: "All your open and past job postings in one place — use it to monitor applicant volume and manage what roles you're actively hiring for."
107
121
  })
108
- ] }), /* @__PURE__ */ h(_, {
122
+ ] }), /* @__PURE__ */ _(y, {
109
123
  size: "sm",
110
- onClick: () => M("../hiring/create-job-page"),
111
- children: [/* @__PURE__ */ m(u, { className: "h-4 w-4 mr-1" }), j("crewfoundry.hiringJobs.actions.postNewJob", "Post New Job")]
124
+ onClick: () => o("../hiring/create-job-page"),
125
+ children: [/* @__PURE__ */ g(f, { className: "h-4 w-4 mr-1" }), a("crewfoundry.hiringJobs.actions.postNewJob", "Post New Job")]
112
126
  })]
113
127
  }),
114
- /* @__PURE__ */ h("div", {
128
+ /* @__PURE__ */ _("div", {
115
129
  className: "flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between",
116
- children: [/* @__PURE__ */ m("div", {
130
+ children: [/* @__PURE__ */ g("div", {
117
131
  className: "-mx-1 overflow-x-auto px-1",
118
- children: /* @__PURE__ */ m(i, {
119
- tabs: J,
120
- value: L,
132
+ children: /* @__PURE__ */ g(i, {
133
+ tabs: X,
134
+ value: z,
121
135
  onChange: (e) => {
122
- R(e), H(1);
136
+ B(e), W(1);
123
137
  },
124
138
  "aria-label": "Filter job postings by status"
125
139
  })
126
- }), /* @__PURE__ */ h("div", {
140
+ }), /* @__PURE__ */ _("div", {
127
141
  className: "relative w-full max-w-sm",
128
- children: [/* @__PURE__ */ m(d, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary" }), /* @__PURE__ */ m(w, {
129
- placeholder: j("crewfoundry.hiringJobs.searchPlaceholder", "Search positions..."),
142
+ children: [/* @__PURE__ */ g(p, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary" }), /* @__PURE__ */ g(E, {
143
+ placeholder: a("crewfoundry.hiringJobs.searchPlaceholder", "Search positions..."),
130
144
  className: "pl-9",
131
- value: F,
145
+ value: L,
132
146
  onChange: (e) => {
133
- I(e.target.value), H(1);
147
+ R(e.target.value), W(1);
134
148
  }
135
149
  })]
136
150
  })]
137
151
  }),
138
- U.length === 0 && /* @__PURE__ */ m(C, {
152
+ G.length === 0 && /* @__PURE__ */ g(T, {
139
153
  illustration: "empty-search",
140
154
  title: "No job postings found",
141
155
  description: "Try adjusting your search or status filter, or create a new job posting."
142
156
  }),
143
- /* @__PURE__ */ m("div", {
157
+ /* @__PURE__ */ g("div", {
144
158
  className: "grid gap-4 md:grid-cols-2 xl:grid-cols-3",
145
- children: W.map((e) => {
146
- let n = Y.get(e.id) ?? [];
147
- return /* @__PURE__ */ h(x, {
159
+ children: K.map((e) => {
160
+ let n = Z.get(e.id) ?? [];
161
+ return /* @__PURE__ */ _(C, {
148
162
  treatment: "glass",
149
163
  role: "button",
150
164
  tabIndex: 0,
151
165
  "aria-label": `View ${e.title}`,
152
166
  className: "flex cursor-pointer flex-col transition-shadow hover:shadow-elevation-2 focus-visible:outline-none focus-visible:shadow-[var(--shadow-focus)]",
153
- onClick: () => G(e.id),
167
+ onClick: () => q(e.id),
154
168
  onKeyDown: (t) => {
155
- (t.key === "Enter" || t.key === " ") && (t.preventDefault(), G(e.id));
169
+ (t.key === "Enter" || t.key === " ") && (t.preventDefault(), q(e.id));
156
170
  },
157
- children: [/* @__PURE__ */ h(y, { children: [/* @__PURE__ */ h("div", {
171
+ children: [/* @__PURE__ */ _(x, { children: [/* @__PURE__ */ _("div", {
158
172
  className: "flex items-start justify-between gap-2",
159
- children: [/* @__PURE__ */ m(b, {
173
+ children: [/* @__PURE__ */ g(S, {
160
174
  className: "text-lg leading-snug",
161
175
  children: e.title
162
- }), /* @__PURE__ */ m(g, {
176
+ }), /* @__PURE__ */ g(v, {
163
177
  variant: e.status === "active" ? "default" : e.status === "draft" ? "secondary" : "outline",
164
178
  children: t(e.status)
165
179
  })]
166
- }), /* @__PURE__ */ h("div", {
180
+ }), /* @__PURE__ */ _("div", {
167
181
  className: "mt-2 flex flex-wrap gap-1.5",
168
- children: [/* @__PURE__ */ m(g, {
182
+ children: [/* @__PURE__ */ g(v, {
169
183
  variant: "outline",
170
184
  className: "text-[10px]",
171
185
  children: t(e.type)
172
- }), /* @__PURE__ */ m(g, {
186
+ }), /* @__PURE__ */ g(v, {
173
187
  variant: "outline",
174
188
  className: "text-[10px]",
175
189
  children: t(e.experienceLevel)
176
190
  })]
177
- })] }), /* @__PURE__ */ h(v, {
191
+ })] }), /* @__PURE__ */ _(b, {
178
192
  className: "flex flex-1 flex-col gap-2.5",
179
193
  children: [
180
- /* @__PURE__ */ m(A, {
181
- label: j("crewfoundry.hiringJobs.card.department", "Department"),
182
- children: /* @__PURE__ */ h("span", {
194
+ /* @__PURE__ */ g(F, {
195
+ label: a("crewfoundry.hiringJobs.card.department", "Department"),
196
+ children: /* @__PURE__ */ _("span", {
183
197
  className: "inline-flex items-center gap-1",
184
- children: [/* @__PURE__ */ m(s, { className: "h-3 w-3 text-text-secondary" }), e.department || "—"]
198
+ children: [/* @__PURE__ */ g(l, { className: "h-3 w-3 text-text-secondary" }), e.department || "—"]
185
199
  })
186
200
  }),
187
- /* @__PURE__ */ m(A, {
188
- label: j("crewfoundry.hiringJobs.card.location", "Location"),
189
- children: /* @__PURE__ */ h("span", {
201
+ /* @__PURE__ */ g(F, {
202
+ label: a("crewfoundry.hiringJobs.card.location", "Location"),
203
+ children: /* @__PURE__ */ _("span", {
190
204
  className: "inline-flex items-center gap-1",
191
- children: [/* @__PURE__ */ m(l, { className: "h-3 w-3 text-text-secondary" }), e.location || "—"]
205
+ children: [/* @__PURE__ */ g(d, { className: "h-3 w-3 text-text-secondary" }), e.location || "—"]
192
206
  })
193
207
  }),
194
- /* @__PURE__ */ m(A, {
195
- label: j("crewfoundry.hiringJobs.card.salary", "Salary"),
196
- children: /* @__PURE__ */ h("span", {
208
+ /* @__PURE__ */ g(F, {
209
+ label: a("crewfoundry.hiringJobs.card.salary", "Salary"),
210
+ children: /* @__PURE__ */ _("span", {
197
211
  className: "inline-flex items-center gap-0.5 tabular-nums",
198
- children: [
199
- /* @__PURE__ */ m(c, { className: "h-3 w-3 text-text-secondary" }),
200
- (e.salaryMin / 1e3).toFixed(0),
201
- "k – $",
202
- (e.salaryMax / 1e3).toFixed(0),
203
- "k"
204
- ]
212
+ children: [/* @__PURE__ */ g(u, { className: "h-3 w-3 text-text-secondary" }), P(e.salaryMin, e.salaryMax)]
205
213
  })
206
214
  }),
207
- /* @__PURE__ */ m(A, {
208
- label: j("crewfoundry.hiringJobs.card.applicantsLabel", "Applicants"),
209
- children: /* @__PURE__ */ h("span", {
215
+ /* @__PURE__ */ g(F, {
216
+ label: a("crewfoundry.hiringJobs.card.applicantsLabel", "Applicants"),
217
+ children: /* @__PURE__ */ _("span", {
210
218
  className: "inline-flex items-center gap-2",
211
- children: [n.length > 0 && /* @__PURE__ */ m(r, {
219
+ children: [n.length > 0 && /* @__PURE__ */ g(r, {
212
220
  people: n,
213
221
  max: 4,
214
222
  size: 22
215
- }), /* @__PURE__ */ h("span", {
223
+ }), /* @__PURE__ */ _("span", {
216
224
  className: "inline-flex items-center gap-1 tabular-nums",
217
- children: [/* @__PURE__ */ m(f, { className: "h-3 w-3 text-text-secondary" }), e.applicants]
225
+ children: [/* @__PURE__ */ g(m, { className: "h-3 w-3 text-text-secondary" }), e.applicants]
218
226
  })]
219
227
  })
220
228
  }),
221
- /* @__PURE__ */ m("div", {
222
- className: "mt-auto pt-1",
223
- children: /* @__PURE__ */ m(A, {
224
- label: j("crewfoundry.hiringJobs.card.posted", "Posted"),
225
- children: /* @__PURE__ */ m("span", {
229
+ /* @__PURE__ */ _("div", {
230
+ className: "mt-auto flex flex-col gap-2.5",
231
+ children: [/* @__PURE__ */ g(F, {
232
+ label: a("crewfoundry.hiringJobs.card.posted", "Posted"),
233
+ children: /* @__PURE__ */ g("span", {
226
234
  className: "text-text-secondary font-normal",
227
- children: k(e.postedDate)
235
+ children: j(e.postedDate)
228
236
  })
229
- })
237
+ }), /* @__PURE__ */ g(F, {
238
+ label: a("crewfoundry.hiringJobs.card.deadline", "Deadline"),
239
+ children: /* @__PURE__ */ g("span", {
240
+ className: "text-text-secondary font-normal",
241
+ children: N(e.deadline)
242
+ })
243
+ })]
230
244
  })
231
245
  ]
232
246
  })]
233
247
  }, e.id);
234
248
  })
235
249
  }),
236
- U.length > B && /* @__PURE__ */ m(E, {
237
- page: z,
238
- pageSize: B,
239
- totalItems: U.length,
240
- onPageChange: H
250
+ G.length > H && /* @__PURE__ */ g(O, {
251
+ page: V,
252
+ pageSize: H,
253
+ totalItems: G.length,
254
+ onPageChange: W
241
255
  })
242
256
  ]
243
257
  });
244
258
  }
245
259
  //#endregion
246
- export { j as HiringJobsPagePage };
260
+ export { I as HiringJobsPagePage };
247
261
 
248
262
  //# sourceMappingURL=HiringJobsPagePage.js.map