@burdenoff/microfe-crewfoundry 2026.1013.2 → 2026.1029.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +338 -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":"CommunicationsAnnouncementsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/CommunicationsAnnouncementsPagePage.tsx"],"sourcesContent":["import {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n Input,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { formatRelativeTime } from '@burdenoff/fe-libs/shared/utils';\nimport { Eye, MessageSquare, Pencil, Pin, PinOff, Plus, Search, Send, Trash2 } from 'lucide-react';\nimport { useMemo, useState } from 'react';\nimport { useNavigate } from 'react-router';\nimport { useCrewFoundryStore, type Announcement } from '../store/crewfoundryStore';\nimport {\n FormModal,\n useFormModal,\n type FieldConfig,\n type FormValues,\n} from '../components/forms/FormModal';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\nimport { AvatarStack } from '../components/viz';\n\nconst CATEGORY_OPTIONS = [\n { label: 'General', value: 'general' },\n { label: 'HR', value: 'hr' },\n { label: 'IT', value: 'it' },\n { label: 'Emergency', value: 'emergency' },\n { label: 'Event', value: 'event' },\n];\n\nconst PRIORITY_OPTIONS = [\n { label: 'Low', value: 'low' },\n { label: 'Medium', value: 'medium' },\n { label: 'High', value: 'high' },\n { label: 'Urgent', value: 'urgent' },\n];\n\n// Category → tokenized pill styling (semantic tokens only — themes cleanly).\nconst CATEGORY_STYLE: Record<Announcement['category'], string> = {\n general: 'bg-bg-sunken text-text-secondary',\n hr: 'bg-status-success-bg text-status-success-text',\n it: 'bg-bg-sunken text-text-secondary',\n emergency: 'bg-status-error-bg text-status-error-text',\n event: 'bg-status-info-bg text-status-info-text',\n};\n\nconst PRIORITY_STYLE: Record<Announcement['priority'], string> = {\n low: 'bg-bg-sunken text-text-secondary',\n medium: 'bg-bg-sunken text-text-secondary',\n high: 'bg-status-warning-bg text-status-warning-text',\n urgent: 'bg-status-error-bg text-status-error-text',\n};\n\nconst CREATE_FIELDS: FieldConfig[] = [\n { name: 'title', label: 'Title', type: 'text', required: true, colSpan: 2 },\n { name: 'content', label: 'Content', type: 'textarea', required: true, colSpan: 2 },\n { name: 'author', label: 'Author', type: 'text', required: true },\n { name: 'authorRole', label: 'Author Role', type: 'text', required: true },\n {\n name: 'category',\n label: 'Category',\n type: 'select',\n required: true,\n options: CATEGORY_OPTIONS,\n placeholder: 'Select a category',\n },\n {\n name: 'priority',\n label: 'Priority',\n type: 'select',\n required: true,\n options: PRIORITY_OPTIONS,\n placeholder: 'Select a priority',\n },\n { name: 'tags', label: 'Tags', type: 'tags', placeholder: 'Add a tag and press Enter' },\n { name: 'isPinned', label: 'Pin to top', type: 'switch' },\n];\n\n// Author/role are immutable after creation (they are not part of the update\n// input), so the edit form omits them.\nconst EDIT_FIELDS: FieldConfig[] = CREATE_FIELDS.filter(\n (field) => field.name !== 'author' && field.name !== 'authorRole'\n);\n\nconst relativeTime = (value: string): string => {\n if (!value) {\n return '';\n }\n try {\n return formatRelativeTime(value);\n } catch {\n return value;\n }\n};\n\nexport function CommunicationsAnnouncementsPagePage() {\n const { announcements, notifications, addAnnouncement, updateAnnouncement, removeAnnouncement } =\n useCrewFoundryStore();\n const { showToast } = useGlobalUi();\n const navigate = useNavigate();\n const form = useFormModal<Announcement>();\n const [deleteTarget, setDeleteTarget] = useState<Announcement | null>(null);\n const [deleting, setDeleting] = useState(false);\n const [search, setSearch] = useState('');\n const [draftMessage, setDraftMessage] = useState('');\n\n const editing = form.context;\n\n // Real conversations surfaced by the workspace overlap query land in the store\n // as `communications`-category notifications (id `conversation-*`). Render the\n // rail from that real data — no fabricated threads.\n const conversations = useMemo(\n () => notifications.filter((n) => n.category === 'communications'),\n [notifications]\n );\n\n const query = search.trim().toLowerCase();\n const filtered = useMemo(\n () =>\n query.length === 0\n ? announcements\n : announcements.filter((a) =>\n `${a.title} ${a.content} ${a.tags.join(' ')}`.toLowerCase().includes(query)\n ),\n [announcements, query]\n );\n\n const pinned = filtered.filter((a) => a.isPinned);\n const regular = filtered.filter((a) => !a.isPinned);\n\n const handleSubmit = async (values: FormValues) => {\n const tags = Array.isArray(values.tags) ? values.tags : [];\n if (editing) {\n await updateAnnouncement(editing.id, {\n title: String(values.title),\n content: String(values.content),\n category: String(values.category) as Announcement['category'],\n priority: String(values.priority) as Announcement['priority'],\n isPinned: Boolean(values.isPinned),\n tags,\n });\n showToast('Announcement updated', 'success');\n return;\n }\n await addAnnouncement({\n id: crypto.randomUUID(),\n title: String(values.title),\n content: String(values.content),\n author: String(values.author),\n authorRole: String(values.authorRole),\n category: String(values.category) as Announcement['category'],\n priority: String(values.priority) as Announcement['priority'],\n isPinned: Boolean(values.isPinned),\n createdAt: new Date().toISOString().slice(0, 10),\n viewCount: 0,\n tags,\n });\n showToast('Announcement published', 'success');\n };\n\n const togglePin = async (announcement: Announcement) => {\n try {\n await updateAnnouncement(announcement.id, { isPinned: !announcement.isPinned });\n showToast(announcement.isPinned ? 'Unpinned' : 'Pinned to top', 'success');\n } catch (error) {\n showToast(error instanceof Error ? error.message : 'Unable to update announcement', 'error');\n }\n };\n\n const confirmDelete = async () => {\n if (!deleteTarget) {\n return;\n }\n setDeleting(true);\n try {\n await removeAnnouncement(deleteTarget.id);\n showToast('Announcement deleted', 'success');\n setDeleteTarget(null);\n } catch (error) {\n showToast(error instanceof Error ? error.message : 'Unable to delete announcement', 'error');\n } finally {\n setDeleting(false);\n }\n };\n\n const openMessages = () => navigate('../communications/messages-page');\n\n const initialValues: FormValues | undefined = editing\n ? {\n title: editing.title,\n content: editing.content,\n category: editing.category,\n priority: editing.priority,\n tags: editing.tags,\n isPinned: editing.isPinned,\n }\n : undefined;\n\n const renderCard = (ann: Announcement, isPinnedCard: boolean) => (\n <GlassCard\n key={ann.id}\n treatment={isPinnedCard ? 'glass' : 'plain'}\n glow={isPinnedCard}\n className=\"transition-shadow hover:shadow-elevation-2\"\n >\n <CardHeader className=\"pb-3\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge className={`border-transparent text-[10px] ${PRIORITY_STYLE[ann.priority]}`}>\n {formatEnumLabel(ann.priority)}\n </Badge>\n <Badge className={`border-transparent text-[10px] ${CATEGORY_STYLE[ann.category]}`}>\n {formatEnumLabel(ann.category)}\n </Badge>\n <span className=\"flex items-center gap-1.5 text-xs text-text-secondary\">\n <AvatarStack people={[{ name: ann.author }]} size={20} />\n {ann.author}\n <span className=\"text-text-muted\">· {relativeTime(ann.createdAt)}</span>\n </span>\n </div>\n <div className=\"flex items-center gap-0.5\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className={`h-7 w-7 ${ann.isPinned ? 'text-status-info-text' : ''}`}\n aria-label={ann.isPinned ? `Unpin ${ann.title}` : `Pin ${ann.title}`}\n aria-pressed={ann.isPinned}\n onClick={() => void togglePin(ann)}\n >\n {ann.isPinned ? <PinOff className=\"h-3.5 w-3.5\" /> : <Pin className=\"h-3.5 w-3.5\" />}\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-7 w-7\"\n aria-label={`Edit announcement ${ann.title}`}\n onClick={() => form.openModal(ann)}\n >\n <Pencil className=\"h-3.5 w-3.5\" />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-7 w-7 text-status-error-text\"\n aria-label={`Delete announcement ${ann.title}`}\n onClick={() => setDeleteTarget(ann)}\n >\n <Trash2 className=\"h-3.5 w-3.5\" />\n </Button>\n </div>\n </div>\n <CardTitle className=\"text-base\">{ann.title}</CardTitle>\n </CardHeader>\n <CardContent className=\"pt-0\">\n <p className=\"text-sm text-text-secondary\">{ann.content}</p>\n {ann.tags.length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1\">\n {ann.tags.map((t) => (\n <Badge key={t} variant=\"outline\" className=\"text-[9px]\">\n {t}\n </Badge>\n ))}\n </div>\n )}\n <div className=\"mt-3 flex items-center gap-1 text-xs text-text-muted\">\n <Eye className=\"h-3 w-3\" />\n {ann.viewCount} read\n </div>\n </CardContent>\n </GlassCard>\n );\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>Announcements</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {announcements.length} company announcements\n </p>\n <PagePurpose className=\"mt-1\">\n Broadcast company-wide news and updates so everyone stays informed — post announcements\n here when you have something the whole team needs to see.\n </PagePurpose>\n </div>\n <Button size=\"sm\" onClick={() => form.openModal()}>\n <Plus className=\"h-4 w-4 mr-1\" />\n New Announcement\n </Button>\n </div>\n\n <div className=\"relative max-w-md\">\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 announcements...\"\n className=\"pl-9\"\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n />\n </div>\n\n <div className=\"grid gap-6 lg:grid-cols-3\">\n {/* Feed */}\n <div className=\"space-y-4 lg:col-span-2\">\n {filtered.length === 0 && (\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title={query ? 'No announcements match' : 'No announcements yet'}\n description={\n query\n ? 'Try a different search term.'\n : 'Post an announcement to share news with the whole team.'\n }\n />\n )}\n {pinned.length > 0 && (\n <div className=\"space-y-3\">\n <h2 className=\"flex items-center gap-1 text-sm font-medium\">\n <Pin className=\"h-4 w-4\" />\n Pinned\n </h2>\n {pinned.map((ann) => renderCard(ann, true))}\n </div>\n )}\n {regular.length > 0 && (\n <div className=\"space-y-3\">{regular.map((ann) => renderCard(ann, false))}</div>\n )}\n </div>\n\n {/* Conversations rail */}\n <div className=\"lg:col-span-1\">\n <Card className=\"lg:sticky lg:top-6\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-3\">\n <CardTitle className=\"text-base\">Conversations</CardTitle>\n <Button variant=\"ghost\" size=\"sm\" onClick={openMessages}>\n <MessageSquare className=\"mr-1 h-3.5 w-3.5\" />\n Open\n </Button>\n </CardHeader>\n <CardContent className=\"space-y-1\">\n {conversations.length === 0 ? (\n <p className=\"py-6 text-center text-xs text-text-muted\">\n No active conversations yet. Team channels will appear here as they get activity.\n </p>\n ) : (\n conversations.map((conv) => (\n <button\n key={conv.id}\n type=\"button\"\n onClick={openMessages}\n className=\"flex w-full items-start gap-3 rounded-[var(--radius-card)] p-2 text-left transition-colors hover:bg-bg-sunken\"\n >\n <AvatarStack people={[{ name: conv.title }]} size={32} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center justify-between gap-2\">\n <p className=\"truncate text-sm font-medium\">{conv.title}</p>\n <span className=\"shrink-0 text-[10px] text-text-muted\">\n {relativeTime(conv.createdAt)}\n </span>\n </div>\n <p className=\"truncate text-xs text-text-secondary\">{conv.message}</p>\n </div>\n {!conv.read && (\n <span\n className=\"mt-1.5 h-2 w-2 shrink-0 rounded-full bg-status-success-text\"\n aria-label=\"Unread\"\n />\n )}\n </button>\n ))\n )}\n <div className=\"flex items-center gap-2 pt-3\">\n <Input\n placeholder=\"Type a message...\"\n className=\"h-9 flex-1\"\n value={draftMessage}\n onChange={(event) => setDraftMessage(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') {\n openMessages();\n }\n }}\n aria-label=\"Message composer\"\n />\n <Button\n size=\"icon\"\n className=\"h-9 w-9 shrink-0\"\n aria-label=\"Open messages to send\"\n onClick={openMessages}\n >\n <Send className=\"h-4 w-4\" />\n </Button>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n\n <FormModal\n open={form.open}\n onOpenChange={form.onOpenChange}\n title={editing ? 'Edit Announcement' : 'New Announcement'}\n description={\n editing\n ? 'Update this announcement for the whole team.'\n : 'Share news and updates across the company.'\n }\n fields={editing ? EDIT_FIELDS : CREATE_FIELDS}\n initialValues={initialValues}\n submitLabel={editing ? 'Save changes' : 'Publish'}\n onSubmit={handleSubmit}\n />\n\n <AlertDialog\n open={deleteTarget !== null}\n onOpenChange={(open) => {\n if (!open) {\n setDeleteTarget(null);\n }\n }}\n >\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Delete announcement?</AlertDialogTitle>\n <AlertDialogDescription>\n {deleteTarget\n ? `\"${deleteTarget.title}\" will be permanently removed. This cannot be undone.`\n : ''}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel disabled={deleting}>Cancel</AlertDialogCancel>\n <AlertDialogAction\n onClick={(event) => {\n event.preventDefault();\n void confirmDelete();\n }}\n disabled={deleting}\n >\n {deleting ? 'Deleting…' : 'Delete'}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"CommunicationsAnnouncementsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/CommunicationsAnnouncementsPagePage.tsx"],"sourcesContent":["import {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n Input,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { formatRelativeTime } from '@burdenoff/fe-libs/shared/utils';\nimport { Eye, MessageSquare, Pencil, Pin, PinOff, Plus, Search, Send, Trash2 } from 'lucide-react';\nimport { useMemo, useState } from 'react';\nimport { useNavigate } from 'react-router';\nimport {\n CONVERSATIONS_ROUTE,\n useCrewFoundryStore,\n type Announcement,\n} from '../store/crewfoundryStore';\nimport {\n FormModal,\n useFormModal,\n type FieldConfig,\n type FormValues,\n} from '../components/forms/FormModal';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\nimport { AvatarStack } from '../components/viz';\n\nconst CATEGORY_OPTIONS = [\n { label: 'General', value: 'general' },\n { label: 'HR', value: 'hr' },\n { label: 'IT', value: 'it' },\n { label: 'Emergency', value: 'emergency' },\n { label: 'Event', value: 'event' },\n];\n\nconst PRIORITY_OPTIONS = [\n { label: 'Low', value: 'low' },\n { label: 'Medium', value: 'medium' },\n { label: 'High', value: 'high' },\n { label: 'Urgent', value: 'urgent' },\n];\n\n// Category → tokenized pill styling (semantic tokens only — themes cleanly).\nconst CATEGORY_STYLE: Record<Announcement['category'], string> = {\n general: 'bg-bg-sunken text-text-secondary',\n hr: 'bg-status-success-bg text-status-success-text',\n it: 'bg-bg-sunken text-text-secondary',\n emergency: 'bg-status-error-bg text-status-error-text',\n event: 'bg-status-info-bg text-status-info-text',\n};\n\nconst PRIORITY_STYLE: Record<Announcement['priority'], string> = {\n low: 'bg-bg-sunken text-text-secondary',\n medium: 'bg-bg-sunken text-text-secondary',\n high: 'bg-status-warning-bg text-status-warning-text',\n urgent: 'bg-status-error-bg text-status-error-text',\n};\n\nconst CREATE_FIELDS: FieldConfig[] = [\n { name: 'title', label: 'Title', type: 'text', required: true, colSpan: 2 },\n { name: 'content', label: 'Content', type: 'textarea', required: true, colSpan: 2 },\n { name: 'author', label: 'Author', type: 'text', required: true },\n { name: 'authorRole', label: 'Author Role', type: 'text', required: true },\n {\n name: 'category',\n label: 'Category',\n type: 'select',\n required: true,\n options: CATEGORY_OPTIONS,\n placeholder: 'Select a category',\n },\n {\n name: 'priority',\n label: 'Priority',\n type: 'select',\n required: true,\n options: PRIORITY_OPTIONS,\n placeholder: 'Select a priority',\n },\n { name: 'tags', label: 'Tags', type: 'tags', placeholder: 'Add a tag and press Enter' },\n { name: 'isPinned', label: 'Pin to top', type: 'switch' },\n];\n\n// Author/role are immutable after creation (they are not part of the update\n// input), so the edit form omits them.\nconst EDIT_FIELDS: FieldConfig[] = CREATE_FIELDS.filter(\n (field) => field.name !== 'author' && field.name !== 'authorRole'\n);\n\nconst relativeTime = (value: string): string => {\n if (!value) {\n return '';\n }\n try {\n return formatRelativeTime(value);\n } catch {\n return value;\n }\n};\n\nexport function CommunicationsAnnouncementsPagePage() {\n const { announcements, notifications, addAnnouncement, updateAnnouncement, removeAnnouncement } =\n useCrewFoundryStore();\n const { showToast } = useGlobalUi();\n const navigate = useNavigate();\n const form = useFormModal<Announcement>();\n const [deleteTarget, setDeleteTarget] = useState<Announcement | null>(null);\n const [deleting, setDeleting] = useState(false);\n const [search, setSearch] = useState('');\n\n const editing = form.context;\n\n // Real conversations surfaced by the workspace overlap query land in the store\n // as `communications`-category notifications (id `conversation-*`). Render the\n // rail from that real data — no fabricated threads.\n const conversations = useMemo(\n () => notifications.filter((n) => n.category === 'communications'),\n [notifications]\n );\n\n const query = search.trim().toLowerCase();\n const filtered = useMemo(\n () =>\n query.length === 0\n ? announcements\n : announcements.filter((a) =>\n `${a.title} ${a.content} ${a.tags.join(' ')}`.toLowerCase().includes(query)\n ),\n [announcements, query]\n );\n\n const pinned = filtered.filter((a) => a.isPinned);\n const regular = filtered.filter((a) => !a.isPinned);\n\n const handleSubmit = async (values: FormValues) => {\n const tags = Array.isArray(values.tags) ? values.tags : [];\n if (editing) {\n await updateAnnouncement(editing.id, {\n title: String(values.title),\n content: String(values.content),\n category: String(values.category) as Announcement['category'],\n priority: String(values.priority) as Announcement['priority'],\n isPinned: Boolean(values.isPinned),\n tags,\n });\n showToast('Announcement updated', 'success');\n return;\n }\n await addAnnouncement({\n id: crypto.randomUUID(),\n title: String(values.title),\n content: String(values.content),\n author: String(values.author),\n authorRole: String(values.authorRole),\n category: String(values.category) as Announcement['category'],\n priority: String(values.priority) as Announcement['priority'],\n isPinned: Boolean(values.isPinned),\n createdAt: new Date().toISOString().slice(0, 10),\n viewCount: 0,\n tags,\n });\n showToast('Announcement published', 'success');\n };\n\n const togglePin = async (announcement: Announcement) => {\n try {\n await updateAnnouncement(announcement.id, { isPinned: !announcement.isPinned });\n showToast(announcement.isPinned ? 'Unpinned' : 'Pinned to top', 'success');\n } catch (error) {\n showToast(error instanceof Error ? error.message : 'Unable to update announcement', 'error');\n }\n };\n\n const confirmDelete = async () => {\n if (!deleteTarget) {\n return;\n }\n setDeleting(true);\n try {\n await removeAnnouncement(deleteTarget.id);\n showToast('Announcement deleted', 'success');\n setDeleteTarget(null);\n } catch (error) {\n showToast(error instanceof Error ? error.message : 'Unable to delete announcement', 'error');\n } finally {\n setDeleting(false);\n }\n };\n\n // Messaging is owned by the shell's shared Conversations app\n // (wspace-conversations-svc); send there instead of a local composer that\n // could not deliver anything.\n const openMessages = () => navigate(CONVERSATIONS_ROUTE);\n\n const initialValues: FormValues | undefined = editing\n ? {\n title: editing.title,\n content: editing.content,\n category: editing.category,\n priority: editing.priority,\n tags: editing.tags,\n isPinned: editing.isPinned,\n }\n : undefined;\n\n const renderCard = (ann: Announcement, isPinnedCard: boolean) => (\n <GlassCard\n key={ann.id}\n treatment={isPinnedCard ? 'glass' : 'plain'}\n glow={isPinnedCard}\n className=\"transition-shadow hover:shadow-elevation-2\"\n >\n <CardHeader className=\"pb-3\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge className={`border-transparent text-[10px] ${PRIORITY_STYLE[ann.priority]}`}>\n {formatEnumLabel(ann.priority)}\n </Badge>\n <Badge className={`border-transparent text-[10px] ${CATEGORY_STYLE[ann.category]}`}>\n {formatEnumLabel(ann.category)}\n </Badge>\n <span className=\"flex items-center gap-1.5 text-xs text-text-secondary\">\n <AvatarStack people={[{ name: ann.author }]} size={20} />\n {ann.author}\n <span className=\"text-text-muted\">· {relativeTime(ann.createdAt)}</span>\n </span>\n </div>\n <div className=\"flex items-center gap-0.5\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className={`h-7 w-7 ${ann.isPinned ? 'text-status-info-text' : ''}`}\n aria-label={ann.isPinned ? `Unpin ${ann.title}` : `Pin ${ann.title}`}\n aria-pressed={ann.isPinned}\n onClick={() => void togglePin(ann)}\n >\n {ann.isPinned ? <PinOff className=\"h-3.5 w-3.5\" /> : <Pin className=\"h-3.5 w-3.5\" />}\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-7 w-7\"\n aria-label={`Edit announcement ${ann.title}`}\n onClick={() => form.openModal(ann)}\n >\n <Pencil className=\"h-3.5 w-3.5\" />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-7 w-7 text-status-error-text\"\n aria-label={`Delete announcement ${ann.title}`}\n onClick={() => setDeleteTarget(ann)}\n >\n <Trash2 className=\"h-3.5 w-3.5\" />\n </Button>\n </div>\n </div>\n <CardTitle className=\"text-base\">{ann.title}</CardTitle>\n </CardHeader>\n <CardContent className=\"pt-0\">\n <p className=\"text-sm text-text-secondary\">{ann.content}</p>\n {ann.tags.length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1\">\n {ann.tags.map((t) => (\n <Badge key={t} variant=\"outline\" className=\"text-[9px]\">\n {t}\n </Badge>\n ))}\n </div>\n )}\n <div className=\"mt-3 flex items-center gap-1 text-xs text-text-muted\">\n <Eye className=\"h-3 w-3\" />\n {ann.viewCount} read\n </div>\n </CardContent>\n </GlassCard>\n );\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>Announcements</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {announcements.length} company announcements\n </p>\n <PagePurpose className=\"mt-1\">\n Broadcast company-wide news and updates so everyone stays informed — post announcements\n here when you have something the whole team needs to see.\n </PagePurpose>\n </div>\n <Button size=\"sm\" onClick={() => form.openModal()}>\n <Plus className=\"h-4 w-4 mr-1\" />\n New Announcement\n </Button>\n </div>\n\n <div className=\"relative max-w-md\">\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 announcements...\"\n className=\"pl-9\"\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n />\n </div>\n\n <div className=\"grid gap-6 lg:grid-cols-3\">\n {/* Feed */}\n <div className=\"space-y-4 lg:col-span-2\">\n {filtered.length === 0 && (\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title={query ? 'No announcements match' : 'No announcements yet'}\n description={\n query\n ? 'Try a different search term.'\n : 'Post an announcement to share news with the whole team.'\n }\n />\n )}\n {pinned.length > 0 && (\n <div className=\"space-y-3\">\n <h2 className=\"flex items-center gap-1 text-sm font-medium\">\n <Pin className=\"h-4 w-4\" />\n Pinned\n </h2>\n {pinned.map((ann) => renderCard(ann, true))}\n </div>\n )}\n {regular.length > 0 && (\n <div className=\"space-y-3\">{regular.map((ann) => renderCard(ann, false))}</div>\n )}\n </div>\n\n {/* Conversations rail */}\n <div className=\"lg:col-span-1\">\n <Card className=\"lg:sticky lg:top-6\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-3\">\n <CardTitle className=\"text-base\">Conversations</CardTitle>\n <Button variant=\"ghost\" size=\"sm\" onClick={openMessages}>\n <MessageSquare className=\"mr-1 h-3.5 w-3.5\" />\n Open\n </Button>\n </CardHeader>\n <CardContent className=\"space-y-1\">\n {conversations.length === 0 ? (\n <p className=\"py-6 text-center text-xs text-text-muted\">\n No active conversations yet. Team channels will appear here as they get activity.\n </p>\n ) : (\n conversations.map((conv) => (\n <button\n key={conv.id}\n type=\"button\"\n onClick={openMessages}\n className=\"flex w-full items-start gap-3 rounded-[var(--radius-card)] p-2 text-left transition-colors hover:bg-bg-sunken\"\n >\n <AvatarStack people={[{ name: conv.title }]} size={32} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center justify-between gap-2\">\n <p className=\"truncate text-sm font-medium\">{conv.title}</p>\n <span className=\"shrink-0 text-[10px] text-text-muted\">\n {relativeTime(conv.createdAt)}\n </span>\n </div>\n <p className=\"truncate text-xs text-text-secondary\">{conv.message}</p>\n </div>\n {!conv.read && (\n <span\n className=\"mt-1.5 h-2 w-2 shrink-0 rounded-full bg-status-success-text\"\n aria-label=\"Unread\"\n />\n )}\n </button>\n ))\n )}\n <Button variant=\"outline\" size=\"sm\" className=\"mt-3 w-full\" onClick={openMessages}>\n <Send className=\"mr-1 h-3.5 w-3.5\" />\n Write a message in Conversations\n </Button>\n </CardContent>\n </Card>\n </div>\n </div>\n\n <FormModal\n open={form.open}\n onOpenChange={form.onOpenChange}\n title={editing ? 'Edit Announcement' : 'New Announcement'}\n description={\n editing\n ? 'Update this announcement for the whole team.'\n : 'Share news and updates across the company.'\n }\n fields={editing ? EDIT_FIELDS : CREATE_FIELDS}\n initialValues={initialValues}\n submitLabel={editing ? 'Save changes' : 'Publish'}\n onSubmit={handleSubmit}\n />\n\n <AlertDialog\n open={deleteTarget !== null}\n onOpenChange={(open) => {\n if (!open) {\n setDeleteTarget(null);\n }\n }}\n >\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Delete announcement?</AlertDialogTitle>\n <AlertDialogDescription>\n {deleteTarget\n ? `\"${deleteTarget.title}\" will be permanently removed. This cannot be undone.`\n : ''}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel disabled={deleting}>Cancel</AlertDialogCancel>\n <AlertDialogAction\n onClick={(event) => {\n event.preventDefault();\n void confirmDelete();\n }}\n disabled={deleting}\n >\n {deleting ? 'Deleting…' : 'Delete'}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CompanyClientManagementPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/CompanyClientManagementPagePage.tsx"],"names":[],"mappings":"AA2GA,wBAAgB,+BAA+B,gCAuQ9C"}
1
+ {"version":3,"file":"CompanyClientManagementPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/CompanyClientManagementPagePage.tsx"],"names":[],"mappings":"AA2GA,wBAAgB,+BAA+B,gCAsQ9C"}
@@ -5,7 +5,7 @@ import { AvatarStack as r } from "../components/viz/AvatarStack.js";
5
5
  import { SegmentedTabs as ee } from "../components/viz/SegmentedTabs.js";
6
6
  import "../components/viz/index.js";
7
7
  import { FormModal as te, useFormModal as i } from "../components/forms/FormModal.js";
8
- import { thisQuarterRevenue as a, totalContractValue as o } from "../lib/companyFinance.js";
8
+ import { clientRevenue as a, totalContractValue as o } from "../lib/companyFinance.js";
9
9
  import { useState as s } from "react";
10
10
  import { MoreHorizontal as c, Pencil as l, Plus as u, Search as ne, Trash2 as re } from "lucide-react";
11
11
  import { useNavigate as ie } from "react-router";
@@ -97,10 +97,10 @@ var R = [
97
97
  since: String(e.since ?? "")
98
98
  }), V = (e) => e === "active" ? "default" : e === "prospect" ? "secondary" : "destructive", H = (e) => e >= 1e6 ? `$${(e / 1e6).toFixed(1)}M` : e >= 1e3 ? `$${Math.round(e / 1e3)}k` : `$${Math.round(e)}`;
99
99
  function U() {
100
- let { clients: U, vendors: W, invoices: de, addClient: fe, updateClient: pe, removeClient: me } = e(), he = ie(), G = i(), [K, ge] = s(""), [q, _e] = s("all"), [J, Y] = s(null), { page: ve, pageSize: X, offset: Z, setPage: Q } = L({ initialPageSize: 25 }), ye = U.filter((e) => e.status === "active"), be = o(U, W), xe = a(de), $ = U.filter((e) => {
100
+ let { clients: U, vendors: W, addClient: de, updateClient: fe, removeClient: pe } = e(), me = ie(), G = i(), [K, he] = s(""), [q, ge] = s("all"), [J, Y] = s(null), { page: _e, pageSize: X, offset: Z, setPage: Q } = L({ initialPageSize: 25 }), ve = U.filter((e) => e.status === "active"), ye = o(U, W), be = a(U), $ = U.filter((e) => {
101
101
  let t = `${e.name} ${e.industry} ${e.contactPerson} ${e.email}`.toLowerCase().includes(K.toLowerCase()), n = q === "all" || e.status === q;
102
102
  return t && n;
103
- }), Se = $.slice(Z, Z + X);
103
+ }), xe = $.slice(Z, Z + X);
104
104
  return /* @__PURE__ */ f("div", {
105
105
  className: "space-y-6 p-6",
106
106
  children: [
@@ -135,7 +135,7 @@ function U() {
135
135
  variant: "underline",
136
136
  value: "clients",
137
137
  onChange: (e) => {
138
- e === "vendors" && he("../company/vendor-management-page");
138
+ e === "vendors" && me("../company/vendor-management-page");
139
139
  },
140
140
  tabs: [{
141
141
  id: "clients",
@@ -152,7 +152,7 @@ function U() {
152
152
  children: [
153
153
  /* @__PURE__ */ d(n, {
154
154
  label: "Active Clients",
155
- value: ye.length
155
+ value: ve.length
156
156
  }),
157
157
  /* @__PURE__ */ d(n, {
158
158
  label: "Vendors",
@@ -160,11 +160,11 @@ function U() {
160
160
  }),
161
161
  /* @__PURE__ */ d(n, {
162
162
  label: "Total Contract Value",
163
- value: H(be)
163
+ value: H(ye)
164
164
  }),
165
165
  /* @__PURE__ */ d(n, {
166
- label: "This Quarter",
167
- value: H(xe)
166
+ label: "Client Revenue",
167
+ value: H(be)
168
168
  })
169
169
  ]
170
170
  }),
@@ -180,13 +180,13 @@ function U() {
180
180
  className: "pl-9",
181
181
  value: K,
182
182
  onChange: (e) => {
183
- ge(e.target.value), Q(1);
183
+ he(e.target.value), Q(1);
184
184
  }
185
185
  })]
186
186
  }), /* @__PURE__ */ f(N, {
187
187
  value: q,
188
188
  onValueChange: (e) => {
189
- _e(e), Q(1);
189
+ ge(e), Q(1);
190
190
  },
191
191
  children: [/* @__PURE__ */ d(I, {
192
192
  className: "w-full sm:w-40",
@@ -213,7 +213,7 @@ function U() {
213
213
  })]
214
214
  }),
215
215
  /* @__PURE__ */ d(M, {
216
- data: Se,
216
+ data: xe,
217
217
  rowKey: (e) => e.id,
218
218
  columns: [
219
219
  {
@@ -316,7 +316,7 @@ function U() {
316
316
  })
317
317
  }),
318
318
  $.length > X && /* @__PURE__ */ d(j, {
319
- page: ve,
319
+ page: _e,
320
320
  pageSize: X,
321
321
  totalItems: $.length,
322
322
  onPageChange: Q
@@ -332,7 +332,7 @@ function U() {
332
332
  initialValues: G.context ? z(G.context) : void 0,
333
333
  submitLabel: G.context ? "Save changes" : "Add client",
334
334
  onSubmit: async (e) => {
335
- G.context ? await pe(G.context.id, B(e, G.context)) : await fe(B(e));
335
+ G.context ? await fe(G.context.id, B(e, G.context)) : await de(B(e));
336
336
  }
337
337
  }),
338
338
  /* @__PURE__ */ d(ae, {
@@ -342,7 +342,7 @@ function U() {
342
342
  },
343
343
  children: /* @__PURE__ */ f(ce, { children: [/* @__PURE__ */ f(m, { children: [/* @__PURE__ */ d(h, { children: "Delete client?" }), /* @__PURE__ */ d(le, { children: J ? `This removes “${J.name}” from your client portfolio. This action cannot be undone.` : "" })] }), /* @__PURE__ */ f(p, { children: [/* @__PURE__ */ d(se, { children: "Cancel" }), /* @__PURE__ */ d(oe, {
344
344
  onClick: (e) => {
345
- e.preventDefault(), J && me(J.id), Y(null);
345
+ e.preventDefault(), J && pe(J.id), Y(null);
346
346
  },
347
347
  children: "Delete"
348
348
  })] })] })
@@ -1 +1 @@
1
- {"version":3,"file":"CompanyClientManagementPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/CompanyClientManagementPagePage.tsx"],"sourcesContent":["import { 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 Card,\n CardContent,\n CardHeader,\n CardTitle,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n GradientText,\n IllustratedEmptyState,\n Input,\n PagePurpose,\n Pagination,\n ResponsiveTable,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n type ResponsiveTableColumn,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { MoreHorizontal, Pencil, Plus, Search, Trash2 } 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 { useCrewFoundryStore, type Client } from '../store/crewfoundryStore';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\nimport { AvatarStack, SegmentedTabs, StatCard } from '../components/viz';\nimport { thisQuarterRevenue, totalContractValue } from '../lib/companyFinance';\n\nconst CLIENT_FIELDS: FieldConfig[] = [\n { name: 'name', label: 'Client name', type: 'text', required: true },\n { name: 'industry', label: 'Industry', type: 'text' },\n { name: 'contactPerson', label: 'Contact person', type: 'text' },\n { name: 'email', label: 'Contact email', type: 'email' },\n { name: 'phone', label: 'Phone', type: 'text' },\n {\n name: 'status',\n label: 'Status',\n type: 'select',\n options: [\n { label: 'Active', value: 'active' },\n { label: 'Prospect', value: 'prospect' },\n { label: 'Churned', value: 'churned' },\n ],\n },\n { name: 'revenue', label: 'Revenue (USD)', type: 'number' },\n { name: 'projects', label: 'Projects', type: 'number' },\n { name: 'since', label: 'Client since', type: 'date' },\n];\n\nconst toInitialValues = (client: Client): FormValues => ({\n name: client.name,\n industry: client.industry,\n contactPerson: client.contactPerson,\n email: client.email,\n phone: client.phone,\n status: client.status,\n revenue: client.revenue,\n projects: client.projects,\n since: client.since ? client.since.slice(0, 10) : '',\n});\n\nconst valuesToClient = (values: FormValues, base?: Client): Client => ({\n id: base?.id ?? crypto.randomUUID(),\n name: String(values.name ?? ''),\n industry: String(values.industry ?? ''),\n contactPerson: String(values.contactPerson ?? ''),\n email: String(values.email ?? ''),\n phone: String(values.phone ?? ''),\n status: (values.status as Client['status']) || 'prospect',\n revenue: typeof values.revenue === 'number' ? values.revenue : Number(values.revenue) || 0,\n projects: typeof values.projects === 'number' ? values.projects : Number(values.projects) || 0,\n since: String(values.since ?? ''),\n});\n\nconst statusVariant = (status: Client['status']) =>\n status === 'active' ? 'default' : status === 'prospect' ? 'secondary' : 'destructive';\n\nconst compactMoney = (value: number): string => {\n if (value >= 1_000_000) {\n return `$${(value / 1_000_000).toFixed(1)}M`;\n }\n if (value >= 1_000) {\n return `$${Math.round(value / 1_000)}k`;\n }\n return `$${Math.round(value)}`;\n};\n\nexport function CompanyClientManagementPagePage() {\n const { clients, vendors, invoices, addClient, updateClient, removeClient } =\n useCrewFoundryStore();\n const navigate = useNavigate();\n const modal = useFormModal<Client>();\n const [search, setSearch] = useState('');\n const [filterStatus, setFilterStatus] = useState<string>('all');\n const [deleteTarget, setDeleteTarget] = useState<Client | null>(null);\n const { page, pageSize, offset, setPage } = usePagination({ initialPageSize: 25 });\n\n const activeClients = clients.filter((c) => c.status === 'active');\n const contractValue = totalContractValue(clients, vendors);\n const quarterRevenue = thisQuarterRevenue(invoices);\n\n const filtered = clients.filter((c) => {\n const matchSearch = `${c.name} ${c.industry} ${c.contactPerson} ${c.email}`\n .toLowerCase()\n .includes(search.toLowerCase());\n const matchStatus = filterStatus === 'all' || c.status === filterStatus;\n return matchSearch && matchStatus;\n });\n const paged = filtered.slice(offset, offset + pageSize);\n\n const handleSubmit = async (values: FormValues) => {\n if (modal.context) {\n await updateClient(modal.context.id, valuesToClient(values, modal.context));\n } else {\n await addClient(valuesToClient(values));\n }\n };\n\n const columns: ResponsiveTableColumn<Client>[] = [\n {\n key: 'name',\n header: 'Client',\n priority: 'primary',\n cell: (c) => (\n <div className=\"flex items-center gap-3\">\n <AvatarStack people={[{ name: c.name }]} size={36} />\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium\">{c.name}</p>\n <p className=\"truncate text-xs text-text-secondary\">{c.industry || '—'}</p>\n </div>\n </div>\n ),\n },\n {\n key: 'contact',\n header: 'Primary Contact',\n priority: 'secondary',\n cell: (c) => (\n <div>\n <p className=\"text-xs\">{c.contactPerson || '—'}</p>\n <p className=\"text-xs text-text-secondary\">{c.email}</p>\n </div>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n priority: 'secondary',\n cell: (c) => (\n <Badge variant={statusVariant(c.status)} className=\"text-[10px]\">\n {formatEnumLabel(c.status)}\n </Badge>\n ),\n },\n {\n key: 'projects',\n header: 'Projects',\n priority: 'hidden-on-mobile',\n headerClassName: 'text-right',\n cellClassName: 'text-right',\n cell: (c) => <span className=\"tabular-nums\">{c.projects}</span>,\n },\n {\n key: 'revenue',\n header: 'Value',\n priority: 'secondary',\n headerClassName: 'text-right',\n cellClassName: 'text-right',\n cell: (c) => <span className=\"tabular-nums\">${c.revenue.toLocaleString()}</span>,\n },\n ];\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Clients &amp; Vendors</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {clients.length} clients · {vendors.length} vendors\n </p>\n <PagePurpose className=\"mt-1\">\n Keep track of the clients you work with — their contacts, status, and engagements — so\n account relationships never slip through the cracks.\n </PagePurpose>\n </div>\n <Button size=\"sm\" onClick={() => modal.openModal()}>\n <Plus className=\"h-4 w-4 mr-1\" />\n Add Client\n </Button>\n </div>\n\n <SegmentedTabs\n aria-label=\"Clients and vendors\"\n variant=\"underline\"\n value=\"clients\"\n onChange={(id) => {\n if (id === 'vendors') {\n navigate('../company/vendor-management-page');\n }\n }}\n tabs={[\n { id: 'clients', label: 'Clients', count: clients.length },\n { id: 'vendors', label: 'Vendors', count: vendors.length },\n ]}\n />\n\n <div className=\"grid gap-4 sm:grid-cols-2 xl:grid-cols-4\">\n <StatCard label=\"Active Clients\" value={activeClients.length} />\n <StatCard label=\"Vendors\" value={vendors.length} />\n <StatCard label=\"Total Contract Value\" value={compactMoney(contractValue)} />\n <StatCard label=\"This Quarter\" value={compactMoney(quarterRevenue)} />\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Client Portfolio</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\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 clients...\"\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={filterStatus}\n onValueChange={(value) => {\n setFilterStatus(value);\n setPage(1);\n }}\n >\n <SelectTrigger className=\"w-full sm:w-40\" aria-label=\"Filter by status\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">All statuses</SelectItem>\n <SelectItem value=\"active\">Active</SelectItem>\n <SelectItem value=\"prospect\">Prospect</SelectItem>\n <SelectItem value=\"churned\">Churned</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <ResponsiveTable\n data={paged}\n rowKey={(c) => c.id}\n columns={columns}\n emptyState={\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No clients match\"\n description=\"Try a different search term or status filter.\"\n />\n }\n rowActions={(c) => (\n <div className=\"flex items-center justify-end\">\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 ${c.name}`}\n >\n <MoreHorizontal className=\"h-4 w-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem onSelect={() => modal.openModal(c)}>\n <Pencil className=\"mr-2 h-4 w-4\" />\n Edit\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"text-status-error-text\"\n onSelect={() => setDeleteTarget(c)}\n >\n <Trash2 className=\"mr-2 h-4 w-4\" />\n Delete\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\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 <FormModal\n open={modal.open}\n onOpenChange={modal.onOpenChange}\n title={modal.context ? 'Edit client' : 'Add client'}\n description=\"Track the client relationship, contacts, and engagement value.\"\n fields={CLIENT_FIELDS}\n initialValues={modal.context ? toInitialValues(modal.context) : undefined}\n submitLabel={modal.context ? 'Save changes' : 'Add client'}\n onSubmit={handleSubmit}\n />\n\n <AlertDialog\n open={deleteTarget !== null}\n onOpenChange={(open) => {\n if (!open) {\n setDeleteTarget(null);\n }\n }}\n >\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Delete client?</AlertDialogTitle>\n <AlertDialogDescription>\n {deleteTarget\n ? `This removes “${deleteTarget.name}” from your client portfolio. This action cannot be undone.`\n : ''}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Cancel</AlertDialogCancel>\n <AlertDialogAction\n onClick={(event) => {\n event.preventDefault();\n if (deleteTarget) {\n void removeClient(deleteTarget.id);\n }\n setDeleteTarget(null);\n }}\n >\n Delete\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AAgDA,IAAM,IAA+B;CACnC;EAAE,MAAM;EAAQ,OAAO;EAAe,MAAM;EAAQ,UAAU;EAAM;CACpE;EAAE,MAAM;EAAY,OAAO;EAAY,MAAM;EAAQ;CACrD;EAAE,MAAM;EAAiB,OAAO;EAAkB,MAAM;EAAQ;CAChE;EAAE,MAAM;EAAS,OAAO;EAAiB,MAAM;EAAS;CACxD;EAAE,MAAM;EAAS,OAAO;EAAS,MAAM;EAAQ;CAC/C;EACE,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;GACP;IAAE,OAAO;IAAU,OAAO;IAAU;GACpC;IAAE,OAAO;IAAY,OAAO;IAAY;GACxC;IAAE,OAAO;IAAW,OAAO;IAAW;GACvC;EACF;CACD;EAAE,MAAM;EAAW,OAAO;EAAiB,MAAM;EAAU;CAC3D;EAAE,MAAM;EAAY,OAAO;EAAY,MAAM;EAAU;CACvD;EAAE,MAAM;EAAS,OAAO;EAAgB,MAAM;EAAQ;CACvD,EAEK,KAAmB,OAAgC;CACvD,MAAM,EAAO;CACb,UAAU,EAAO;CACjB,eAAe,EAAO;CACtB,OAAO,EAAO;CACd,OAAO,EAAO;CACd,QAAQ,EAAO;CACf,SAAS,EAAO;CAChB,UAAU,EAAO;CACjB,OAAO,EAAO,QAAQ,EAAO,MAAM,MAAM,GAAG,GAAG,GAAG;CACnD,GAEK,KAAkB,GAAoB,OAA2B;CACrE,IAAI,GAAM,MAAM,OAAO,YAAY;CACnC,MAAM,OAAO,EAAO,QAAQ,GAAG;CAC/B,UAAU,OAAO,EAAO,YAAY,GAAG;CACvC,eAAe,OAAO,EAAO,iBAAiB,GAAG;CACjD,OAAO,OAAO,EAAO,SAAS,GAAG;CACjC,OAAO,OAAO,EAAO,SAAS,GAAG;CACjC,QAAS,EAAO,UAA+B;CAC/C,SAAS,OAAO,EAAO,WAAY,WAAW,EAAO,UAAU,OAAO,EAAO,QAAQ,IAAI;CACzF,UAAU,OAAO,EAAO,YAAa,WAAW,EAAO,WAAW,OAAO,EAAO,SAAS,IAAI;CAC7F,OAAO,OAAO,EAAO,SAAS,GAAG;CAClC,GAEK,KAAiB,MACrB,MAAW,WAAW,YAAY,MAAW,aAAa,cAAc,eAEpE,KAAgB,MAChB,KAAS,MACJ,KAAK,IAAQ,KAAW,QAAQ,EAAE,CAAC,KAExC,KAAS,MACJ,IAAI,KAAK,MAAM,IAAQ,IAAM,CAAC,KAEhC,IAAI,KAAK,MAAM,EAAM;AAG9B,SAAgB,IAAkC;CAChD,IAAM,EAAE,YAAS,YAAS,cAAU,eAAW,kBAAc,qBAC3D,GAAqB,EACjB,KAAW,IAAa,EACxB,IAAQ,GAAsB,EAC9B,CAAC,GAAQ,MAAa,EAAS,GAAG,EAClC,CAAC,GAAc,MAAmB,EAAiB,MAAM,EACzD,CAAC,GAAc,KAAmB,EAAwB,KAAK,EAC/D,EAAE,UAAM,aAAU,WAAQ,eAAY,EAAc,EAAE,iBAAiB,IAAI,CAAC,EAE5E,KAAgB,EAAQ,QAAQ,MAAM,EAAE,WAAW,SAAS,EAC5D,KAAgB,EAAmB,GAAS,EAAQ,EACpD,KAAiB,EAAmB,GAAS,EAE7C,IAAW,EAAQ,QAAQ,MAAM;EACrC,IAAM,IAAc,GAAG,EAAE,KAAK,GAAG,EAAE,SAAS,GAAG,EAAE,cAAc,GAAG,EAAE,QACjE,aAAa,CACb,SAAS,EAAO,aAAa,CAAC,EAC3B,IAAc,MAAiB,SAAS,EAAE,WAAW;AAC3D,SAAO,KAAe;GACtB,EACI,KAAQ,EAAS,MAAM,GAAQ,IAAS,EAAS;AAgEvD,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,qBAAoC,CAAA;MAC/C,CAAA;KACL,kBAAC,KAAD;MAAG,WAAU;gBAAb;OACG,EAAQ;OAAO;OAAY,EAAQ;OAAO;OACzC;;KACJ,kBAAC,GAAD;MAAa,WAAU;gBAAO;MAGhB,CAAA;KACV,EAAA,CAAA,EACN,kBAAC,GAAD;KAAQ,MAAK;KAAK,eAAe,EAAM,WAAW;eAAlD,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,aAE1B;OACL;;GAEN,kBAAC,IAAD;IACE,cAAW;IACX,SAAQ;IACR,OAAM;IACN,WAAW,MAAO;AAChB,KAAI,MAAO,aACT,GAAS,oCAAoC;;IAGjD,MAAM,CACJ;KAAE,IAAI;KAAW,OAAO;KAAW,OAAO,EAAQ;KAAQ,EAC1D;KAAE,IAAI;KAAW,OAAO;KAAW,OAAO,EAAQ;KAAQ,CAC3D;IACD,CAAA;GAEF,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MAAU,OAAM;MAAiB,OAAO,GAAc;MAAU,CAAA;KAChE,kBAAC,GAAD;MAAU,OAAM;MAAU,OAAO,EAAQ;MAAU,CAAA;KACnD,kBAAC,GAAD;MAAU,OAAM;MAAuB,OAAO,EAAa,GAAc;MAAI,CAAA;KAC7E,kBAAC,GAAD;MAAU,OAAM;MAAe,OAAO,EAAa,GAAe;MAAI,CAAA;KAClE;;GAEN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,oBAA4B,CAAA,EAC5B,CAAA,EACb,kBAAC,GAAD;IAAa,WAAU;cAAvB;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,IAAD,EAAQ,WAAU,wEAAyE,CAAA,EAC3F,kBAAC,GAAD;QACE,aAAY;QACZ,WAAU;QACV,OAAO;QACP,WAAW,MAAU;AAEnB,SADA,GAAU,EAAM,OAAO,MAAM,EAC7B,EAAQ,EAAE;;QAEZ,CAAA,CACE;UACN,kBAAC,GAAD;OACE,OAAO;OACP,gBAAgB,MAAU;AAExB,QADA,GAAgB,EAAM,EACtB,EAAQ,EAAE;;iBAJd,CAOE,kBAAC,GAAD;QAAe,WAAU;QAAiB,cAAW;kBACnD,kBAAC,IAAD,EAAe,CAAA;QACD,CAAA,EAChB,kBAAC,GAAD,EAAA,UAAA;QACE,kBAAC,GAAD;SAAY,OAAM;mBAAM;SAAyB,CAAA;QACjD,kBAAC,GAAD;SAAY,OAAM;mBAAS;SAAmB,CAAA;QAC9C,kBAAC,GAAD;SAAY,OAAM;mBAAW;SAAqB,CAAA;QAClD,kBAAC,GAAD;SAAY,OAAM;mBAAU;SAAoB,CAAA;QAClC,EAAA,CAAA,CACT;SACL;;KACN,kBAAC,GAAD;MACE,MAAM;MACN,SAAS,MAAM,EAAE;MACR,SAxI8B;OAC/C;QACE,KAAK;QACL,QAAQ;QACR,UAAU;QACV,OAAO,MACL,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAa,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC;UAAE,MAAM;UAAM,CAAA,EACrD,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,KAAD;WAAG,WAAU;qBAAwB,EAAE;WAAS,CAAA,EAChD,kBAAC,KAAD;WAAG,WAAU;qBAAwC,EAAE,YAAY;WAAQ,CAAA,CACvE;YACF;;QAET;OACD;QACE,KAAK;QACL,QAAQ;QACR,UAAU;QACV,OAAO,MACL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAW,EAAE,iBAAiB;SAAQ,CAAA,EACnD,kBAAC,KAAD;SAAG,WAAU;mBAA+B,EAAE;SAAU,CAAA,CACpD,EAAA,CAAA;QAET;OACD;QACE,KAAK;QACL,QAAQ;QACR,UAAU;QACV,OAAO,MACL,kBAAC,GAAD;SAAO,SAAS,EAAc,EAAE,OAAO;SAAE,WAAU;mBAChD,EAAgB,EAAE,OAAO;SACpB,CAAA;QAEX;OACD;QACE,KAAK;QACL,QAAQ;QACR,UAAU;QACV,iBAAiB;QACjB,eAAe;QACf,OAAO,MAAM,kBAAC,QAAD;SAAM,WAAU;mBAAgB,EAAE;SAAgB,CAAA;QAChE;OACD;QACE,KAAK;QACL,QAAQ;QACR,UAAU;QACV,iBAAiB;QACjB,eAAe;QACf,OAAO,MAAM,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CAA+B,KAAE,EAAE,QAAQ,gBAAgB,CAAQ;;QACjF;OACF;MAqFS,YACE,kBAAC,GAAD;OACE,cAAa;OACb,OAAM;OACN,aAAY;OACZ,CAAA;MAEJ,aAAa,MACX,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;QAAqB,SAAA;kBACnB,kBAAC,GAAD;SACE,SAAQ;SACR,MAAK;SACL,WAAU;SACV,cAAY,oBAAoB,EAAE;mBAElC,kBAAC,GAAD,EAAgB,WAAU,WAAY,CAAA;SAC/B,CAAA;QACW,CAAA,EACtB,kBAAC,GAAD;QAAqB,OAAM;kBAA3B;SACE,kBAAC,GAAD;UAAkB,gBAAgB,EAAM,UAAU,EAAE;oBAApD,CACE,kBAAC,GAAD,EAAQ,WAAU,gBAAiB,CAAA,EAAA,OAElB;;SACnB,kBAAC,GAAD,EAAyB,CAAA;SACzB,kBAAC,GAAD;UACE,WAAU;UACV,gBAAgB,EAAgB,EAAE;oBAFpC,CAIE,kBAAC,IAAD,EAAQ,WAAU,gBAAiB,CAAA,EAAA,SAElB;;SACC;UACT,EAAA,CAAA;OACX,CAAA;MAER,CAAA;KACD,EAAS,SAAS,KACjB,kBAAC,GAAD;MACQ;MACI;MACV,YAAY,EAAS;MACrB,cAAc;MACd,CAAA;KAEQ;MACT,EAAA,CAAA;GAEP,kBAAC,IAAD;IACE,MAAM,EAAM;IACZ,cAAc,EAAM;IACpB,OAAO,EAAM,UAAU,gBAAgB;IACvC,aAAY;IACZ,QAAQ;IACR,eAAe,EAAM,UAAU,EAAgB,EAAM,QAAQ,GAAG,KAAA;IAChE,aAAa,EAAM,UAAU,iBAAiB;IAC9C,UA1Me,OAAO,MAAuB;AACjD,KAAI,EAAM,UACR,MAAM,GAAa,EAAM,QAAQ,IAAI,EAAe,GAAQ,EAAM,QAAQ,CAAC,GAE3E,MAAM,GAAU,EAAe,EAAO,CAAC;;IAuMrC,CAAA;GAEF,kBAAC,IAAD;IACE,MAAM,MAAiB;IACvB,eAAe,MAAS;AACtB,KAAK,KACH,EAAgB,KAAK;;cAIzB,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAkB,kBAAiC,CAAA,EACnD,kBAAC,IAAD,EAAA,UACG,IACG,iBAAiB,EAAa,KAAK,+DACnC,IACmB,CAAA,CACP,EAAA,CAAA,EACpB,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAmB,UAA0B,CAAA,EAC7C,kBAAC,IAAD;KACE,UAAU,MAAU;AAKlB,MAJA,EAAM,gBAAgB,EAClB,KACG,GAAa,EAAa,GAAG,EAEpC,EAAgB,KAAK;;eAExB;KAEmB,CAAA,CACF,EAAA,CAAA,CACD,EAAA,CAAA;IACT,CAAA;GACV"}
1
+ {"version":3,"file":"CompanyClientManagementPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/CompanyClientManagementPagePage.tsx"],"sourcesContent":["import { 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 Card,\n CardContent,\n CardHeader,\n CardTitle,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n GradientText,\n IllustratedEmptyState,\n Input,\n PagePurpose,\n Pagination,\n ResponsiveTable,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n type ResponsiveTableColumn,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { MoreHorizontal, Pencil, Plus, Search, Trash2 } 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 { useCrewFoundryStore, type Client } from '../store/crewfoundryStore';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\nimport { AvatarStack, SegmentedTabs, StatCard } from '../components/viz';\nimport { clientRevenue, totalContractValue } from '../lib/companyFinance';\n\nconst CLIENT_FIELDS: FieldConfig[] = [\n { name: 'name', label: 'Client name', type: 'text', required: true },\n { name: 'industry', label: 'Industry', type: 'text' },\n { name: 'contactPerson', label: 'Contact person', type: 'text' },\n { name: 'email', label: 'Contact email', type: 'email' },\n { name: 'phone', label: 'Phone', type: 'text' },\n {\n name: 'status',\n label: 'Status',\n type: 'select',\n options: [\n { label: 'Active', value: 'active' },\n { label: 'Prospect', value: 'prospect' },\n { label: 'Churned', value: 'churned' },\n ],\n },\n { name: 'revenue', label: 'Revenue (USD)', type: 'number' },\n { name: 'projects', label: 'Projects', type: 'number' },\n { name: 'since', label: 'Client since', type: 'date' },\n];\n\nconst toInitialValues = (client: Client): FormValues => ({\n name: client.name,\n industry: client.industry,\n contactPerson: client.contactPerson,\n email: client.email,\n phone: client.phone,\n status: client.status,\n revenue: client.revenue,\n projects: client.projects,\n since: client.since ? client.since.slice(0, 10) : '',\n});\n\nconst valuesToClient = (values: FormValues, base?: Client): Client => ({\n id: base?.id ?? crypto.randomUUID(),\n name: String(values.name ?? ''),\n industry: String(values.industry ?? ''),\n contactPerson: String(values.contactPerson ?? ''),\n email: String(values.email ?? ''),\n phone: String(values.phone ?? ''),\n status: (values.status as Client['status']) || 'prospect',\n revenue: typeof values.revenue === 'number' ? values.revenue : Number(values.revenue) || 0,\n projects: typeof values.projects === 'number' ? values.projects : Number(values.projects) || 0,\n since: String(values.since ?? ''),\n});\n\nconst statusVariant = (status: Client['status']) =>\n status === 'active' ? 'default' : status === 'prospect' ? 'secondary' : 'destructive';\n\nconst compactMoney = (value: number): string => {\n if (value >= 1_000_000) {\n return `$${(value / 1_000_000).toFixed(1)}M`;\n }\n if (value >= 1_000) {\n return `$${Math.round(value / 1_000)}k`;\n }\n return `$${Math.round(value)}`;\n};\n\nexport function CompanyClientManagementPagePage() {\n const { clients, vendors, addClient, updateClient, removeClient } = useCrewFoundryStore();\n const navigate = useNavigate();\n const modal = useFormModal<Client>();\n const [search, setSearch] = useState('');\n const [filterStatus, setFilterStatus] = useState<string>('all');\n const [deleteTarget, setDeleteTarget] = useState<Client | null>(null);\n const { page, pageSize, offset, setPage } = usePagination({ initialPageSize: 25 });\n\n const activeClients = clients.filter((c) => c.status === 'active');\n const contractValue = totalContractValue(clients, vendors);\n const revenue = clientRevenue(clients);\n\n const filtered = clients.filter((c) => {\n const matchSearch = `${c.name} ${c.industry} ${c.contactPerson} ${c.email}`\n .toLowerCase()\n .includes(search.toLowerCase());\n const matchStatus = filterStatus === 'all' || c.status === filterStatus;\n return matchSearch && matchStatus;\n });\n const paged = filtered.slice(offset, offset + pageSize);\n\n const handleSubmit = async (values: FormValues) => {\n if (modal.context) {\n await updateClient(modal.context.id, valuesToClient(values, modal.context));\n } else {\n await addClient(valuesToClient(values));\n }\n };\n\n const columns: ResponsiveTableColumn<Client>[] = [\n {\n key: 'name',\n header: 'Client',\n priority: 'primary',\n cell: (c) => (\n <div className=\"flex items-center gap-3\">\n <AvatarStack people={[{ name: c.name }]} size={36} />\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium\">{c.name}</p>\n <p className=\"truncate text-xs text-text-secondary\">{c.industry || '—'}</p>\n </div>\n </div>\n ),\n },\n {\n key: 'contact',\n header: 'Primary Contact',\n priority: 'secondary',\n cell: (c) => (\n <div>\n <p className=\"text-xs\">{c.contactPerson || '—'}</p>\n <p className=\"text-xs text-text-secondary\">{c.email}</p>\n </div>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n priority: 'secondary',\n cell: (c) => (\n <Badge variant={statusVariant(c.status)} className=\"text-[10px]\">\n {formatEnumLabel(c.status)}\n </Badge>\n ),\n },\n {\n key: 'projects',\n header: 'Projects',\n priority: 'hidden-on-mobile',\n headerClassName: 'text-right',\n cellClassName: 'text-right',\n cell: (c) => <span className=\"tabular-nums\">{c.projects}</span>,\n },\n {\n key: 'revenue',\n header: 'Value',\n priority: 'secondary',\n headerClassName: 'text-right',\n cellClassName: 'text-right',\n cell: (c) => <span className=\"tabular-nums\">${c.revenue.toLocaleString()}</span>,\n },\n ];\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Clients &amp; Vendors</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {clients.length} clients · {vendors.length} vendors\n </p>\n <PagePurpose className=\"mt-1\">\n Keep track of the clients you work with — their contacts, status, and engagements — so\n account relationships never slip through the cracks.\n </PagePurpose>\n </div>\n <Button size=\"sm\" onClick={() => modal.openModal()}>\n <Plus className=\"h-4 w-4 mr-1\" />\n Add Client\n </Button>\n </div>\n\n <SegmentedTabs\n aria-label=\"Clients and vendors\"\n variant=\"underline\"\n value=\"clients\"\n onChange={(id) => {\n if (id === 'vendors') {\n navigate('../company/vendor-management-page');\n }\n }}\n tabs={[\n { id: 'clients', label: 'Clients', count: clients.length },\n { id: 'vendors', label: 'Vendors', count: vendors.length },\n ]}\n />\n\n <div className=\"grid gap-4 sm:grid-cols-2 xl:grid-cols-4\">\n <StatCard label=\"Active Clients\" value={activeClients.length} />\n <StatCard label=\"Vendors\" value={vendors.length} />\n <StatCard label=\"Total Contract Value\" value={compactMoney(contractValue)} />\n <StatCard label=\"Client Revenue\" value={compactMoney(revenue)} />\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Client Portfolio</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\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 clients...\"\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={filterStatus}\n onValueChange={(value) => {\n setFilterStatus(value);\n setPage(1);\n }}\n >\n <SelectTrigger className=\"w-full sm:w-40\" aria-label=\"Filter by status\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">All statuses</SelectItem>\n <SelectItem value=\"active\">Active</SelectItem>\n <SelectItem value=\"prospect\">Prospect</SelectItem>\n <SelectItem value=\"churned\">Churned</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <ResponsiveTable\n data={paged}\n rowKey={(c) => c.id}\n columns={columns}\n emptyState={\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No clients match\"\n description=\"Try a different search term or status filter.\"\n />\n }\n rowActions={(c) => (\n <div className=\"flex items-center justify-end\">\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 ${c.name}`}\n >\n <MoreHorizontal className=\"h-4 w-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem onSelect={() => modal.openModal(c)}>\n <Pencil className=\"mr-2 h-4 w-4\" />\n Edit\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"text-status-error-text\"\n onSelect={() => setDeleteTarget(c)}\n >\n <Trash2 className=\"mr-2 h-4 w-4\" />\n Delete\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\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 <FormModal\n open={modal.open}\n onOpenChange={modal.onOpenChange}\n title={modal.context ? 'Edit client' : 'Add client'}\n description=\"Track the client relationship, contacts, and engagement value.\"\n fields={CLIENT_FIELDS}\n initialValues={modal.context ? toInitialValues(modal.context) : undefined}\n submitLabel={modal.context ? 'Save changes' : 'Add client'}\n onSubmit={handleSubmit}\n />\n\n <AlertDialog\n open={deleteTarget !== null}\n onOpenChange={(open) => {\n if (!open) {\n setDeleteTarget(null);\n }\n }}\n >\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Delete client?</AlertDialogTitle>\n <AlertDialogDescription>\n {deleteTarget\n ? `This removes “${deleteTarget.name}” from your client portfolio. This action cannot be undone.`\n : ''}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Cancel</AlertDialogCancel>\n <AlertDialogAction\n onClick={(event) => {\n event.preventDefault();\n if (deleteTarget) {\n void removeClient(deleteTarget.id);\n }\n setDeleteTarget(null);\n }}\n >\n Delete\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CompanyVendorManagementPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/CompanyVendorManagementPagePage.tsx"],"names":[],"mappings":"AAsGA,wBAAgB,+BAA+B,gCAgQ9C"}
1
+ {"version":3,"file":"CompanyVendorManagementPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/CompanyVendorManagementPagePage.tsx"],"names":[],"mappings":"AAsGA,wBAAgB,+BAA+B,gCA+P9C"}
@@ -5,7 +5,7 @@ import { AvatarStack as r } from "../components/viz/AvatarStack.js";
5
5
  import { SegmentedTabs as ee } from "../components/viz/SegmentedTabs.js";
6
6
  import "../components/viz/index.js";
7
7
  import { FormModal as te, useFormModal as i } from "../components/forms/FormModal.js";
8
- import { thisQuarterRevenue as a, totalContractValue as o } from "../lib/companyFinance.js";
8
+ import { clientRevenue as a, totalContractValue as o } from "../lib/companyFinance.js";
9
9
  import { useState as s } from "react";
10
10
  import { Mail as c, MoreHorizontal as l, Pencil as u, Plus as d, Search as f, Star as ne, Trash2 as re } from "lucide-react";
11
11
  import { useNavigate as ie } from "react-router";
@@ -91,10 +91,10 @@ var z = [
91
91
  rating: typeof e.rating == "number" ? e.rating : Number(e.rating) || 0
92
92
  }), H = (e) => e >= 1e6 ? `$${(e / 1e6).toFixed(1)}M` : e >= 1e3 ? `$${Math.round(e / 1e3)}k` : `$${Math.round(e)}`;
93
93
  function U() {
94
- let { vendors: U, clients: W, invoices: de, addVendor: fe, updateVendor: pe, removeVendor: me } = e(), he = ie(), G = i(), [K, ge] = s(""), [q, _e] = s("all"), [J, Y] = s(null), { page: ve, pageSize: X, offset: Z, setPage: Q } = ue({ initialPageSize: 12 }), ye = W.filter((e) => e.status === "active"), be = o(W, U), xe = a(de), $ = U.filter((e) => {
94
+ let { vendors: U, clients: W, addVendor: de, updateVendor: fe, removeVendor: pe } = e(), me = ie(), G = i(), [K, he] = s(""), [q, ge] = s("all"), [J, Y] = s(null), { page: _e, pageSize: X, offset: Z, setPage: Q } = ue({ initialPageSize: 12 }), ve = W.filter((e) => e.status === "active"), ye = o(W, U), be = a(W), $ = U.filter((e) => {
95
95
  let t = `${e.name} ${e.category} ${e.contactPerson} ${e.email}`.toLowerCase().includes(K.toLowerCase()), n = q === "all" || e.status === q;
96
96
  return t && n;
97
- }), Se = $.slice(Z, Z + X);
97
+ }), xe = $.slice(Z, Z + X);
98
98
  return /* @__PURE__ */ m("div", {
99
99
  className: "space-y-6 p-6",
100
100
  children: [
@@ -129,7 +129,7 @@ function U() {
129
129
  variant: "underline",
130
130
  value: "vendors",
131
131
  onChange: (e) => {
132
- e === "clients" && he("../company/client-management-page");
132
+ e === "clients" && me("../company/client-management-page");
133
133
  },
134
134
  tabs: [{
135
135
  id: "clients",
@@ -146,7 +146,7 @@ function U() {
146
146
  children: [
147
147
  /* @__PURE__ */ p(n, {
148
148
  label: "Active Clients",
149
- value: ye.length
149
+ value: ve.length
150
150
  }),
151
151
  /* @__PURE__ */ p(n, {
152
152
  label: "Vendors",
@@ -154,11 +154,11 @@ function U() {
154
154
  }),
155
155
  /* @__PURE__ */ p(n, {
156
156
  label: "Total Contract Value",
157
- value: H(be)
157
+ value: H(ye)
158
158
  }),
159
159
  /* @__PURE__ */ p(n, {
160
- label: "This Quarter",
161
- value: H(xe)
160
+ label: "Client Revenue",
161
+ value: H(be)
162
162
  })
163
163
  ]
164
164
  }),
@@ -171,13 +171,13 @@ function U() {
171
171
  className: "pl-9",
172
172
  value: K,
173
173
  onChange: (e) => {
174
- ge(e.target.value), Q(1);
174
+ he(e.target.value), Q(1);
175
175
  }
176
176
  })]
177
177
  }), /* @__PURE__ */ m(P, {
178
178
  value: q,
179
179
  onValueChange: (e) => {
180
- _e(e), Q(1);
180
+ ge(e), Q(1);
181
181
  },
182
182
  children: [/* @__PURE__ */ p(L, {
183
183
  className: "w-full sm:w-40",
@@ -219,7 +219,7 @@ function U() {
219
219
  title: "No vendors match",
220
220
  description: "Try a different search term or status filter."
221
221
  })
222
- }) : Se.map((e) => /* @__PURE__ */ m(O, {
222
+ }) : xe.map((e) => /* @__PURE__ */ m(O, {
223
223
  treatment: "glass",
224
224
  children: [/* @__PURE__ */ p(x, { children: /* @__PURE__ */ m("div", {
225
225
  className: "flex items-center justify-between gap-2",
@@ -305,7 +305,7 @@ function U() {
305
305
  }, e.id))
306
306
  }),
307
307
  $.length > X && /* @__PURE__ */ p(N, {
308
- page: ve,
308
+ page: _e,
309
309
  pageSize: X,
310
310
  totalItems: $.length,
311
311
  onPageChange: Q
@@ -319,7 +319,7 @@ function U() {
319
319
  initialValues: G.context ? B(G.context) : void 0,
320
320
  submitLabel: G.context ? "Save changes" : "Add vendor",
321
321
  onSubmit: async (e) => {
322
- G.context ? await pe(G.context.id, V(e, G.context)) : await fe(V(e));
322
+ G.context ? await fe(G.context.id, V(e, G.context)) : await de(V(e));
323
323
  }
324
324
  }),
325
325
  /* @__PURE__ */ p(ae, {
@@ -329,7 +329,7 @@ function U() {
329
329
  },
330
330
  children: /* @__PURE__ */ m(ce, { children: [/* @__PURE__ */ m(g, { children: [/* @__PURE__ */ p(_, { children: "Delete vendor?" }), /* @__PURE__ */ p(le, { children: J ? `This removes “${J.name}” from your vendor directory. This action cannot be undone.` : "" })] }), /* @__PURE__ */ m(h, { children: [/* @__PURE__ */ p(se, { children: "Cancel" }), /* @__PURE__ */ p(oe, {
331
331
  onClick: (e) => {
332
- e.preventDefault(), J && me(J.id), Y(null);
332
+ e.preventDefault(), J && pe(J.id), Y(null);
333
333
  },
334
334
  children: "Delete"
335
335
  })] })] })
@@ -1 +1 @@
1
- {"version":3,"file":"CompanyVendorManagementPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/CompanyVendorManagementPagePage.tsx"],"sourcesContent":["import { 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} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { Mail, MoreHorizontal, Pencil, Plus, Search, Star, Trash2 } 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 { useCrewFoundryStore, type Vendor } from '../store/crewfoundryStore';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\nimport { AvatarStack, SegmentedTabs, StatCard } from '../components/viz';\nimport { totalContractValue, thisQuarterRevenue } from '../lib/companyFinance';\n\nconst VENDOR_FIELDS: FieldConfig[] = [\n { name: 'name', label: 'Vendor name', type: 'text', required: true },\n { name: 'category', label: 'Category', type: 'text', required: true },\n { name: 'contactPerson', label: 'Contact person', type: 'text' },\n { name: 'email', label: 'Contact email', type: 'email' },\n { name: 'phone', label: 'Phone', type: 'text' },\n {\n name: 'status',\n label: 'Status',\n type: 'select',\n options: [\n { label: 'Active', value: 'active' },\n { label: 'Inactive', value: 'inactive' },\n { label: 'Pending', value: 'pending' },\n ],\n },\n { name: 'contractValue', label: 'Contract value (USD)', type: 'number' },\n { name: 'rating', label: 'Rating (0-5)', type: 'number' },\n];\n\nconst toInitialValues = (vendor: Vendor): FormValues => ({\n name: vendor.name,\n category: vendor.category,\n contactPerson: vendor.contactPerson,\n email: vendor.email,\n phone: vendor.phone,\n status: vendor.status,\n contractValue: vendor.contractValue,\n rating: vendor.rating,\n});\n\nconst valuesToVendor = (values: FormValues, base?: Vendor): Vendor => ({\n id: base?.id ?? crypto.randomUUID(),\n name: String(values.name ?? ''),\n category: String(values.category ?? ''),\n contactPerson: String(values.contactPerson ?? ''),\n email: String(values.email ?? ''),\n phone: String(values.phone ?? ''),\n status: (values.status as Vendor['status']) || 'pending',\n contractValue:\n typeof values.contractValue === 'number'\n ? values.contractValue\n : Number(values.contractValue) || 0,\n rating: typeof values.rating === 'number' ? values.rating : Number(values.rating) || 0,\n});\n\nconst compactMoney = (value: number): string => {\n if (value >= 1_000_000) {\n return `$${(value / 1_000_000).toFixed(1)}M`;\n }\n if (value >= 1_000) {\n return `$${Math.round(value / 1_000)}k`;\n }\n return `$${Math.round(value)}`;\n};\n\nexport function CompanyVendorManagementPagePage() {\n const { vendors, clients, invoices, addVendor, updateVendor, removeVendor } =\n useCrewFoundryStore();\n const navigate = useNavigate();\n const modal = useFormModal<Vendor>();\n const [search, setSearch] = useState('');\n const [filterStatus, setFilterStatus] = useState<string>('all');\n const [deleteTarget, setDeleteTarget] = useState<Vendor | null>(null);\n const { page, pageSize, offset, setPage } = usePagination({ initialPageSize: 12 });\n\n const activeClients = clients.filter((c) => c.status === 'active');\n const contractValue = totalContractValue(clients, vendors);\n const quarterRevenue = thisQuarterRevenue(invoices);\n\n const filtered = vendors.filter((v) => {\n const matchSearch = `${v.name} ${v.category} ${v.contactPerson} ${v.email}`\n .toLowerCase()\n .includes(search.toLowerCase());\n const matchStatus = filterStatus === 'all' || v.status === filterStatus;\n return matchSearch && matchStatus;\n });\n const paged = filtered.slice(offset, offset + pageSize);\n\n const handleSubmit = async (values: FormValues) => {\n if (modal.context) {\n await updateVendor(modal.context.id, valuesToVendor(values, modal.context));\n } else {\n await addVendor(valuesToVendor(values));\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Clients &amp; Vendors</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {clients.length} clients · {vendors.length} vendors\n </p>\n <PagePurpose className=\"mt-1\">\n Track the vendors and suppliers you depend on — contracts, contacts, and status — to\n manage external relationships in one place.\n </PagePurpose>\n </div>\n <Button size=\"sm\" onClick={() => modal.openModal()}>\n <Plus className=\"h-4 w-4 mr-1\" />\n Add Vendor\n </Button>\n </div>\n\n <SegmentedTabs\n aria-label=\"Clients and vendors\"\n variant=\"underline\"\n value=\"vendors\"\n onChange={(id) => {\n if (id === 'clients') {\n navigate('../company/client-management-page');\n }\n }}\n tabs={[\n { id: 'clients', label: 'Clients', count: clients.length },\n { id: 'vendors', label: 'Vendors', count: vendors.length },\n ]}\n />\n\n <div className=\"grid gap-4 sm:grid-cols-2 xl:grid-cols-4\">\n <StatCard label=\"Active Clients\" value={activeClients.length} />\n <StatCard label=\"Vendors\" value={vendors.length} />\n <StatCard label=\"Total Contract Value\" value={compactMoney(contractValue)} />\n <StatCard label=\"This Quarter\" value={compactMoney(quarterRevenue)} />\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 vendors...\"\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={filterStatus}\n onValueChange={(value) => {\n setFilterStatus(value);\n setPage(1);\n }}\n >\n <SelectTrigger className=\"w-full sm:w-40\" aria-label=\"Filter by status\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">All statuses</SelectItem>\n <SelectItem value=\"active\">Active</SelectItem>\n <SelectItem value=\"inactive\">Inactive</SelectItem>\n <SelectItem value=\"pending\">Pending</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {vendors.length === 0 ? (\n <div className=\"col-span-full\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No vendors yet\"\n description=\"Add a vendor to start tracking external relationships.\"\n />\n </div>\n ) : filtered.length === 0 ? (\n <div className=\"col-span-full\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No vendors match\"\n description=\"Try a different search term or status filter.\"\n />\n </div>\n ) : (\n paged.map((vendor) => (\n <GlassCard key={vendor.id} treatment=\"glass\">\n <CardHeader>\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <AvatarStack people={[{ name: vendor.name }]} size={36} />\n <CardTitle className=\"truncate text-base\">{vendor.name}</CardTitle>\n </div>\n <div className=\"flex items-center gap-1\">\n <Badge\n variant={vendor.status === 'active' ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {formatEnumLabel(vendor.status)}\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 ${vendor.name}`}\n >\n <MoreHorizontal className=\"h-4 w-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem onSelect={() => modal.openModal(vendor)}>\n <Pencil className=\"mr-2 h-4 w-4\" />\n Edit\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"text-status-error-text\"\n onSelect={() => setDeleteTarget(vendor)}\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>\n <div className=\"space-y-2\">\n <Badge variant=\"outline\" className=\"text-[10px]\">\n {vendor.category}\n </Badge>\n <div className=\"grid grid-cols-2 gap-2 mt-2\">\n <div>\n <p className=\"text-xs text-text-secondary\">Contact</p>\n <p className=\"text-sm\">{vendor.contactPerson || '—'}</p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">Contract Value</p>\n <p className=\"text-sm font-medium tabular-nums\">\n ${vendor.contractValue.toLocaleString()}\n </p>\n </div>\n </div>\n <div className=\"flex items-center justify-between pt-2 border-t mt-2\">\n <div className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <Mail className=\"h-3 w-3\" />\n {vendor.email || '—'}\n </div>\n <div className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-status-warning-text\" />\n <span className=\"text-sm tabular-nums\">{vendor.rating}</span>\n </div>\n </div>\n </div>\n </CardContent>\n </GlassCard>\n ))\n )}\n </div>\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 vendor' : 'Add vendor'}\n description=\"Track the vendor, its contract value, and primary contact.\"\n fields={VENDOR_FIELDS}\n initialValues={modal.context ? toInitialValues(modal.context) : undefined}\n submitLabel={modal.context ? 'Save changes' : 'Add vendor'}\n onSubmit={handleSubmit}\n />\n\n <AlertDialog\n open={deleteTarget !== null}\n onOpenChange={(open) => {\n if (!open) {\n setDeleteTarget(null);\n }\n }}\n >\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Delete vendor?</AlertDialogTitle>\n <AlertDialogDescription>\n {deleteTarget\n ? `This removes “${deleteTarget.name}” from your vendor directory. This action cannot be undone.`\n : ''}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Cancel</AlertDialogCancel>\n <AlertDialogAction\n onClick={(event) => {\n event.preventDefault();\n if (deleteTarget) {\n void removeVendor(deleteTarget.id);\n }\n setDeleteTarget(null);\n }}\n >\n Delete\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AA8CA,IAAM,IAA+B;CACnC;EAAE,MAAM;EAAQ,OAAO;EAAe,MAAM;EAAQ,UAAU;EAAM;CACpE;EAAE,MAAM;EAAY,OAAO;EAAY,MAAM;EAAQ,UAAU;EAAM;CACrE;EAAE,MAAM;EAAiB,OAAO;EAAkB,MAAM;EAAQ;CAChE;EAAE,MAAM;EAAS,OAAO;EAAiB,MAAM;EAAS;CACxD;EAAE,MAAM;EAAS,OAAO;EAAS,MAAM;EAAQ;CAC/C;EACE,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;GACP;IAAE,OAAO;IAAU,OAAO;IAAU;GACpC;IAAE,OAAO;IAAY,OAAO;IAAY;GACxC;IAAE,OAAO;IAAW,OAAO;IAAW;GACvC;EACF;CACD;EAAE,MAAM;EAAiB,OAAO;EAAwB,MAAM;EAAU;CACxE;EAAE,MAAM;EAAU,OAAO;EAAgB,MAAM;EAAU;CAC1D,EAEK,KAAmB,OAAgC;CACvD,MAAM,EAAO;CACb,UAAU,EAAO;CACjB,eAAe,EAAO;CACtB,OAAO,EAAO;CACd,OAAO,EAAO;CACd,QAAQ,EAAO;CACf,eAAe,EAAO;CACtB,QAAQ,EAAO;CAChB,GAEK,KAAkB,GAAoB,OAA2B;CACrE,IAAI,GAAM,MAAM,OAAO,YAAY;CACnC,MAAM,OAAO,EAAO,QAAQ,GAAG;CAC/B,UAAU,OAAO,EAAO,YAAY,GAAG;CACvC,eAAe,OAAO,EAAO,iBAAiB,GAAG;CACjD,OAAO,OAAO,EAAO,SAAS,GAAG;CACjC,OAAO,OAAO,EAAO,SAAS,GAAG;CACjC,QAAS,EAAO,UAA+B;CAC/C,eACE,OAAO,EAAO,iBAAkB,WAC5B,EAAO,gBACP,OAAO,EAAO,cAAc,IAAI;CACtC,QAAQ,OAAO,EAAO,UAAW,WAAW,EAAO,SAAS,OAAO,EAAO,OAAO,IAAI;CACtF,GAEK,KAAgB,MAChB,KAAS,MACJ,KAAK,IAAQ,KAAW,QAAQ,EAAE,CAAC,KAExC,KAAS,MACJ,IAAI,KAAK,MAAM,IAAQ,IAAM,CAAC,KAEhC,IAAI,KAAK,MAAM,EAAM;AAG9B,SAAgB,IAAkC;CAChD,IAAM,EAAE,YAAS,YAAS,cAAU,eAAW,kBAAc,qBAC3D,GAAqB,EACjB,KAAW,IAAa,EACxB,IAAQ,GAAsB,EAC9B,CAAC,GAAQ,MAAa,EAAS,GAAG,EAClC,CAAC,GAAc,MAAmB,EAAiB,MAAM,EACzD,CAAC,GAAc,KAAmB,EAAwB,KAAK,EAC/D,EAAE,UAAM,aAAU,WAAQ,eAAY,GAAc,EAAE,iBAAiB,IAAI,CAAC,EAE5E,KAAgB,EAAQ,QAAQ,MAAM,EAAE,WAAW,SAAS,EAC5D,KAAgB,EAAmB,GAAS,EAAQ,EACpD,KAAiB,EAAmB,GAAS,EAE7C,IAAW,EAAQ,QAAQ,MAAM;EACrC,IAAM,IAAc,GAAG,EAAE,KAAK,GAAG,EAAE,SAAS,GAAG,EAAE,cAAc,GAAG,EAAE,QACjE,aAAa,CACb,SAAS,EAAO,aAAa,CAAC,EAC3B,IAAc,MAAiB,SAAS,EAAE,WAAW;AAC3D,SAAO,KAAe;GACtB,EACI,KAAQ,EAAS,MAAM,GAAQ,IAAS,EAAS;AAUvD,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,qBAAoC,CAAA;MAC/C,CAAA;KACL,kBAAC,KAAD;MAAG,WAAU;gBAAb;OACG,EAAQ;OAAO;OAAY,EAAQ;OAAO;OACzC;;KACJ,kBAAC,GAAD;MAAa,WAAU;gBAAO;MAGhB,CAAA;KACV,EAAA,CAAA,EACN,kBAAC,GAAD;KAAQ,MAAK;KAAK,eAAe,EAAM,WAAW;eAAlD,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,aAE1B;OACL;;GAEN,kBAAC,IAAD;IACE,cAAW;IACX,SAAQ;IACR,OAAM;IACN,WAAW,MAAO;AAChB,KAAI,MAAO,aACT,GAAS,oCAAoC;;IAGjD,MAAM,CACJ;KAAE,IAAI;KAAW,OAAO;KAAW,OAAO,EAAQ;KAAQ,EAC1D;KAAE,IAAI;KAAW,OAAO;KAAW,OAAO,EAAQ;KAAQ,CAC3D;IACD,CAAA;GAEF,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MAAU,OAAM;MAAiB,OAAO,GAAc;MAAU,CAAA;KAChE,kBAAC,GAAD;MAAU,OAAM;MAAU,OAAO,EAAQ;MAAU,CAAA;KACnD,kBAAC,GAAD;MAAU,OAAM;MAAuB,OAAO,EAAa,GAAc;MAAI,CAAA;KAC7E,kBAAC,GAAD;MAAU,OAAM;MAAe,OAAO,EAAa,GAAe;MAAI,CAAA;KAClE;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,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,EAAM,EACtB,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;iBAAM;OAAyB,CAAA;MACjD,kBAAC,GAAD;OAAY,OAAM;iBAAS;OAAmB,CAAA;MAC9C,kBAAC,GAAD;OAAY,OAAM;iBAAW;OAAqB,CAAA;MAClD,kBAAC,GAAD;OAAY,OAAM;iBAAU;OAAoB,CAAA;MAClC,EAAA,CAAA,CACT;OACL;;GAEN,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAQ,WAAW,IAClB,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MACE,cAAa;MACb,OAAM;MACN,aAAY;MACZ,CAAA;KACE,CAAA,GACJ,EAAS,WAAW,IACtB,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MACE,cAAa;MACb,OAAM;MACN,aAAY;MACZ,CAAA;KACE,CAAA,GAEN,GAAM,KAAK,MACT,kBAAC,GAAD;KAA2B,WAAU;eAArC,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAa,QAAQ,CAAC,EAAE,MAAM,EAAO,MAAM,CAAC;QAAE,MAAM;QAAM,CAAA,EAC1D,kBAAC,GAAD;QAAW,WAAU;kBAAsB,EAAO;QAAiB,CAAA,CAC/D;UACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QACE,SAAS,EAAO,WAAW,WAAW,YAAY;QAClD,WAAU;kBAET,EAAgB,EAAO,OAAO;QACzB,CAAA,EACR,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;QAAqB,SAAA;kBACnB,kBAAC,GAAD;SACE,SAAQ;SACR,MAAK;SACL,WAAU;SACV,cAAY,oBAAoB,EAAO;mBAEvC,kBAAC,GAAD,EAAgB,WAAU,WAAY,CAAA;SAC/B,CAAA;QACW,CAAA,EACtB,kBAAC,GAAD;QAAqB,OAAM;kBAA3B;SACE,kBAAC,GAAD;UAAkB,gBAAgB,EAAM,UAAU,EAAO;oBAAzD,CACE,kBAAC,GAAD,EAAQ,WAAU,gBAAiB,CAAA,EAAA,OAElB;;SACnB,kBAAC,GAAD,EAAyB,CAAA;SACzB,kBAAC,GAAD;UACE,WAAU;UACV,gBAAgB,EAAgB,EAAO;oBAFzC,CAIE,kBAAC,IAAD,EAAQ,WAAU,gBAAiB,CAAA,EAAA,SAElB;;SACC;UACT,EAAA,CAAA,CACX;SACF;SACK,CAAA,EACb,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,GAAD;QAAO,SAAQ;QAAU,WAAU;kBAChC,EAAO;QACF,CAAA;OACR,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAA8B;SAAW,CAAA,EACtD,kBAAC,KAAD;SAAG,WAAU;mBAAW,EAAO,iBAAiB;SAAQ,CAAA,CACpD,EAAA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAA8B;SAAkB,CAAA,EAC7D,kBAAC,KAAD;SAAG,WAAU;mBAAb,CAAgD,KAC5C,EAAO,cAAc,gBAAgB,CACrC;WACA,EAAA,CAAA,CACF;;OACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAC3B,EAAO,SAAS,IACb;YACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,IAAD,EAAM,WAAU,oCAAqC,CAAA,EACrD,kBAAC,QAAD;UAAM,WAAU;oBAAwB,EAAO;UAAc,CAAA,CACzD;WACF;;OACF;SACM,CAAA,CACJ;OAxEI,EAAO,GAwEX,CACZ;IAEA,CAAA;GACL,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,EAAgB,EAAM,QAAQ,GAAG,KAAA;IAChE,aAAa,EAAM,UAAU,iBAAiB;IAC9C,UAnMe,OAAO,MAAuB;AACjD,KAAI,EAAM,UACR,MAAM,GAAa,EAAM,QAAQ,IAAI,EAAe,GAAQ,EAAM,QAAQ,CAAC,GAE3E,MAAM,GAAU,EAAe,EAAO,CAAC;;IAgMrC,CAAA;GAEF,kBAAC,IAAD;IACE,MAAM,MAAiB;IACvB,eAAe,MAAS;AACtB,KAAK,KACH,EAAgB,KAAK;;cAIzB,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAkB,kBAAiC,CAAA,EACnD,kBAAC,IAAD,EAAA,UACG,IACG,iBAAiB,EAAa,KAAK,+DACnC,IACmB,CAAA,CACP,EAAA,CAAA,EACpB,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAmB,UAA0B,CAAA,EAC7C,kBAAC,IAAD;KACE,UAAU,MAAU;AAKlB,MAJA,EAAM,gBAAgB,EAClB,KACG,GAAa,EAAa,GAAG,EAEpC,EAAgB,KAAK;;eAExB;KAEmB,CAAA,CACF,EAAA,CAAA,CACD,EAAA,CAAA;IACT,CAAA;GACV"}
1
+ {"version":3,"file":"CompanyVendorManagementPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/CompanyVendorManagementPagePage.tsx"],"sourcesContent":["import { 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} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { Mail, MoreHorizontal, Pencil, Plus, Search, Star, Trash2 } 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 { useCrewFoundryStore, type Vendor } from '../store/crewfoundryStore';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\nimport { AvatarStack, SegmentedTabs, StatCard } from '../components/viz';\nimport { clientRevenue, totalContractValue } from '../lib/companyFinance';\n\nconst VENDOR_FIELDS: FieldConfig[] = [\n { name: 'name', label: 'Vendor name', type: 'text', required: true },\n { name: 'category', label: 'Category', type: 'text', required: true },\n { name: 'contactPerson', label: 'Contact person', type: 'text' },\n { name: 'email', label: 'Contact email', type: 'email' },\n { name: 'phone', label: 'Phone', type: 'text' },\n {\n name: 'status',\n label: 'Status',\n type: 'select',\n options: [\n { label: 'Active', value: 'active' },\n { label: 'Inactive', value: 'inactive' },\n { label: 'Pending', value: 'pending' },\n ],\n },\n { name: 'contractValue', label: 'Contract value (USD)', type: 'number' },\n { name: 'rating', label: 'Rating (0-5)', type: 'number' },\n];\n\nconst toInitialValues = (vendor: Vendor): FormValues => ({\n name: vendor.name,\n category: vendor.category,\n contactPerson: vendor.contactPerson,\n email: vendor.email,\n phone: vendor.phone,\n status: vendor.status,\n contractValue: vendor.contractValue,\n rating: vendor.rating,\n});\n\nconst valuesToVendor = (values: FormValues, base?: Vendor): Vendor => ({\n id: base?.id ?? crypto.randomUUID(),\n name: String(values.name ?? ''),\n category: String(values.category ?? ''),\n contactPerson: String(values.contactPerson ?? ''),\n email: String(values.email ?? ''),\n phone: String(values.phone ?? ''),\n status: (values.status as Vendor['status']) || 'pending',\n contractValue:\n typeof values.contractValue === 'number'\n ? values.contractValue\n : Number(values.contractValue) || 0,\n rating: typeof values.rating === 'number' ? values.rating : Number(values.rating) || 0,\n});\n\nconst compactMoney = (value: number): string => {\n if (value >= 1_000_000) {\n return `$${(value / 1_000_000).toFixed(1)}M`;\n }\n if (value >= 1_000) {\n return `$${Math.round(value / 1_000)}k`;\n }\n return `$${Math.round(value)}`;\n};\n\nexport function CompanyVendorManagementPagePage() {\n const { vendors, clients, addVendor, updateVendor, removeVendor } = useCrewFoundryStore();\n const navigate = useNavigate();\n const modal = useFormModal<Vendor>();\n const [search, setSearch] = useState('');\n const [filterStatus, setFilterStatus] = useState<string>('all');\n const [deleteTarget, setDeleteTarget] = useState<Vendor | null>(null);\n const { page, pageSize, offset, setPage } = usePagination({ initialPageSize: 12 });\n\n const activeClients = clients.filter((c) => c.status === 'active');\n const contractValue = totalContractValue(clients, vendors);\n const revenue = clientRevenue(clients);\n\n const filtered = vendors.filter((v) => {\n const matchSearch = `${v.name} ${v.category} ${v.contactPerson} ${v.email}`\n .toLowerCase()\n .includes(search.toLowerCase());\n const matchStatus = filterStatus === 'all' || v.status === filterStatus;\n return matchSearch && matchStatus;\n });\n const paged = filtered.slice(offset, offset + pageSize);\n\n const handleSubmit = async (values: FormValues) => {\n if (modal.context) {\n await updateVendor(modal.context.id, valuesToVendor(values, modal.context));\n } else {\n await addVendor(valuesToVendor(values));\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Clients &amp; Vendors</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {clients.length} clients · {vendors.length} vendors\n </p>\n <PagePurpose className=\"mt-1\">\n Track the vendors and suppliers you depend on — contracts, contacts, and status — to\n manage external relationships in one place.\n </PagePurpose>\n </div>\n <Button size=\"sm\" onClick={() => modal.openModal()}>\n <Plus className=\"h-4 w-4 mr-1\" />\n Add Vendor\n </Button>\n </div>\n\n <SegmentedTabs\n aria-label=\"Clients and vendors\"\n variant=\"underline\"\n value=\"vendors\"\n onChange={(id) => {\n if (id === 'clients') {\n navigate('../company/client-management-page');\n }\n }}\n tabs={[\n { id: 'clients', label: 'Clients', count: clients.length },\n { id: 'vendors', label: 'Vendors', count: vendors.length },\n ]}\n />\n\n <div className=\"grid gap-4 sm:grid-cols-2 xl:grid-cols-4\">\n <StatCard label=\"Active Clients\" value={activeClients.length} />\n <StatCard label=\"Vendors\" value={vendors.length} />\n <StatCard label=\"Total Contract Value\" value={compactMoney(contractValue)} />\n <StatCard label=\"Client Revenue\" value={compactMoney(revenue)} />\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 vendors...\"\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={filterStatus}\n onValueChange={(value) => {\n setFilterStatus(value);\n setPage(1);\n }}\n >\n <SelectTrigger className=\"w-full sm:w-40\" aria-label=\"Filter by status\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">All statuses</SelectItem>\n <SelectItem value=\"active\">Active</SelectItem>\n <SelectItem value=\"inactive\">Inactive</SelectItem>\n <SelectItem value=\"pending\">Pending</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {vendors.length === 0 ? (\n <div className=\"col-span-full\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No vendors yet\"\n description=\"Add a vendor to start tracking external relationships.\"\n />\n </div>\n ) : filtered.length === 0 ? (\n <div className=\"col-span-full\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No vendors match\"\n description=\"Try a different search term or status filter.\"\n />\n </div>\n ) : (\n paged.map((vendor) => (\n <GlassCard key={vendor.id} treatment=\"glass\">\n <CardHeader>\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <AvatarStack people={[{ name: vendor.name }]} size={36} />\n <CardTitle className=\"truncate text-base\">{vendor.name}</CardTitle>\n </div>\n <div className=\"flex items-center gap-1\">\n <Badge\n variant={vendor.status === 'active' ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {formatEnumLabel(vendor.status)}\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 ${vendor.name}`}\n >\n <MoreHorizontal className=\"h-4 w-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem onSelect={() => modal.openModal(vendor)}>\n <Pencil className=\"mr-2 h-4 w-4\" />\n Edit\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"text-status-error-text\"\n onSelect={() => setDeleteTarget(vendor)}\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>\n <div className=\"space-y-2\">\n <Badge variant=\"outline\" className=\"text-[10px]\">\n {vendor.category}\n </Badge>\n <div className=\"grid grid-cols-2 gap-2 mt-2\">\n <div>\n <p className=\"text-xs text-text-secondary\">Contact</p>\n <p className=\"text-sm\">{vendor.contactPerson || '—'}</p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">Contract Value</p>\n <p className=\"text-sm font-medium tabular-nums\">\n ${vendor.contractValue.toLocaleString()}\n </p>\n </div>\n </div>\n <div className=\"flex items-center justify-between pt-2 border-t mt-2\">\n <div className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <Mail className=\"h-3 w-3\" />\n {vendor.email || '—'}\n </div>\n <div className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-status-warning-text\" />\n <span className=\"text-sm tabular-nums\">{vendor.rating}</span>\n </div>\n </div>\n </div>\n </CardContent>\n </GlassCard>\n ))\n )}\n </div>\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 vendor' : 'Add vendor'}\n description=\"Track the vendor, its contract value, and primary contact.\"\n fields={VENDOR_FIELDS}\n initialValues={modal.context ? toInitialValues(modal.context) : undefined}\n submitLabel={modal.context ? 'Save changes' : 'Add vendor'}\n onSubmit={handleSubmit}\n />\n\n <AlertDialog\n open={deleteTarget !== null}\n onOpenChange={(open) => {\n if (!open) {\n setDeleteTarget(null);\n }\n }}\n >\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Delete vendor?</AlertDialogTitle>\n <AlertDialogDescription>\n {deleteTarget\n ? `This removes “${deleteTarget.name}” from your vendor directory. This action cannot be undone.`\n : ''}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Cancel</AlertDialogCancel>\n <AlertDialogAction\n onClick={(event) => {\n event.preventDefault();\n if (deleteTarget) {\n void removeVendor(deleteTarget.id);\n }\n setDeleteTarget(null);\n }}\n >\n Delete\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"HelpApiDocumentationPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/HelpApiDocumentationPagePage.tsx"],"names":[],"mappings":"AAyGA,wBAAgB,4BAA4B,gCAiS3C"}
1
+ {"version":3,"file":"HelpApiDocumentationPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/HelpApiDocumentationPagePage.tsx"],"names":[],"mappings":"AA0OA,wBAAgB,4BAA4B,gCA6S3C"}