@burdenoff/microfe-crewfoundry 2026.1013.2 → 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 (198) 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/marketplace/BookServiceModal.d.ts +14 -0
  12. package/dist/crewfoundry/components/marketplace/BookServiceModal.d.ts.map +1 -0
  13. package/dist/crewfoundry/components/marketplace/BookServiceModal.js +75 -0
  14. package/dist/crewfoundry/components/marketplace/BookServiceModal.js.map +1 -0
  15. package/dist/crewfoundry/components/marketplace/MarketplaceTabs.d.ts +24 -0
  16. package/dist/crewfoundry/components/marketplace/MarketplaceTabs.d.ts.map +1 -0
  17. package/dist/crewfoundry/components/marketplace/MarketplaceTabs.js +47 -0
  18. package/dist/crewfoundry/components/marketplace/MarketplaceTabs.js.map +1 -0
  19. package/dist/crewfoundry/components/marketplace/ServiceListingModal.d.ts +12 -0
  20. package/dist/crewfoundry/components/marketplace/ServiceListingModal.d.ts.map +1 -0
  21. package/dist/crewfoundry/components/marketplace/ServiceListingModal.js +123 -0
  22. package/dist/crewfoundry/components/marketplace/ServiceListingModal.js.map +1 -0
  23. package/dist/crewfoundry/components/viz/CertificateCard.js +4 -118
  24. package/dist/crewfoundry/components/viz/PathSteps.js +3 -68
  25. package/dist/crewfoundry/components/viz/lib/geometry.js +5 -13
  26. package/dist/crewfoundry/components/viz/lib/geometry.js.map +1 -1
  27. package/dist/crewfoundry/lib/analytics.d.ts +1 -6
  28. package/dist/crewfoundry/lib/analytics.d.ts.map +1 -1
  29. package/dist/crewfoundry/lib/analytics.js +3 -18
  30. package/dist/crewfoundry/lib/analytics.js.map +1 -1
  31. package/dist/crewfoundry/lib/companyFinance.d.ts +3 -7
  32. package/dist/crewfoundry/lib/companyFinance.d.ts.map +1 -1
  33. package/dist/crewfoundry/lib/companyFinance.js +3 -8
  34. package/dist/crewfoundry/lib/companyFinance.js.map +1 -1
  35. package/dist/crewfoundry/lib/learning.d.ts +12 -0
  36. package/dist/crewfoundry/lib/learning.d.ts.map +1 -0
  37. package/dist/crewfoundry/lib/learning.js +26 -0
  38. package/dist/crewfoundry/lib/learning.js.map +1 -0
  39. package/dist/crewfoundry/lib/marketplace.d.ts +30 -0
  40. package/dist/crewfoundry/lib/marketplace.d.ts.map +1 -0
  41. package/dist/crewfoundry/lib/marketplace.js +91 -0
  42. package/dist/crewfoundry/lib/marketplace.js.map +1 -0
  43. package/dist/crewfoundry/lib/useEnsureLoaded.d.ts +7 -0
  44. package/dist/crewfoundry/lib/useEnsureLoaded.d.ts.map +1 -0
  45. package/dist/crewfoundry/lib/useEnsureLoaded.js +25 -0
  46. package/dist/crewfoundry/lib/useEnsureLoaded.js.map +1 -0
  47. package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.d.ts.map +1 -1
  48. package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js +12 -25
  49. package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js.map +1 -1
  50. package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.d.ts.map +1 -1
  51. package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js +145 -157
  52. package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js.map +1 -1
  53. package/dist/crewfoundry/pages/CompanyClientManagementPagePage.d.ts.map +1 -1
  54. package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js +14 -14
  55. package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js.map +1 -1
  56. package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.d.ts.map +1 -1
  57. package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js +14 -14
  58. package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js.map +1 -1
  59. package/dist/crewfoundry/pages/HelpApiDocumentationPagePage.d.ts.map +1 -1
  60. package/dist/crewfoundry/pages/HelpApiDocumentationPagePage.js +71 -33
  61. package/dist/crewfoundry/pages/HelpApiDocumentationPagePage.js.map +1 -1
  62. package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.d.ts.map +1 -1
  63. package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js +247 -173
  64. package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js.map +1 -1
  65. package/dist/crewfoundry/pages/LearningCoursesPagePage.d.ts +12 -0
  66. package/dist/crewfoundry/pages/LearningCoursesPagePage.d.ts.map +1 -1
  67. package/dist/crewfoundry/pages/LearningCoursesPagePage.js +307 -293
  68. package/dist/crewfoundry/pages/LearningCoursesPagePage.js.map +1 -1
  69. package/dist/crewfoundry/pages/LearningEnrollmentsPagePage.d.ts +2 -0
  70. package/dist/crewfoundry/pages/LearningEnrollmentsPagePage.d.ts.map +1 -0
  71. package/dist/crewfoundry/pages/LearningEnrollmentsPagePage.js +223 -0
  72. package/dist/crewfoundry/pages/LearningEnrollmentsPagePage.js.map +1 -0
  73. package/dist/crewfoundry/pages/LearningLearningAnalyticsPagePage.d.ts +4 -0
  74. package/dist/crewfoundry/pages/LearningLearningAnalyticsPagePage.d.ts.map +1 -1
  75. package/dist/crewfoundry/pages/LearningLearningAnalyticsPagePage.js +115 -160
  76. package/dist/crewfoundry/pages/LearningLearningAnalyticsPagePage.js.map +1 -1
  77. package/dist/crewfoundry/pages/MarketplaceBookingManagementPagePage.d.ts.map +1 -1
  78. package/dist/crewfoundry/pages/MarketplaceBookingManagementPagePage.js +165 -75
  79. package/dist/crewfoundry/pages/MarketplaceBookingManagementPagePage.js.map +1 -1
  80. package/dist/crewfoundry/pages/MarketplaceEscrowPagePage.d.ts +7 -0
  81. package/dist/crewfoundry/pages/MarketplaceEscrowPagePage.d.ts.map +1 -0
  82. package/dist/crewfoundry/pages/MarketplaceEscrowPagePage.js +175 -0
  83. package/dist/crewfoundry/pages/MarketplaceEscrowPagePage.js.map +1 -0
  84. package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.d.ts.map +1 -1
  85. package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js +182 -223
  86. package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js.map +1 -1
  87. package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.d.ts.map +1 -1
  88. package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.js +168 -227
  89. package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.js.map +1 -1
  90. package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.d.ts.map +1 -1
  91. package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js +127 -290
  92. package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js.map +1 -1
  93. package/dist/crewfoundry/store/crewfoundryStore.d.ts +138 -78
  94. package/dist/crewfoundry/store/crewfoundryStore.d.ts.map +1 -1
  95. package/dist/crewfoundry/store/crewfoundryStore.js +323 -244
  96. package/dist/crewfoundry/store/crewfoundryStore.js.map +1 -1
  97. package/dist/crewfoundry/utils/dateUtils.d.ts +5 -0
  98. package/dist/crewfoundry/utils/dateUtils.d.ts.map +1 -1
  99. package/dist/crewfoundry/utils/dateUtils.js +10 -1
  100. package/dist/crewfoundry/utils/dateUtils.js.map +1 -1
  101. package/dist/generated/global-types.d.ts +5145 -1627
  102. package/dist/generated/global-types.d.ts.map +1 -1
  103. package/dist/generated/wspace-types.d.ts +100373 -34602
  104. package/dist/generated/wspace-types.d.ts.map +1 -1
  105. package/dist/utils/pageContext.d.ts.map +1 -1
  106. package/dist/utils/pageContext.js +0 -1
  107. package/dist/utils/pageContext.js.map +1 -1
  108. package/package.json +1 -1
  109. package/dist/crewfoundry/components/viz/CertificateCard.js.map +0 -1
  110. package/dist/crewfoundry/components/viz/PathSteps.js.map +0 -1
  111. package/dist/crewfoundry/pages/CommunicationsCallsPagePage.d.ts +0 -2
  112. package/dist/crewfoundry/pages/CommunicationsCallsPagePage.d.ts.map +0 -1
  113. package/dist/crewfoundry/pages/CommunicationsCallsPagePage.js +0 -53
  114. package/dist/crewfoundry/pages/CommunicationsCallsPagePage.js.map +0 -1
  115. package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.d.ts +0 -2
  116. package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.d.ts.map +0 -1
  117. package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.js +0 -107
  118. package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.js.map +0 -1
  119. package/dist/crewfoundry/pages/CommunicationsVideoCallsPagePage.d.ts +0 -2
  120. package/dist/crewfoundry/pages/CommunicationsVideoCallsPagePage.d.ts.map +0 -1
  121. package/dist/crewfoundry/pages/CommunicationsVideoCallsPagePage.js +0 -62
  122. package/dist/crewfoundry/pages/CommunicationsVideoCallsPagePage.js.map +0 -1
  123. package/dist/crewfoundry/pages/LearningAssessmentsPagePage.d.ts +0 -2
  124. package/dist/crewfoundry/pages/LearningAssessmentsPagePage.d.ts.map +0 -1
  125. package/dist/crewfoundry/pages/LearningAssessmentsPagePage.js +0 -37
  126. package/dist/crewfoundry/pages/LearningAssessmentsPagePage.js.map +0 -1
  127. package/dist/crewfoundry/pages/LearningCertificatesPagePage.d.ts +0 -2
  128. package/dist/crewfoundry/pages/LearningCertificatesPagePage.d.ts.map +0 -1
  129. package/dist/crewfoundry/pages/LearningCertificatesPagePage.js +0 -111
  130. package/dist/crewfoundry/pages/LearningCertificatesPagePage.js.map +0 -1
  131. package/dist/crewfoundry/pages/LearningCourseLearningPagePage.d.ts +0 -2
  132. package/dist/crewfoundry/pages/LearningCourseLearningPagePage.d.ts.map +0 -1
  133. package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js +0 -309
  134. package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js.map +0 -1
  135. package/dist/crewfoundry/pages/LearningLearningPagePage.d.ts +0 -2
  136. package/dist/crewfoundry/pages/LearningLearningPagePage.d.ts.map +0 -1
  137. package/dist/crewfoundry/pages/LearningLearningPagePage.js +0 -200
  138. package/dist/crewfoundry/pages/LearningLearningPagePage.js.map +0 -1
  139. package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.d.ts +0 -2
  140. package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.d.ts.map +0 -1
  141. package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js +0 -190
  142. package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js.map +0 -1
  143. package/dist/crewfoundry/pages/LearningLearningPathsPagePage.d.ts +0 -2
  144. package/dist/crewfoundry/pages/LearningLearningPathsPagePage.d.ts.map +0 -1
  145. package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js +0 -230
  146. package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js.map +0 -1
  147. package/dist/crewfoundry/pages/LearningMyLearningPagePage.d.ts +0 -2
  148. package/dist/crewfoundry/pages/LearningMyLearningPagePage.d.ts.map +0 -1
  149. package/dist/crewfoundry/pages/LearningMyLearningPagePage.js +0 -293
  150. package/dist/crewfoundry/pages/LearningMyLearningPagePage.js.map +0 -1
  151. package/dist/crewfoundry/pages/MarketplaceEarningsPagePage.d.ts +0 -2
  152. package/dist/crewfoundry/pages/MarketplaceEarningsPagePage.d.ts.map +0 -1
  153. package/dist/crewfoundry/pages/MarketplaceEarningsPagePage.js +0 -163
  154. package/dist/crewfoundry/pages/MarketplaceEarningsPagePage.js.map +0 -1
  155. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.d.ts +0 -2
  156. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.d.ts.map +0 -1
  157. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.js +0 -365
  158. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.js.map +0 -1
  159. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.d.ts +0 -2
  160. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.d.ts.map +0 -1
  161. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js +0 -224
  162. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js.map +0 -1
  163. package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.d.ts +0 -2
  164. package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.d.ts.map +0 -1
  165. package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js +0 -180
  166. package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js.map +0 -1
  167. package/dist/crewfoundry/pages/MarketplaceMessagingPagePage.d.ts +0 -2
  168. package/dist/crewfoundry/pages/MarketplaceMessagingPagePage.d.ts.map +0 -1
  169. package/dist/crewfoundry/pages/MarketplaceMessagingPagePage.js +0 -38
  170. package/dist/crewfoundry/pages/MarketplaceMessagingPagePage.js.map +0 -1
  171. package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.d.ts +0 -2
  172. package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.d.ts.map +0 -1
  173. package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.js +0 -204
  174. package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.js.map +0 -1
  175. package/dist/crewfoundry/pages/PaymentsEscrowPagePage.d.ts +0 -2
  176. package/dist/crewfoundry/pages/PaymentsEscrowPagePage.d.ts.map +0 -1
  177. package/dist/crewfoundry/pages/PaymentsEscrowPagePage.js +0 -55
  178. package/dist/crewfoundry/pages/PaymentsEscrowPagePage.js.map +0 -1
  179. package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.d.ts +0 -2
  180. package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.d.ts.map +0 -1
  181. package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js +0 -335
  182. package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js.map +0 -1
  183. package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.d.ts +0 -2
  184. package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.d.ts.map +0 -1
  185. package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.js +0 -160
  186. package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.js.map +0 -1
  187. package/dist/crewfoundry/pages/PaymentsPaymentMethodsPagePage.d.ts +0 -2
  188. package/dist/crewfoundry/pages/PaymentsPaymentMethodsPagePage.d.ts.map +0 -1
  189. package/dist/crewfoundry/pages/PaymentsPaymentMethodsPagePage.js +0 -80
  190. package/dist/crewfoundry/pages/PaymentsPaymentMethodsPagePage.js.map +0 -1
  191. package/dist/crewfoundry/pages/PaymentsSecurityPagePage.d.ts +0 -2
  192. package/dist/crewfoundry/pages/PaymentsSecurityPagePage.d.ts.map +0 -1
  193. package/dist/crewfoundry/pages/PaymentsSecurityPagePage.js +0 -191
  194. package/dist/crewfoundry/pages/PaymentsSecurityPagePage.js.map +0 -1
  195. package/dist/crewfoundry/utils/courseProgress.d.ts +0 -33
  196. package/dist/crewfoundry/utils/courseProgress.d.ts.map +0 -1
  197. package/dist/crewfoundry/utils/courseProgress.js +0 -46
  198. package/dist/crewfoundry/utils/courseProgress.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"LearningCoursesPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/LearningCoursesPagePage.tsx"],"sourcesContent":["import { useMemo, useState, type ComponentType } from 'react';\nimport {\n Badge,\n Button,\n CardContent,\n CardHeader,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n Input,\n PagePurpose,\n Pagination,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport {\n BarChart3,\n Briefcase,\n Code2,\n GraduationCap,\n Megaphone,\n Palette,\n Play,\n Plus,\n Search,\n ShieldCheck,\n Users,\n} from 'lucide-react';\nimport { useNavigate } from 'react-router';\nimport { useCrewFoundryStore, type LearningCourse } from '../store/crewfoundryStore';\nimport {\n FormModal,\n useFormModal,\n type FieldConfig,\n type FormValues,\n} from '../components/forms/FormModal';\nimport { chartPalette, RatingStars } from '../components/viz';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\nimport { readCourseProgress } from '../utils/courseProgress';\n\nconst COURSE_FIELDS: FieldConfig[] = [\n { name: 'title', label: 'Course Title', type: 'text', required: true, colSpan: 2 },\n { name: 'category', label: 'Category', type: 'text', required: true },\n { name: 'instructor', label: 'Instructor', type: 'text', required: true },\n {\n name: 'duration',\n label: 'Duration',\n type: 'text',\n required: true,\n placeholder: 'e.g. 6 weeks',\n },\n { name: 'modules', label: 'Modules', type: 'number', required: true, placeholder: 'e.g. 8' },\n {\n name: 'level',\n label: 'Level',\n type: 'select',\n required: true,\n options: [\n { label: 'Beginner', value: 'beginner' },\n { label: 'Intermediate', value: 'intermediate' },\n { label: 'Advanced', value: 'advanced' },\n ],\n },\n {\n name: 'status',\n label: 'Status',\n type: 'select',\n required: true,\n options: [\n { label: 'Active', value: 'active' },\n { label: 'Draft', value: 'draft' },\n { label: 'Archived', value: 'archived' },\n ],\n },\n { name: 'description', label: 'Description', type: 'textarea', required: true, colSpan: 2 },\n {\n name: 'skills',\n label: 'Skills',\n type: 'tags',\n placeholder: 'Add a skill and press Enter',\n colSpan: 2,\n },\n];\n\ntype IconType = ComponentType<{ className?: string }>;\n\n/** Deterministic tokenized accent for a category (never a raw color). */\nfunction categoryAccent(category: string): string {\n const palette = chartPalette(8);\n let hash = 0;\n for (let i = 0; i < category.length; i += 1) {\n hash = (hash * 31 + category.charCodeAt(i)) >>> 0;\n }\n return palette[hash % palette.length];\n}\n\nfunction categoryIcon(category: string): IconType {\n const c = category.toLowerCase();\n if (\n c.includes('engineer') ||\n c.includes('develop') ||\n c.includes('program') ||\n c.includes('tech')\n )\n return Code2;\n if (c.includes('design') || c.includes('ux') || c.includes('ui')) return Palette;\n if (c.includes('lead') || c.includes('manage') || c.includes('people') || c.includes('hr'))\n return Users;\n if (c.includes('compliance') || c.includes('security') || c.includes('legal')) return ShieldCheck;\n if (c.includes('data') || c.includes('analyt')) return BarChart3;\n if (c.includes('market') || c.includes('sales')) return Megaphone;\n if (c.includes('business') || c.includes('finance') || c.includes('operations')) return Briefcase;\n return GraduationCap;\n}\n\nfunction levelVariant(level: LearningCourse['level']): 'secondary' | 'default' | 'destructive' {\n return level === 'beginner' ? 'secondary' : level === 'intermediate' ? 'default' : 'destructive';\n}\n\nexport function LearningCoursesPagePage() {\n const { courses, addCourse, enrollCourse } = useCrewFoundryStore();\n const { showToast } = useGlobalUi();\n const navigate = useNavigate();\n const [search, setSearch] = useState('');\n const [category, setCategory] = useState('all');\n const [enrollingId, setEnrollingId] = useState<string | null>(null);\n const createModal = useFormModal();\n const { page, pageSize, offset, setPage } = usePagination({ initialPageSize: 12 });\n\n const categories = useMemo(() => {\n const seen = new Map<string, number>();\n for (const course of courses) {\n const key = course.category.trim() || 'Uncategorized';\n seen.set(key, (seen.get(key) ?? 0) + 1);\n }\n return [...seen.entries()].sort((a, b) => b[1] - a[1]);\n }, [courses]);\n\n const filtered = courses.filter((c) => {\n const matchesCategory =\n category === 'all' || (c.category.trim() || 'Uncategorized') === category;\n const matchesSearch = `${c.title} ${c.category} ${c.instructor} ${c.skills.join(' ')}`\n .toLowerCase()\n .includes(search.toLowerCase());\n return matchesCategory && matchesSearch;\n });\n const paged = filtered.slice(offset, offset + pageSize);\n\n const openCourse = (id: string) =>\n navigate(`../learning/course-details-page?id=${encodeURIComponent(id)}`);\n const openPlayer = (id: string) =>\n navigate(`../learning/course-learning-page?id=${encodeURIComponent(id)}`);\n\n const handleEnroll = async (course: LearningCourse) => {\n setEnrollingId(course.id);\n try {\n await enrollCourse(course.id);\n showToast(`Enrolled in ${course.title}`, 'success');\n openPlayer(course.id);\n } catch (error) {\n showToast(error instanceof Error ? error.message : 'Unable to enroll in course', 'error');\n } finally {\n setEnrollingId(null);\n }\n };\n\n const handleCreate = async (values: FormValues) => {\n await addCourse({\n id: `course-${Date.now()}`,\n title: String(values.title ?? ''),\n category: String(values.category ?? ''),\n instructor: String(values.instructor ?? ''),\n duration: String(values.duration ?? ''),\n level: String(values.level ?? 'beginner') as LearningCourse['level'],\n enrolled: 0,\n completed: 0,\n rating: 0,\n status: String(values.status ?? 'draft') as LearningCourse['status'],\n description: String(values.description ?? ''),\n modules: typeof values.modules === 'number' ? values.modules : Number(values.modules) || 0,\n skills: Array.isArray(values.skills) ? values.skills : [],\n });\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Course Catalog</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {courses.length} courses · {categories.length} categories\n </p>\n <PagePurpose className=\"mt-1\">\n Browse the course catalog — find training to grow your team&apos;s skills and enroll in\n the programs that fit your goals.\n </PagePurpose>\n </div>\n <Button onClick={() => createModal.openModal()}>\n <Plus className=\"mr-1 h-4 w-4\" />\n Create Course\n </Button>\n </div>\n\n <div className=\"flex flex-col gap-3\">\n <div className=\"relative max-w-md\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary\" />\n <Input\n placeholder=\"Search courses, skills, or instructors...\"\n className=\"pl-9\"\n value={search}\n onChange={(e) => {\n setSearch(e.target.value);\n setPage(1);\n }}\n />\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n setCategory('all');\n setPage(1);\n }}\n className={`rounded-full border px-3 py-1 text-xs font-medium transition-colors ${\n category === 'all'\n ? 'border-transparent bg-action-primary-bg text-action-primary-text'\n : 'border-border-subtle bg-bg-surface text-text-secondary hover:text-text-primary'\n }`}\n >\n All\n </button>\n {categories.map(([name, count]) => (\n <button\n key={name}\n type=\"button\"\n onClick={() => {\n setCategory(name);\n setPage(1);\n }}\n className={`inline-flex items-center gap-1.5 rounded-full border px-3 py-1 text-xs font-medium transition-colors ${\n category === name\n ? 'border-transparent bg-action-primary-bg text-action-primary-text'\n : 'border-border-subtle bg-bg-surface text-text-secondary hover:text-text-primary'\n }`}\n >\n {name}\n <span className=\"tabular-nums opacity-70\">{count}</span>\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {filtered.length === 0 ? (\n <div className=\"col-span-full\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No courses found\"\n description=\"Try a different search term or category to find training.\"\n />\n </div>\n ) : (\n paged.map((course) => {\n const accent = categoryAccent(course.category);\n const Icon = categoryIcon(course.category);\n const progress = readCourseProgress(course.id);\n const totalModules = Math.max(course.modules, 1);\n const pct =\n progress.completed.length > 0\n ? Math.min(100, Math.round((progress.completed.length / totalModules) * 100))\n : 0;\n const started = pct > 0;\n return (\n <GlassCard\n key={course.id}\n treatment=\"plain\"\n className=\"group flex flex-col overflow-hidden transition-shadow hover:shadow-md\"\n >\n <button\n type=\"button\"\n aria-label={`View ${course.title}`}\n onClick={() => openCourse(course.id)}\n className=\"relative flex h-24 w-full items-center justify-between px-5 text-left outline-none focus-visible:shadow-[var(--shadow-focus)]\"\n style={{\n background: `linear-gradient(135deg, color-mix(in srgb, ${accent} 28%, var(--color-bg-surface)), color-mix(in srgb, ${accent} 8%, var(--color-bg-surface)))`,\n }}\n >\n <span\n className=\"flex h-11 w-11 items-center justify-center rounded-xl bg-bg-surface/70 shadow-elevation-1\"\n style={{ color: accent }}\n >\n <Icon className=\"h-6 w-6\" />\n </span>\n <Badge variant={levelVariant(course.level)} className=\"text-[10px]\">\n {formatEnumLabel(course.level)}\n </Badge>\n </button>\n <CardHeader className=\"pb-2\">\n <Badge variant=\"outline\" className=\"w-fit text-[10px]\">\n {course.category}\n </Badge>\n <button\n type=\"button\"\n onClick={() => openCourse(course.id)}\n className=\"mt-2 text-left text-base font-semibold outline-none hover:text-action-primary-bg focus-visible:text-action-primary-bg\"\n >\n {course.title}\n </button>\n <p className=\"text-xs text-text-secondary\">\n {course.instructor} · {course.duration} · {course.modules} modules\n </p>\n </CardHeader>\n <CardContent className=\"flex flex-1 flex-col gap-3\">\n <p className=\"line-clamp-2 text-sm text-text-secondary\">{course.description}</p>\n {course.skills.length > 0 && (\n <div className=\"flex flex-wrap gap-1\">\n {course.skills.slice(0, 3).map((s) => (\n <Badge key={s} variant=\"outline\" className=\"text-[9px]\">\n {s}\n </Badge>\n ))}\n </div>\n )}\n <div className=\"mt-auto space-y-3\">\n <div className=\"flex items-center justify-between text-xs text-text-secondary\">\n <RatingStars value={course.rating} size={13} showValue />\n <span className=\"flex items-center gap-1\">\n <Users className=\"h-3 w-3\" />\n {course.enrolled} enrolled\n </span>\n </div>\n {started && (\n <div className=\"space-y-1\">\n <div className=\"flex justify-between text-[11px] font-medium\">\n <span className=\"text-text-secondary\">{pct}% complete</span>\n </div>\n <div className=\"h-1.5 overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-full rounded-full bg-action-primary-bg\"\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n )}\n <Button\n className=\"w-full\"\n variant={started ? 'outline' : 'default'}\n disabled={enrollingId === course.id}\n onClick={() => (started ? openPlayer(course.id) : void handleEnroll(course))}\n >\n <Play className=\"mr-1 h-4 w-4\" />\n {enrollingId === course.id ? 'Enrolling…' : started ? 'Continue' : 'Enroll'}\n </Button>\n </div>\n </CardContent>\n </GlassCard>\n );\n })\n )}\n </div>\n\n {filtered.length > pageSize && (\n <Pagination\n page={page}\n pageSize={pageSize}\n totalItems={filtered.length}\n onPageChange={setPage}\n />\n )}\n\n <FormModal\n open={createModal.open}\n onOpenChange={createModal.onOpenChange}\n title=\"Create Course\"\n description=\"Add a new course to the catalog.\"\n fields={COURSE_FIELDS}\n submitLabel=\"Create Course\"\n onSubmit={handleCreate}\n />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"LearningCoursesPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/LearningCoursesPagePage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n Badge,\n Button,\n CardContent,\n CardHeader,\n CardTitle,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n Input,\n PagePurpose,\n Pagination,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Spinner,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport {\n Archive,\n ArchiveRestore,\n Clock,\n ExternalLink,\n MoreHorizontal,\n Pencil,\n Plus,\n Search,\n Trash2,\n Users,\n} from 'lucide-react';\nimport { useNavigate } from 'react-router';\nimport {\n FormModal,\n useFormModal,\n type FieldConfig,\n type FormValues,\n} from '../components/forms/FormModal';\nimport { SegmentedTabs, StatCard } from '../components/viz';\nimport { InlineError } from '../components/InlineError';\nimport {\n useCrewFoundryStore,\n type CrewTrainingCourse,\n type CrewTrainingCourseInput,\n} from '../store/crewfoundryStore';\nimport { formatDuration, isSafeContentUrl } from '../lib/learning';\nimport { useEnsureLoaded } from '../lib/useEnsureLoaded';\n\nexport const COURSE_FIELDS: FieldConfig[] = [\n { name: 'title', label: 'Course title', type: 'text', required: true, colSpan: 2 },\n { name: 'description', label: 'Description', type: 'textarea', required: true },\n {\n name: 'contentUrl',\n label: 'Content link',\n type: 'text',\n required: true,\n colSpan: 2,\n placeholder: 'https://… (internal doc, LMS or video)',\n },\n {\n name: 'durationMinutes',\n label: 'Duration (minutes)',\n type: 'number',\n min: 0,\n placeholder: 'e.g. 45',\n },\n { name: 'isActive', label: 'Active (open for enrollment)', type: 'switch' },\n {\n name: 'requiredForRoles',\n label: 'Required for roles',\n type: 'tags',\n placeholder: 'Type a role and press Enter',\n },\n];\n\nexport const courseToValues = (course: CrewTrainingCourse): FormValues => ({\n title: course.title,\n description: course.description,\n contentUrl: course.contentUrl,\n durationMinutes: course.durationMinutes ?? '',\n isActive: course.isActive,\n requiredForRoles: course.requiredForRoles,\n});\n\n/** Validate + coerce form values into the course mutation input. Throws a user-facing message. */\nexport const valuesToCourseInput = (values: FormValues): CrewTrainingCourseInput => {\n const contentUrl = String(values.contentUrl ?? '').trim();\n if (!isSafeContentUrl(contentUrl)) {\n throw new Error('Content link must be a full http(s) URL, e.g. https://example.com/course');\n }\n const minutes = Number(values.durationMinutes);\n return {\n title: String(values.title ?? '').trim(),\n description: String(values.description ?? '').trim(),\n contentUrl,\n durationMinutes:\n values.durationMinutes === '' || !Number.isFinite(minutes) ? null : Math.round(minutes),\n isActive: Boolean(values.isActive),\n requiredForRoles: Array.isArray(values.requiredForRoles) ? values.requiredForRoles : [],\n };\n};\n\nexport const LEARNING_TABS = [\n { id: 'courses', label: 'Courses', path: '../learning/courses-page' },\n { id: 'enrollments', label: 'Enrollments', path: '../learning/enrollments-page' },\n { id: 'completion', label: 'Completion', path: '../learning/learning-analytics-page' },\n];\n\nexport function LearningCoursesPagePage() {\n const {\n trainingCourses,\n enrollments,\n trainingCompletion,\n learningLoaded,\n fetchLearning,\n createTrainingCourse,\n updateTrainingCourse,\n removeTrainingCourse,\n } = useCrewFoundryStore();\n const loadError = useEnsureLoaded(learningLoaded, fetchLearning);\n const navigate = useNavigate();\n const modal = useFormModal<CrewTrainingCourse>();\n const [formError, setFormError] = useState<string | undefined>();\n const [actionError, setActionError] = useState<string | null>(null);\n const [search, setSearch] = useState('');\n const [statusFilter, setStatusFilter] = useState<'all' | 'active' | 'inactive'>('active');\n const [deleteTarget, setDeleteTarget] = useState<CrewTrainingCourse | null>(null);\n const { page, pageSize, offset, setPage } = usePagination({ initialPageSize: 12 });\n\n const completionByCourse = useMemo(\n () => new Map(trainingCompletion.map((row) => [row.courseId, row])),\n [trainingCompletion]\n );\n\n const query = search.trim().toLowerCase();\n const filtered = trainingCourses.filter((course) => {\n const matchesSearch =\n query === '' ||\n `${course.title} ${course.description} ${course.requiredForRoles.join(' ')}`\n .toLowerCase()\n .includes(query);\n const matchesStatus =\n statusFilter === 'all' || (statusFilter === 'active' ? course.isActive : !course.isActive);\n return matchesSearch && matchesStatus;\n });\n const paged = filtered.slice(offset, offset + pageSize);\n\n const activeCount = trainingCourses.filter((course) => course.isActive).length;\n const completedCount = enrollments.filter((row) => row.status === 'completed').length;\n const completionRate =\n enrollments.length === 0 ? 0 : Math.round((completedCount / enrollments.length) * 100);\n\n const handleSubmit = async (values: FormValues) => {\n setFormError(undefined);\n try {\n const input = valuesToCourseInput(values);\n if (modal.context) {\n await updateTrainingCourse(modal.context.id, input);\n } else {\n await createTrainingCourse(input);\n }\n } catch (error) {\n setFormError(error instanceof Error ? error.message : 'Unable to save course');\n throw error;\n }\n };\n\n const runAction = async (action: () => Promise<void>) => {\n setActionError(null);\n try {\n await action();\n } catch (error) {\n setActionError(error instanceof Error ? error.message : 'Something went wrong');\n }\n };\n\n const openDetails = (course: CrewTrainingCourse) =>\n navigate(`../learning/course-details-page?id=${encodeURIComponent(course.id)}`);\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Training Courses</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {trainingCourses.length} courses · {activeCount} open for enrollment\n </p>\n <PagePurpose className=\"mt-1\">\n Curate the training your people need — link each course to where its content lives, mark\n which roles must take it, then enroll employees and track their completion.\n </PagePurpose>\n </div>\n <Button\n size=\"sm\"\n onClick={() => {\n setFormError(undefined);\n modal.openModal();\n }}\n >\n <Plus className=\"mr-1 h-4 w-4\" />\n Create Course\n </Button>\n </div>\n\n <SegmentedTabs\n aria-label=\"Learning sections\"\n variant=\"underline\"\n value=\"courses\"\n onChange={(id) => {\n const tab = LEARNING_TABS.find((item) => item.id === id);\n if (tab && id !== 'courses') navigate(tab.path);\n }}\n tabs={LEARNING_TABS.map(({ id, label }) => ({ id, label }))}\n />\n\n <InlineError message={loadError ?? actionError} />\n\n <div className=\"grid gap-4 sm:grid-cols-2 xl:grid-cols-4\">\n <StatCard label=\"Active Courses\" value={activeCount} />\n <StatCard label=\"Enrollments\" value={enrollments.length} />\n <StatCard label=\"Completed\" value={completedCount} />\n <StatCard label=\"Completion Rate\" value={`${completionRate}%`} />\n </div>\n\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n placeholder=\"Search courses or roles...\"\n className=\"pl-9\"\n value={search}\n onChange={(event) => {\n setSearch(event.target.value);\n setPage(1);\n }}\n />\n </div>\n <Select\n value={statusFilter}\n onValueChange={(value) => {\n setStatusFilter(value as typeof statusFilter);\n setPage(1);\n }}\n >\n <SelectTrigger className=\"w-full sm:w-44\" aria-label=\"Filter by status\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"active\">Active</SelectItem>\n <SelectItem value=\"inactive\">Archived</SelectItem>\n <SelectItem value=\"all\">All courses</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n {!learningLoaded && !loadError ? (\n <div className=\"flex min-h-[240px] items-center justify-center\">\n <Spinner size=\"lg\" />\n </div>\n ) : trainingCourses.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No courses yet\"\n description=\"Create a course and link it to its content to start enrolling employees.\"\n action={\n <Button size=\"sm\" onClick={() => modal.openModal()}>\n <Plus className=\"mr-1 h-4 w-4\" />\n Create Course\n </Button>\n }\n />\n ) : filtered.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No courses match\"\n description=\"Try a different search term or status filter.\"\n />\n ) : (\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {paged.map((course) => {\n const completion = completionByCourse.get(course.id);\n return (\n <GlassCard key={course.id} treatment=\"glass\" className=\"flex flex-col\">\n <CardHeader>\n <div className=\"flex items-start justify-between gap-2\">\n <button\n type=\"button\"\n onClick={() => openDetails(course)}\n className=\"min-w-0 text-left\"\n >\n <CardTitle className=\"line-clamp-2 text-base hover:underline\">\n {course.title}\n </CardTitle>\n </button>\n <div className=\"flex items-center gap-1\">\n <Badge\n variant={course.isActive ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {course.isActive ? 'Active' : 'Archived'}\n </Badge>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8\"\n aria-label={`More actions for ${course.title}`}\n >\n <MoreHorizontal className=\"h-4 w-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem\n onSelect={() => {\n setFormError(undefined);\n modal.openModal(course);\n }}\n >\n <Pencil className=\"mr-2 h-4 w-4\" />\n Edit\n </DropdownMenuItem>\n <DropdownMenuItem\n onSelect={() =>\n void runAction(() =>\n updateTrainingCourse(course.id, { isActive: !course.isActive })\n )\n }\n >\n {course.isActive ? (\n <Archive className=\"mr-2 h-4 w-4\" />\n ) : (\n <ArchiveRestore className=\"mr-2 h-4 w-4\" />\n )}\n {course.isActive ? 'Archive' : 'Reactivate'}\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"text-status-error-text\"\n onSelect={() => setDeleteTarget(course)}\n >\n <Trash2 className=\"mr-2 h-4 w-4\" />\n Delete\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"flex flex-1 flex-col gap-3\">\n <p className=\"line-clamp-3 text-sm text-text-secondary\">{course.description}</p>\n {course.requiredForRoles.length > 0 && (\n <div className=\"flex flex-wrap gap-1\">\n {course.requiredForRoles.map((role) => (\n <Badge key={role} variant=\"outline\" className=\"text-[10px]\">\n {role}\n </Badge>\n ))}\n </div>\n )}\n <div className=\"mt-auto flex items-center justify-between border-t border-border-default pt-3 text-xs text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <Clock className=\"h-3.5 w-3.5\" />\n {formatDuration(course.durationMinutes)}\n </span>\n <span className=\"flex items-center gap-1\">\n <Users className=\"h-3.5 w-3.5\" />\n {completion\n ? `${completion.completedCount}/${completion.totalEnrolled} completed`\n : 'No enrollments'}\n </span>\n </div>\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"flex-1\"\n onClick={() => openDetails(course)}\n >\n Manage\n </Button>\n <Button size=\"sm\" variant=\"ghost\" asChild>\n <a href={course.contentUrl} target=\"_blank\" rel=\"noopener noreferrer\">\n <ExternalLink className=\"mr-1 h-3.5 w-3.5\" />\n Open\n </a>\n </Button>\n </div>\n </CardContent>\n </GlassCard>\n );\n })}\n </div>\n )}\n {filtered.length > pageSize && (\n <Pagination\n page={page}\n pageSize={pageSize}\n totalItems={filtered.length}\n onPageChange={setPage}\n />\n )}\n\n <FormModal\n open={modal.open}\n onOpenChange={modal.onOpenChange}\n title={modal.context ? 'Edit course' : 'Create course'}\n description=\"Courses link to content hosted elsewhere; CrewFoundry tracks who is enrolled and who has completed them.\"\n fields={COURSE_FIELDS}\n initialValues={modal.context ? courseToValues(modal.context) : { isActive: true }}\n submitLabel={modal.context ? 'Save changes' : 'Create course'}\n errorMessage={formError}\n onValidationError={setFormError}\n onSubmit={handleSubmit}\n />\n\n <AlertDialog\n open={deleteTarget !== null}\n onOpenChange={(open) => {\n if (!open) setDeleteTarget(null);\n }}\n >\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Delete course?</AlertDialogTitle>\n <AlertDialogDescription>\n {deleteTarget\n ? `“${deleteTarget.title}” is removed from the catalog. Existing enrollment records are kept for your history. To keep the course but stop new enrollments, archive it instead.`\n : ''}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Cancel</AlertDialogCancel>\n <AlertDialogAction\n onClick={(event) => {\n event.preventDefault();\n const target = deleteTarget;\n setDeleteTarget(null);\n if (target) void runAction(() => removeTrainingCourse(target.id));\n }}\n >\n Delete\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AA+DA,IAAa,IAA+B;CAC1C;EAAE,MAAM;EAAS,OAAO;EAAgB,MAAM;EAAQ,UAAU;EAAM,SAAS;EAAG;CAClF;EAAE,MAAM;EAAe,OAAO;EAAe,MAAM;EAAY,UAAU;EAAM;CAC/E;EACE,MAAM;EACN,OAAO;EACP,MAAM;EACN,UAAU;EACV,SAAS;EACT,aAAa;EACd;CACD;EACE,MAAM;EACN,OAAO;EACP,MAAM;EACN,KAAK;EACL,aAAa;EACd;CACD;EAAE,MAAM;EAAY,OAAO;EAAgC,MAAM;EAAU;CAC3E;EACE,MAAM;EACN,OAAO;EACP,MAAM;EACN,aAAa;EACd;CACF,EAEY,KAAkB,OAA4C;CACzE,OAAO,EAAO;CACd,aAAa,EAAO;CACpB,YAAY,EAAO;CACnB,iBAAiB,EAAO,mBAAmB;CAC3C,UAAU,EAAO;CACjB,kBAAkB,EAAO;CAC1B,GAGY,KAAuB,MAAgD;CAClF,IAAM,IAAa,OAAO,EAAO,cAAc,GAAG,CAAC,MAAM;AACzD,KAAI,CAAC,EAAiB,EAAW,CAC/B,OAAU,MAAM,2EAA2E;CAE7F,IAAM,IAAU,OAAO,EAAO,gBAAgB;AAC9C,QAAO;EACL,OAAO,OAAO,EAAO,SAAS,GAAG,CAAC,MAAM;EACxC,aAAa,OAAO,EAAO,eAAe,GAAG,CAAC,MAAM;EACpD;EACA,iBACE,EAAO,oBAAoB,MAAM,CAAC,OAAO,SAAS,EAAQ,GAAG,OAAO,KAAK,MAAM,EAAQ;EACzF,UAAU,EAAQ,EAAO;EACzB,kBAAkB,MAAM,QAAQ,EAAO,iBAAiB,GAAG,EAAO,mBAAmB,EAAE;EACxF;GAGU,IAAgB;CAC3B;EAAE,IAAI;EAAW,OAAO;EAAW,MAAM;EAA4B;CACrE;EAAE,IAAI;EAAe,OAAO;EAAe,MAAM;EAAgC;CACjF;EAAE,IAAI;EAAc,OAAO;EAAc,MAAM;EAAuC;CACvF;AAED,SAAgB,IAA0B;CACxC,IAAM,EACJ,oBACA,gBACA,uBACA,mBACA,mBACA,yBACA,yBACA,6BACE,GAAqB,EACnB,IAAY,GAAgB,GAAgB,GAAc,EAC1D,IAAW,IAAa,EACxB,IAAQ,IAAkC,EAC1C,CAAC,IAAW,KAAgB,GAA8B,EAC1D,CAAC,IAAa,KAAkB,EAAwB,KAAK,EAC7D,CAAC,GAAQ,MAAa,EAAS,GAAG,EAClC,CAAC,GAAc,MAAmB,EAAwC,SAAS,EACnF,CAAC,GAAc,KAAmB,EAAoC,KAAK,EAC3E,EAAE,UAAM,aAAU,WAAQ,eAAY,EAAc,EAAE,iBAAiB,IAAI,CAAC,EAE5E,KAAqB,QACnB,IAAI,IAAI,EAAmB,KAAK,MAAQ,CAAC,EAAI,UAAU,EAAI,CAAC,CAAC,EACnE,CAAC,EAAmB,CACrB,EAEK,IAAQ,EAAO,MAAM,CAAC,aAAa,EACnC,IAAW,EAAgB,QAAQ,MAAW;EAClD,IAAM,IACJ,MAAU,MACV,GAAG,EAAO,MAAM,GAAG,EAAO,YAAY,GAAG,EAAO,iBAAiB,KAAK,IAAI,GACvE,aAAa,CACb,SAAS,EAAM,EACd,IACJ,MAAiB,UAAU,MAAiB,WAAW,EAAO,WAAW,CAAC,EAAO;AACnF,SAAO,KAAiB;GACxB,EACI,KAAQ,EAAS,MAAM,GAAQ,IAAS,EAAS,EAEjD,IAAc,EAAgB,QAAQ,MAAW,EAAO,SAAS,CAAC,QAClE,IAAiB,EAAY,QAAQ,MAAQ,EAAI,WAAW,YAAY,CAAC,QACzE,KACJ,EAAY,WAAW,IAAI,IAAI,KAAK,MAAO,IAAiB,EAAY,SAAU,IAAI,EAElF,KAAe,OAAO,MAAuB;AACjD,IAAa,KAAA,EAAU;AACvB,MAAI;GACF,IAAM,IAAQ,EAAoB,EAAO;AACzC,GAAI,EAAM,UACR,MAAM,EAAqB,EAAM,QAAQ,IAAI,EAAM,GAEnD,MAAM,EAAqB,EAAM;WAE5B,GAAO;AAEd,SADA,EAAa,aAAiB,QAAQ,EAAM,UAAU,wBAAwB,EACxE;;IAIJ,IAAY,OAAO,MAAgC;AACvD,IAAe,KAAK;AACpB,MAAI;AACF,SAAM,GAAQ;WACP,GAAO;AACd,KAAe,aAAiB,QAAQ,EAAM,UAAU,uBAAuB;;IAI7E,KAAe,MACnB,EAAS,sCAAsC,mBAAmB,EAAO,GAAG,GAAG;AAEjF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA;KACE,kBAAC,MAAD;MAAI,WAAU;gBACZ,kBAAC,GAAD,EAAA,UAAc,oBAA+B,CAAA;MAC1C,CAAA;KACL,kBAAC,KAAD;MAAG,WAAU;gBAAb;OACG,EAAgB;OAAO;OAAY;OAAY;OAC9C;;KACJ,kBAAC,GAAD;MAAa,WAAU;gBAAO;MAGhB,CAAA;KACV,EAAA,CAAA,EACN,kBAAC,GAAD;KACE,MAAK;KACL,eAAe;AAEb,MADA,EAAa,KAAA,EAAU,EACvB,EAAM,WAAW;;eAJrB,CAOE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,gBAE1B;OACL;;GAEN,kBAAC,GAAD;IACE,cAAW;IACX,SAAQ;IACR,OAAM;IACN,WAAW,MAAO;KAChB,IAAM,IAAM,EAAc,MAAM,MAAS,EAAK,OAAO,EAAG;AACxD,KAAI,KAAO,MAAO,aAAW,EAAS,EAAI,KAAK;;IAEjD,MAAM,EAAc,KAAK,EAAE,OAAI,gBAAa;KAAE;KAAI;KAAO,EAAE;IAC3D,CAAA;GAEF,kBAAC,IAAD,EAAa,SAAS,KAAa,IAAe,CAAA;GAElD,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MAAU,OAAM;MAAiB,OAAO;MAAe,CAAA;KACvD,kBAAC,GAAD;MAAU,OAAM;MAAc,OAAO,EAAY;MAAU,CAAA;KAC3D,kBAAC,GAAD;MAAU,OAAM;MAAY,OAAO;MAAkB,CAAA;KACrD,kBAAC,GAAD;MAAU,OAAM;MAAkB,OAAO,GAAG,GAAe;MAAM,CAAA;KAC7D;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,IAAD,EAAQ,WAAU,wEAAyE,CAAA,EAC3F,kBAAC,GAAD;MACE,aAAY;MACZ,WAAU;MACV,OAAO;MACP,WAAW,MAAU;AAEnB,OADA,GAAU,EAAM,OAAO,MAAM,EAC7B,EAAQ,EAAE;;MAEZ,CAAA,CACE;QACN,kBAAC,GAAD;KACE,OAAO;KACP,gBAAgB,MAAU;AAExB,MADA,GAAgB,EAA6B,EAC7C,EAAQ,EAAE;;eAJd,CAOE,kBAAC,GAAD;MAAe,WAAU;MAAiB,cAAW;gBACnD,kBAAC,GAAD,EAAe,CAAA;MACD,CAAA,EAChB,kBAAC,GAAD,EAAA,UAAA;MACE,kBAAC,GAAD;OAAY,OAAM;iBAAS;OAAmB,CAAA;MAC9C,kBAAC,GAAD;OAAY,OAAM;iBAAW;OAAqB,CAAA;MAClD,kBAAC,GAAD;OAAY,OAAM;iBAAM;OAAwB,CAAA;MAClC,EAAA,CAAA,CACT;OACL;;GAEL,CAAC,KAAkB,CAAC,IACnB,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD,EAAS,MAAK,MAAO,CAAA;IACjB,CAAA,GACJ,EAAgB,WAAW,IAC7B,kBAAC,GAAD;IACE,cAAa;IACb,OAAM;IACN,aAAY;IACZ,QACE,kBAAC,GAAD;KAAQ,MAAK;KAAK,eAAe,EAAM,WAAW;eAAlD,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,gBAE1B;;IAEX,CAAA,GACA,EAAS,WAAW,IACtB,kBAAC,GAAD;IACE,cAAa;IACb,OAAM;IACN,aAAY;IACZ,CAAA,GAEF,kBAAC,OAAD;IAAK,WAAU;cACZ,GAAM,KAAK,MAAW;KACrB,IAAM,IAAa,GAAmB,IAAI,EAAO,GAAG;AACpD,YACE,kBAAC,GAAD;MAA2B,WAAU;MAAQ,WAAU;gBAAvD,CACE,kBAAC,IAAD,EAAA,UACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAY,EAAO;QAClC,WAAU;kBAEV,kBAAC,IAAD;SAAW,WAAU;mBAClB,EAAO;SACE,CAAA;QACL,CAAA,EACT,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SACE,SAAS,EAAO,WAAW,YAAY;SACvC,WAAU;mBAET,EAAO,WAAW,WAAW;SACxB,CAAA,EACR,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,GAAD;SAAqB,SAAA;mBACnB,kBAAC,GAAD;UACE,SAAQ;UACR,MAAK;UACL,WAAU;UACV,cAAY,oBAAoB,EAAO;oBAEvC,kBAAC,IAAD,EAAgB,WAAU,WAAY,CAAA;UAC/B,CAAA;SACW,CAAA,EACtB,kBAAC,IAAD;SAAqB,OAAM;mBAA3B;UACE,kBAAC,GAAD;WACE,gBAAgB;AAEd,YADA,EAAa,KAAA,EAAU,EACvB,EAAM,UAAU,EAAO;;qBAH3B,CAME,kBAAC,IAAD,EAAQ,WAAU,gBAAiB,CAAA,EAAA,OAElB;;UACnB,kBAAC,GAAD;WACE,gBACE,KAAK,QACH,EAAqB,EAAO,IAAI,EAAE,UAAU,CAAC,EAAO,UAAU,CAAC,CAChE;qBAJL,CAOG,EAAO,WACN,kBAAC,IAAD,EAAS,WAAU,gBAAiB,CAAA,GAEpC,kBAAC,IAAD,EAAgB,WAAU,gBAAiB,CAAA,EAE5C,EAAO,WAAW,YAAY,aACd;;UACnB,kBAAC,IAAD,EAAyB,CAAA;UACzB,kBAAC,GAAD;WACE,WAAU;WACV,gBAAgB,EAAgB,EAAO;qBAFzC,CAIE,kBAAC,IAAD,EAAQ,WAAU,gBAAiB,CAAA,EAAA,SAElB;;UACC;WACT,EAAA,CAAA,CACX;UACF;UACK,CAAA,EACb,kBAAC,IAAD;OAAa,WAAU;iBAAvB;QACE,kBAAC,KAAD;SAAG,WAAU;mBAA4C,EAAO;SAAgB,CAAA;QAC/E,EAAO,iBAAiB,SAAS,KAChC,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAO,iBAAiB,KAAK,MAC5B,kBAAC,GAAD;UAAkB,SAAQ;UAAU,WAAU;oBAC3C;UACK,EAFI,EAEJ,CACR;SACE,CAAA;QAER,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD;UAAM,WAAU;oBAAhB,CACE,kBAAC,IAAD,EAAO,WAAU,eAAgB,CAAA,EAChC,GAAe,EAAO,gBAAgB,CAClC;aACP,kBAAC,QAAD;UAAM,WAAU;oBAAhB,CACE,kBAAC,IAAD,EAAO,WAAU,eAAgB,CAAA,EAChC,IACG,GAAG,EAAW,eAAe,GAAG,EAAW,cAAc,cACzD,iBACC;YACH;;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UACE,MAAK;UACL,SAAQ;UACR,WAAU;UACV,eAAe,EAAY,EAAO;oBACnC;UAEQ,CAAA,EACT,kBAAC,GAAD;UAAQ,MAAK;UAAK,SAAQ;UAAQ,SAAA;oBAChC,kBAAC,KAAD;WAAG,MAAM,EAAO;WAAY,QAAO;WAAS,KAAI;qBAAhD,CACE,kBAAC,IAAD,EAAc,WAAU,oBAAqB,CAAA,EAAA,OAE3C;;UACG,CAAA,CACL;;QACM;SACJ;QA3GI,EAAO,GA2GX;MAEd;IACE,CAAA;GAEP,EAAS,SAAS,KACjB,kBAAC,GAAD;IACQ;IACI;IACV,YAAY,EAAS;IACrB,cAAc;IACd,CAAA;GAGJ,kBAAC,IAAD;IACE,MAAM,EAAM;IACZ,cAAc,EAAM;IACpB,OAAO,EAAM,UAAU,gBAAgB;IACvC,aAAY;IACZ,QAAQ;IACR,eAAe,EAAM,UAAU,EAAe,EAAM,QAAQ,GAAG,EAAE,UAAU,IAAM;IACjF,aAAa,EAAM,UAAU,iBAAiB;IAC9C,cAAc;IACd,mBAAmB;IACnB,UAAU;IACV,CAAA;GAEF,kBAAC,GAAD;IACE,MAAM,MAAiB;IACvB,eAAe,MAAS;AACtB,KAAK,KAAM,EAAgB,KAAK;;cAGlC,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAkB,kBAAiC,CAAA,EACnD,kBAAC,IAAD,EAAA,UACG,IACG,IAAI,EAAa,MAAM,0JACvB,IACmB,CAAA,CACP,EAAA,CAAA,EACpB,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAmB,UAA0B,CAAA,EAC7C,kBAAC,IAAD;KACE,UAAU,MAAU;AAClB,QAAM,gBAAgB;MACtB,IAAM,IAAS;AAEf,MADA,EAAgB,KAAK,EACjB,KAAa,QAAgB,GAAqB,EAAO,GAAG,CAAC;;eAEpE;KAEmB,CAAA,CACF,EAAA,CAAA,CACD,EAAA,CAAA;IACT,CAAA;GACV"}
@@ -0,0 +1,2 @@
1
+ export declare function LearningEnrollmentsPagePage(): import("react").JSX.Element;
2
+ //# sourceMappingURL=LearningEnrollmentsPagePage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LearningEnrollmentsPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/LearningEnrollmentsPagePage.tsx"],"names":[],"mappings":"AAkCA,wBAAgB,2BAA2B,gCA+O1C"}
@@ -0,0 +1,223 @@
1
+ import { useCrewFoundryStore as e } from "../store/crewfoundryStore.js";
2
+ import { AvatarStack as t } from "../components/viz/AvatarStack.js";
3
+ import { SegmentedTabs as n } from "../components/viz/SegmentedTabs.js";
4
+ import "../components/viz/index.js";
5
+ import { formatDate as r } from "../utils/dateUtils.js";
6
+ import { InlineError as i } from "../components/InlineError.js";
7
+ import { ENROLLMENT_STATUS_OPTIONS as a } from "../lib/learning.js";
8
+ import { useEnsureLoaded as o } from "../lib/useEnsureLoaded.js";
9
+ import { LEARNING_TABS as s } from "./LearningCoursesPagePage.js";
10
+ import { useMemo as c, useState as l } from "react";
11
+ import { Search as u, Trash2 as d } from "lucide-react";
12
+ import { useNavigate as f } from "react-router";
13
+ import { jsx as p, jsxs as m } from "react/jsx-runtime";
14
+ import { Button as h, Card as g, CardContent as _, GradientText as v, IllustratedEmptyState as y, Input as b, PagePurpose as x, Pagination as S, ResponsiveTable as C, Select as w, SelectContent as T, SelectItem as E, SelectTrigger as D, SelectValue as O, Spinner as ee } from "@burdenoff/fe-libs/ui";
15
+ import { usePagination as k } from "@burdenoff/fe-libs/shared/hooks";
16
+ //#region src/crewfoundry/pages/LearningEnrollmentsPagePage.tsx
17
+ function A() {
18
+ let A = f(), { enrollments: j, trainingCourses: M, learningLoaded: N, fetchLearning: P, updateEnrollmentStatus: F, removeEnrollment: I } = e(), L = o(N, P), [R, z] = l(null), [B, V] = l(""), [H, U] = l("all"), [W, G] = l("all"), { page: K, pageSize: q, offset: J, setPage: Y } = k({ initialPageSize: 20 }), X = B.trim().toLowerCase(), Z = c(() => j.filter((e) => (H === "all" || e.status === H) && (W === "all" || e.courseId === W) && (X === "" || `${e.employeeName} ${e.courseTitle}`.toLowerCase().includes(X))), [
19
+ j,
20
+ H,
21
+ W,
22
+ X
23
+ ]), Q = Z.slice(J, J + q), $ = async (e) => {
24
+ z(null);
25
+ try {
26
+ await e();
27
+ } catch (e) {
28
+ z(e instanceof Error ? e.message : "Something went wrong");
29
+ }
30
+ };
31
+ return /* @__PURE__ */ m("div", {
32
+ className: "space-y-6 p-6",
33
+ children: [
34
+ /* @__PURE__ */ m("div", { children: [
35
+ /* @__PURE__ */ p("h1", {
36
+ className: "text-2xl font-semibold",
37
+ children: /* @__PURE__ */ p(v, { children: "Enrollments" })
38
+ }),
39
+ /* @__PURE__ */ m("p", {
40
+ className: "text-sm text-text-secondary",
41
+ children: [
42
+ j.length,
43
+ " enrollments across ",
44
+ M.length,
45
+ " courses"
46
+ ]
47
+ }),
48
+ /* @__PURE__ */ p(x, {
49
+ className: "mt-1",
50
+ children: "Track every employee’s progress through their assigned training and update it as they start and complete courses."
51
+ })
52
+ ] }),
53
+ /* @__PURE__ */ p(n, {
54
+ "aria-label": "Learning sections",
55
+ variant: "underline",
56
+ value: "enrollments",
57
+ onChange: (e) => {
58
+ let t = s.find((t) => t.id === e);
59
+ t && e !== "enrollments" && A(t.path);
60
+ },
61
+ tabs: s.map(({ id: e, label: t }) => ({
62
+ id: e,
63
+ label: t
64
+ }))
65
+ }),
66
+ /* @__PURE__ */ p(i, { message: L ?? R }),
67
+ /* @__PURE__ */ m("div", {
68
+ className: "flex flex-col gap-2 md:flex-row md:items-center",
69
+ children: [
70
+ /* @__PURE__ */ m("div", {
71
+ className: "relative flex-1",
72
+ children: [/* @__PURE__ */ p(u, { className: "absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ p(b, {
73
+ placeholder: "Search employee or course...",
74
+ className: "pl-9",
75
+ value: B,
76
+ onChange: (e) => {
77
+ V(e.target.value), Y(1);
78
+ }
79
+ })]
80
+ }),
81
+ /* @__PURE__ */ m(w, {
82
+ value: W,
83
+ onValueChange: (e) => {
84
+ G(e), Y(1);
85
+ },
86
+ children: [/* @__PURE__ */ p(D, {
87
+ className: "w-full md:w-56",
88
+ "aria-label": "Filter by course",
89
+ children: /* @__PURE__ */ p(O, {})
90
+ }), /* @__PURE__ */ m(T, { children: [/* @__PURE__ */ p(E, {
91
+ value: "all",
92
+ children: "All courses"
93
+ }), M.map((e) => /* @__PURE__ */ p(E, {
94
+ value: e.id,
95
+ children: e.title
96
+ }, e.id))] })]
97
+ }),
98
+ /* @__PURE__ */ m(w, {
99
+ value: H,
100
+ onValueChange: (e) => {
101
+ U(e), Y(1);
102
+ },
103
+ children: [/* @__PURE__ */ p(D, {
104
+ className: "w-full md:w-40",
105
+ "aria-label": "Filter by status",
106
+ children: /* @__PURE__ */ p(O, {})
107
+ }), /* @__PURE__ */ m(T, { children: [/* @__PURE__ */ p(E, {
108
+ value: "all",
109
+ children: "All statuses"
110
+ }), a.map((e) => /* @__PURE__ */ p(E, {
111
+ value: e.value,
112
+ children: e.label
113
+ }, e.value))] })]
114
+ })
115
+ ]
116
+ }),
117
+ !N && !L ? /* @__PURE__ */ p("div", {
118
+ className: "flex min-h-[240px] items-center justify-center",
119
+ children: /* @__PURE__ */ p(ee, { size: "lg" })
120
+ }) : j.length === 0 ? /* @__PURE__ */ p(y, {
121
+ illustration: "empty-people",
122
+ title: "No enrollments yet",
123
+ description: "Open a course and enroll employees to start tracking their training.",
124
+ action: /* @__PURE__ */ p(h, {
125
+ size: "sm",
126
+ onClick: () => A("../learning/courses-page"),
127
+ children: "Browse courses"
128
+ })
129
+ }) : /* @__PURE__ */ p(g, { children: /* @__PURE__ */ m(_, {
130
+ className: "space-y-4 pt-6",
131
+ children: [/* @__PURE__ */ p(C, {
132
+ data: Q,
133
+ rowKey: (e) => e.id,
134
+ columns: [
135
+ {
136
+ key: "employee",
137
+ header: "Employee",
138
+ priority: "primary",
139
+ cell: (e) => /* @__PURE__ */ m("div", {
140
+ className: "flex items-center gap-2.5",
141
+ children: [/* @__PURE__ */ p(t, {
142
+ people: [{ name: e.employeeName }],
143
+ size: 28
144
+ }), /* @__PURE__ */ p("span", {
145
+ className: "font-medium",
146
+ children: e.employeeName
147
+ })]
148
+ })
149
+ },
150
+ {
151
+ key: "course",
152
+ header: "Course",
153
+ cell: (e) => /* @__PURE__ */ p("button", {
154
+ type: "button",
155
+ className: "text-left hover:underline",
156
+ onClick: () => A(`../learning/course-details-page?id=${encodeURIComponent(e.courseId)}`),
157
+ children: e.courseTitle
158
+ })
159
+ },
160
+ {
161
+ key: "status",
162
+ header: "Status",
163
+ cell: (e) => /* @__PURE__ */ m(w, {
164
+ value: e.status,
165
+ onValueChange: (t) => void $(() => F(e.id, t)),
166
+ children: [/* @__PURE__ */ p(D, {
167
+ className: "h-8 w-36",
168
+ "aria-label": `Status for ${e.employeeName}`,
169
+ children: /* @__PURE__ */ p(O, {})
170
+ }), /* @__PURE__ */ p(T, { children: a.map((e) => /* @__PURE__ */ p(E, {
171
+ value: e.value,
172
+ children: e.label
173
+ }, e.value)) })]
174
+ })
175
+ },
176
+ {
177
+ key: "enrolled",
178
+ header: "Enrolled",
179
+ cell: (e) => /* @__PURE__ */ p("span", {
180
+ className: "text-text-secondary",
181
+ children: r(e.createdAt)
182
+ })
183
+ },
184
+ {
185
+ key: "completed",
186
+ header: "Completed",
187
+ cell: (e) => /* @__PURE__ */ p("span", {
188
+ className: "text-text-secondary",
189
+ children: r(e.completedAt)
190
+ })
191
+ },
192
+ {
193
+ key: "actions",
194
+ header: "",
195
+ cellClassName: "md:text-right",
196
+ cell: (e) => /* @__PURE__ */ p(h, {
197
+ variant: "ghost",
198
+ size: "icon",
199
+ className: "h-8 w-8",
200
+ "aria-label": `Remove ${e.employeeName} from ${e.courseTitle}`,
201
+ onClick: () => void $(() => I(e.id)),
202
+ children: /* @__PURE__ */ p(d, { className: "h-4 w-4" })
203
+ })
204
+ }
205
+ ],
206
+ emptyState: /* @__PURE__ */ p("p", {
207
+ className: "py-6 text-center text-sm text-text-secondary",
208
+ children: "No enrollments match these filters."
209
+ })
210
+ }), Z.length > q && /* @__PURE__ */ p(S, {
211
+ page: K,
212
+ pageSize: q,
213
+ totalItems: Z.length,
214
+ onPageChange: Y
215
+ })]
216
+ }) })
217
+ ]
218
+ });
219
+ }
220
+ //#endregion
221
+ export { A as LearningEnrollmentsPagePage };
222
+
223
+ //# sourceMappingURL=LearningEnrollmentsPagePage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LearningEnrollmentsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/LearningEnrollmentsPagePage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n Button,\n Card,\n CardContent,\n GradientText,\n IllustratedEmptyState,\n Input,\n PagePurpose,\n Pagination,\n ResponsiveTable,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Spinner,\n type ResponsiveTableColumn,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { Search, Trash2 } from 'lucide-react';\nimport { useNavigate } from 'react-router';\nimport { InlineError } from '../components/InlineError';\nimport { AvatarStack, SegmentedTabs } from '../components/viz';\nimport {\n useCrewFoundryStore,\n type CrewEnrollment,\n type CrewEnrollmentStatus,\n} from '../store/crewfoundryStore';\nimport { ENROLLMENT_STATUS_OPTIONS } from '../lib/learning';\nimport { useEnsureLoaded } from '../lib/useEnsureLoaded';\nimport { formatDate } from '../utils/dateUtils';\nimport { LEARNING_TABS } from './LearningCoursesPagePage';\n\nexport function LearningEnrollmentsPagePage() {\n const navigate = useNavigate();\n const {\n enrollments,\n trainingCourses,\n learningLoaded,\n fetchLearning,\n updateEnrollmentStatus,\n removeEnrollment,\n } = useCrewFoundryStore();\n const loadError = useEnsureLoaded(learningLoaded, fetchLearning);\n const [actionError, setActionError] = useState<string | null>(null);\n const [search, setSearch] = useState('');\n const [statusFilter, setStatusFilter] = useState<'all' | CrewEnrollmentStatus>('all');\n const [courseFilter, setCourseFilter] = useState('all');\n const { page, pageSize, offset, setPage } = usePagination({ initialPageSize: 20 });\n\n const query = search.trim().toLowerCase();\n const filtered = useMemo(\n () =>\n enrollments.filter(\n (row) =>\n (statusFilter === 'all' || row.status === statusFilter) &&\n (courseFilter === 'all' || row.courseId === courseFilter) &&\n (query === '' || `${row.employeeName} ${row.courseTitle}`.toLowerCase().includes(query))\n ),\n [enrollments, statusFilter, courseFilter, query]\n );\n const paged = filtered.slice(offset, offset + pageSize);\n\n const runAction = async (action: () => Promise<void>) => {\n setActionError(null);\n try {\n await action();\n } catch (error) {\n setActionError(error instanceof Error ? error.message : 'Something went wrong');\n }\n };\n\n const columns: ResponsiveTableColumn<CrewEnrollment>[] = [\n {\n key: 'employee',\n header: 'Employee',\n priority: 'primary',\n cell: (row) => (\n <div className=\"flex items-center gap-2.5\">\n <AvatarStack people={[{ name: row.employeeName }]} size={28} />\n <span className=\"font-medium\">{row.employeeName}</span>\n </div>\n ),\n },\n {\n key: 'course',\n header: 'Course',\n cell: (row) => (\n <button\n type=\"button\"\n className=\"text-left hover:underline\"\n onClick={() =>\n navigate(`../learning/course-details-page?id=${encodeURIComponent(row.courseId)}`)\n }\n >\n {row.courseTitle}\n </button>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n cell: (row) => (\n <Select\n value={row.status}\n onValueChange={(value) =>\n void runAction(() => updateEnrollmentStatus(row.id, value as CrewEnrollmentStatus))\n }\n >\n <SelectTrigger className=\"h-8 w-36\" aria-label={`Status for ${row.employeeName}`}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {ENROLLMENT_STATUS_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n ),\n },\n {\n key: 'enrolled',\n header: 'Enrolled',\n cell: (row) => <span className=\"text-text-secondary\">{formatDate(row.createdAt)}</span>,\n },\n {\n key: 'completed',\n header: 'Completed',\n cell: (row) => <span className=\"text-text-secondary\">{formatDate(row.completedAt)}</span>,\n },\n {\n key: 'actions',\n header: '',\n cellClassName: 'md:text-right',\n cell: (row) => (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8\"\n aria-label={`Remove ${row.employeeName} from ${row.courseTitle}`}\n onClick={() => void runAction(() => removeEnrollment(row.id))}\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n ),\n },\n ];\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Enrollments</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {enrollments.length} enrollments across {trainingCourses.length} courses\n </p>\n <PagePurpose className=\"mt-1\">\n Track every employee’s progress through their assigned training and update it as they\n start and complete courses.\n </PagePurpose>\n </div>\n\n <SegmentedTabs\n aria-label=\"Learning sections\"\n variant=\"underline\"\n value=\"enrollments\"\n onChange={(id) => {\n const tab = LEARNING_TABS.find((item) => item.id === id);\n if (tab && id !== 'enrollments') navigate(tab.path);\n }}\n tabs={LEARNING_TABS.map(({ id, label }) => ({ id, label }))}\n />\n\n <InlineError message={loadError ?? actionError} />\n\n <div className=\"flex flex-col gap-2 md:flex-row md:items-center\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n placeholder=\"Search employee or course...\"\n className=\"pl-9\"\n value={search}\n onChange={(event) => {\n setSearch(event.target.value);\n setPage(1);\n }}\n />\n </div>\n <Select\n value={courseFilter}\n onValueChange={(value) => {\n setCourseFilter(value);\n setPage(1);\n }}\n >\n <SelectTrigger className=\"w-full md:w-56\" aria-label=\"Filter by course\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">All courses</SelectItem>\n {trainingCourses.map((course) => (\n <SelectItem key={course.id} value={course.id}>\n {course.title}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Select\n value={statusFilter}\n onValueChange={(value) => {\n setStatusFilter(value as typeof statusFilter);\n setPage(1);\n }}\n >\n <SelectTrigger className=\"w-full md:w-40\" aria-label=\"Filter by status\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">All statuses</SelectItem>\n {ENROLLMENT_STATUS_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {!learningLoaded && !loadError ? (\n <div className=\"flex min-h-[240px] items-center justify-center\">\n <Spinner size=\"lg\" />\n </div>\n ) : enrollments.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title=\"No enrollments yet\"\n description=\"Open a course and enroll employees to start tracking their training.\"\n action={\n <Button size=\"sm\" onClick={() => navigate('../learning/courses-page')}>\n Browse courses\n </Button>\n }\n />\n ) : (\n <Card>\n <CardContent className=\"space-y-4 pt-6\">\n <ResponsiveTable<CrewEnrollment>\n data={paged}\n rowKey={(row) => row.id}\n columns={columns}\n emptyState={\n <p className=\"py-6 text-center text-sm text-text-secondary\">\n No enrollments match these filters.\n </p>\n }\n />\n {filtered.length > pageSize && (\n <Pagination\n page={page}\n pageSize={pageSize}\n totalItems={filtered.length}\n onPageChange={setPage}\n />\n )}\n </CardContent>\n </Card>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,2 +1,6 @@
1
+ /**
2
+ * Manager completion dashboard, read straight from
3
+ * `crewTrainingCompletionDashboard` (active courses only).
4
+ */
1
5
  export declare function LearningLearningAnalyticsPagePage(): import("react").JSX.Element;
2
6
  //# sourceMappingURL=LearningLearningAnalyticsPagePage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"LearningLearningAnalyticsPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/LearningLearningAnalyticsPagePage.tsx"],"names":[],"mappings":"AAkBA,wBAAgB,iCAAiC,gCAsKhD"}
1
+ {"version":3,"file":"LearningLearningAnalyticsPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/LearningLearningAnalyticsPagePage.tsx"],"names":[],"mappings":"AAoBA;;;GAGG;AACH,wBAAgB,iCAAiC,gCAyHhD"}