@burdenoff/microfe-crewfoundry 2026.703.2 → 2026.704.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 (130) hide show
  1. package/dist/crewfoundry/components/forms/FormModal.js.map +1 -1
  2. package/dist/crewfoundry/pages/AdminAdminPagePage.js +117 -164
  3. package/dist/crewfoundry/pages/AdminAdminPagePage.js.map +1 -1
  4. package/dist/crewfoundry/pages/AnalyticsAnalyticsPagePage.js +99 -82
  5. package/dist/crewfoundry/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
  6. package/dist/crewfoundry/pages/AnalyticsEmployeeAnalyticsPagePage.js +160 -192
  7. package/dist/crewfoundry/pages/AnalyticsEmployeeAnalyticsPagePage.js.map +1 -1
  8. package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js +238 -231
  9. package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js.map +1 -1
  10. package/dist/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.js +196 -166
  11. package/dist/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.js.map +1 -1
  12. package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js.map +1 -1
  13. package/dist/crewfoundry/pages/CommunicationsCallsPagePage.js +45 -147
  14. package/dist/crewfoundry/pages/CommunicationsCallsPagePage.js.map +1 -1
  15. package/dist/crewfoundry/pages/CommunicationsDirectoryPagePage.js +20 -11
  16. package/dist/crewfoundry/pages/CommunicationsDirectoryPagePage.js.map +1 -1
  17. package/dist/crewfoundry/pages/CommunicationsFilesPagePage.js +45 -152
  18. package/dist/crewfoundry/pages/CommunicationsFilesPagePage.js.map +1 -1
  19. package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.js +50 -138
  20. package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.js.map +1 -1
  21. package/dist/crewfoundry/pages/CommunicationsTeamsPagePage.js +149 -67
  22. package/dist/crewfoundry/pages/CommunicationsTeamsPagePage.js.map +1 -1
  23. package/dist/crewfoundry/pages/CommunicationsVideoCallsPagePage.js +50 -201
  24. package/dist/crewfoundry/pages/CommunicationsVideoCallsPagePage.js.map +1 -1
  25. package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js +205 -136
  26. package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js.map +1 -1
  27. package/dist/crewfoundry/pages/CompanyCompanyDashboardPagePage.js +25 -31
  28. package/dist/crewfoundry/pages/CompanyCompanyDashboardPagePage.js.map +1 -1
  29. package/dist/crewfoundry/pages/CompanyCompanyProfilePagePage.js +118 -104
  30. package/dist/crewfoundry/pages/CompanyCompanyProfilePagePage.js.map +1 -1
  31. package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js +117 -58
  32. package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js.map +1 -1
  33. package/dist/crewfoundry/pages/DashboardAnalyticsPagePage.js +90 -94
  34. package/dist/crewfoundry/pages/DashboardAnalyticsPagePage.js.map +1 -1
  35. package/dist/crewfoundry/pages/DashboardDashboardPagePage.js +210 -191
  36. package/dist/crewfoundry/pages/DashboardDashboardPagePage.js.map +1 -1
  37. package/dist/crewfoundry/pages/DashboardEnhancedDashboardPagePage.js +213 -180
  38. package/dist/crewfoundry/pages/DashboardEnhancedDashboardPagePage.js.map +1 -1
  39. package/dist/crewfoundry/pages/DashboardExecutiveDashboardPage.js +133 -264
  40. package/dist/crewfoundry/pages/DashboardExecutiveDashboardPage.js.map +1 -1
  41. package/dist/crewfoundry/pages/HelpApiDocumentationPagePage.js +279 -212
  42. package/dist/crewfoundry/pages/HelpApiDocumentationPagePage.js.map +1 -1
  43. package/dist/crewfoundry/pages/HiringApplicationsPagePage.js +131 -105
  44. package/dist/crewfoundry/pages/HiringApplicationsPagePage.js.map +1 -1
  45. package/dist/crewfoundry/pages/HiringCandidatesPagePage.js +167 -148
  46. package/dist/crewfoundry/pages/HiringCandidatesPagePage.js.map +1 -1
  47. package/dist/crewfoundry/pages/HiringCreateJobPagePage.js +3 -3
  48. package/dist/crewfoundry/pages/HiringCreateJobPagePage.js.map +1 -1
  49. package/dist/crewfoundry/pages/HiringInterviewsPagePage.js +13 -5
  50. package/dist/crewfoundry/pages/HiringInterviewsPagePage.js.map +1 -1
  51. package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js +151 -152
  52. package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js.map +1 -1
  53. package/dist/crewfoundry/pages/HiringJobsPagePage.js +38 -21
  54. package/dist/crewfoundry/pages/HiringJobsPagePage.js.map +1 -1
  55. package/dist/crewfoundry/pages/HrDepartmentsPagePage.js +3 -3
  56. package/dist/crewfoundry/pages/HrDepartmentsPagePage.js.map +1 -1
  57. package/dist/crewfoundry/pages/HrEmployeesPagePage.js +134 -127
  58. package/dist/crewfoundry/pages/HrEmployeesPagePage.js.map +1 -1
  59. package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js +163 -132
  60. package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js.map +1 -1
  61. package/dist/crewfoundry/pages/HrPayrollPagePage.js +202 -160
  62. package/dist/crewfoundry/pages/HrPayrollPagePage.js.map +1 -1
  63. package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js +104 -91
  64. package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js.map +1 -1
  65. package/dist/crewfoundry/pages/LearningAssessmentsPagePage.js +29 -147
  66. package/dist/crewfoundry/pages/LearningAssessmentsPagePage.js.map +1 -1
  67. package/dist/crewfoundry/pages/LearningCertificatesPagePage.js +22 -106
  68. package/dist/crewfoundry/pages/LearningCertificatesPagePage.js.map +1 -1
  69. package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js +74 -74
  70. package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js.map +1 -1
  71. package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js +101 -97
  72. package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js.map +1 -1
  73. package/dist/crewfoundry/pages/LearningCoursesPagePage.js +64 -47
  74. package/dist/crewfoundry/pages/LearningCoursesPagePage.js.map +1 -1
  75. package/dist/crewfoundry/pages/LearningLearningAnalyticsPagePage.js +111 -121
  76. package/dist/crewfoundry/pages/LearningLearningAnalyticsPagePage.js.map +1 -1
  77. package/dist/crewfoundry/pages/LearningLearningPagePage.js +131 -110
  78. package/dist/crewfoundry/pages/LearningLearningPagePage.js.map +1 -1
  79. package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js +65 -64
  80. package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js.map +1 -1
  81. package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js +58 -43
  82. package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js.map +1 -1
  83. package/dist/crewfoundry/pages/LearningMyLearningPagePage.js +66 -82
  84. package/dist/crewfoundry/pages/LearningMyLearningPagePage.js.map +1 -1
  85. package/dist/crewfoundry/pages/MarketplaceBookingManagementPagePage.js +38 -88
  86. package/dist/crewfoundry/pages/MarketplaceBookingManagementPagePage.js.map +1 -1
  87. package/dist/crewfoundry/pages/MarketplaceEarningsPagePage.js +59 -65
  88. package/dist/crewfoundry/pages/MarketplaceEarningsPagePage.js.map +1 -1
  89. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.js +209 -323
  90. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.js.map +1 -1
  91. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js +175 -220
  92. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js.map +1 -1
  93. package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js +74 -59
  94. package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js.map +1 -1
  95. package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js +96 -80
  96. package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js.map +1 -1
  97. package/dist/crewfoundry/pages/MarketplaceMessagingPagePage.js +28 -100
  98. package/dist/crewfoundry/pages/MarketplaceMessagingPagePage.js.map +1 -1
  99. package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.js +27 -20
  100. package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.js.map +1 -1
  101. package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.js +73 -73
  102. package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.js.map +1 -1
  103. package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js +62 -56
  104. package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js.map +1 -1
  105. package/dist/crewfoundry/pages/PaymentsEscrowPagePage.js +40 -138
  106. package/dist/crewfoundry/pages/PaymentsEscrowPagePage.js.map +1 -1
  107. package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js +126 -93
  108. package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js.map +1 -1
  109. package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.js +99 -77
  110. package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.js.map +1 -1
  111. package/dist/crewfoundry/pages/PaymentsPaymentMethodsPagePage.js.map +1 -1
  112. package/dist/crewfoundry/pages/PaymentsSecurityPagePage.js +73 -74
  113. package/dist/crewfoundry/pages/PaymentsSecurityPagePage.js.map +1 -1
  114. package/dist/crewfoundry/pages/ProjectsCreateProjectPagePage.js +1 -1
  115. package/dist/crewfoundry/pages/ProjectsCreateProjectPagePage.js.map +1 -1
  116. package/dist/crewfoundry/pages/ProjectsProjectDetailsPagePage.js +122 -122
  117. package/dist/crewfoundry/pages/ProjectsProjectDetailsPagePage.js.map +1 -1
  118. package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js +130 -113
  119. package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js.map +1 -1
  120. package/dist/crewfoundry/pages/TeamsTeamAnalyticsPagePage.js +68 -59
  121. package/dist/crewfoundry/pages/TeamsTeamAnalyticsPagePage.js.map +1 -1
  122. package/dist/crewfoundry/pages/TeamsTeamDetailPagePage.js +73 -62
  123. package/dist/crewfoundry/pages/TeamsTeamDetailPagePage.js.map +1 -1
  124. package/dist/crewfoundry/pages/TeamsTeamDirectoryPagePage.js +82 -58
  125. package/dist/crewfoundry/pages/TeamsTeamDirectoryPagePage.js.map +1 -1
  126. package/dist/crewfoundry/pages/TeamsTeamSettingsPagePage.js +153 -153
  127. package/dist/crewfoundry/pages/TeamsTeamSettingsPagePage.js.map +1 -1
  128. package/dist/crewfoundry/pages/TeamsTeamsPagePage.js +63 -48
  129. package/dist/crewfoundry/pages/TeamsTeamsPagePage.js.map +1 -1
  130. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"FormModal.js","names":[],"sources":["../../../../src/crewfoundry/components/forms/FormModal.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\nimport {\n Button,\n Input,\n Label,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n Switch,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { X } from 'lucide-react';\n\n// ── Public API ──────────────────────────────────────────────────\nexport type FormFieldType =\n | 'text'\n | 'number'\n | 'date'\n | 'email'\n | 'textarea'\n | 'select'\n | 'tags'\n | 'switch';\n\nexport type FormValue = string | number | boolean | string[];\nexport type FormValues = Record<string, FormValue>;\n\nexport interface FieldOption {\n label: string;\n value: string;\n}\n\nexport interface FieldConfig {\n name: string;\n label: string;\n type: FormFieldType;\n required?: boolean;\n placeholder?: string;\n options?: FieldOption[];\n /** Column span in the 2-col grid on md+ (defaults to 1; textarea/tags default to 2). */\n colSpan?: 1 | 2;\n}\n\nexport interface FormModalProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n title: string;\n description?: string;\n fields: FieldConfig[];\n initialValues?: FormValues;\n submitLabel?: string;\n onSubmit: (values: FormValues) => Promise<void>;\n}\n\n// ── Internal state helpers ──────────────────────────────────────\n// Text-like inputs are held as strings for controlled inputs; switches as\n// booleans; tags as string[]. Values are coerced back to their public shapes\n// on submit.\ntype InternalValue = string | boolean | string[];\ntype InternalValues = Record<string, InternalValue>;\n\n\nconst buildInitialState = (fields: FieldConfig[], initialValues?: FormValues): InternalValues => {\n const state: InternalValues = {};\n for (const field of fields) {\n const raw = initialValues?.[field.name];\n if (field.type === 'switch') {\n state[field.name] = Boolean(raw);\n } else if (field.type === 'tags') {\n if (Array.isArray(raw)) {\n state[field.name] = raw.map((item) => String(item));\n } else if (typeof raw === 'string' && raw.length > 0) {\n state[field.name] = raw\n .split(',')\n .map((item) => item.trim())\n .filter(Boolean);\n } else {\n state[field.name] = [];\n }\n } else {\n state[field.name] = raw === undefined || raw === null ? '' : String(raw);\n }\n }\n return state;\n};\n\nconst isEmptyValue = (field: FieldConfig, value: InternalValue): boolean => {\n if (field.type === 'switch') {\n return false;\n }\n if (field.type === 'tags') {\n return !Array.isArray(value) || value.length === 0;\n }\n return typeof value !== 'string' || value.trim() === '';\n};\n\nconst toOutput = (fields: FieldConfig[], state: InternalValues): FormValues => {\n const output: FormValues = {};\n for (const field of fields) {\n const value = state[field.name];\n if (field.type === 'switch') {\n output[field.name] = Boolean(value);\n } else if (field.type === 'tags') {\n output[field.name] = Array.isArray(value) ? value : [];\n } else if (field.type === 'number') {\n const raw = typeof value === 'string' ? value.trim() : '';\n output[field.name] = raw === '' ? '' : Number(raw);\n } else {\n output[field.name] = typeof value === 'string' ? value : '';\n }\n }\n return output;\n};\n\n// Native <select> styled with the same semantic tokens as fe-libs Input.\nconst SELECT_CLASS =\n 'flex h-10 w-full rounded-input border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary transition-colors duration-200 focus-visible:border-border-focus focus-visible:outline-none focus-visible:shadow-[var(--shadow-focus)] focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)] disabled:cursor-not-allowed disabled:opacity-50';\n\n// ── Component ───────────────────────────────────────────────────\nexport function FormModal({\n open,\n onOpenChange,\n title,\n description,\n fields,\n initialValues,\n submitLabel = 'Save',\n onSubmit,\n}: FormModalProps) {\n const { showToast } = useGlobalUi();\n const [values, setValues] = useState<InternalValues>(() =>\n buildInitialState(fields, initialValues)\n );\n const [tagDrafts, setTagDrafts] = useState<Record<string, string>>({});\n const [submitting, setSubmitting] = useState(false);\n\n // Keep the latest field config / initial values reachable from the reset\n // effect without making them effect dependencies (page callers rarely\n // memoize these, and depending on them would loop or clobber edits).\n const fieldsRef = useRef(fields);\n const initialValuesRef = useRef(initialValues);\n const wasOpenRef = useRef(false);\n\n // Sync the latest field config / initial values into refs after each commit\n // (never during render). Declared before the reset effect so that on a rising\n // edge of `open` the refs are refreshed first, then read by the reset below.\n useEffect(() => {\n fieldsRef.current = fields;\n initialValuesRef.current = initialValues;\n }, [fields, initialValues]);\n\n // Reset the working copy on each rising edge of `open` so edit vs create\n // contexts never leak into each other.\n useEffect(() => {\n if (open && !wasOpenRef.current) {\n setValues(buildInitialState(fieldsRef.current, initialValuesRef.current));\n setTagDrafts({});\n setSubmitting(false);\n }\n wasOpenRef.current = open;\n }, [open]);\n\n const setFieldValue = useCallback((name: string, value: InternalValue) => {\n setValues((prev) => ({ ...prev, [name]: value }));\n }, []);\n\n const commitTag = useCallback(\n (name: string) => {\n setTagDrafts((prevDrafts) => {\n const draft = (prevDrafts[name] ?? '').trim();\n if (draft) {\n setValues((prev) => {\n const current = Array.isArray(prev[name]) ? (prev[name] as string[]) : [];\n if (current.includes(draft)) {\n return prev;\n }\n return { ...prev, [name]: [...current, draft] };\n });\n }\n return { ...prevDrafts, [name]: '' };\n });\n },\n []\n );\n\n const removeTag = useCallback((name: string, tag: string) => {\n setValues((prev) => {\n const current = Array.isArray(prev[name]) ? (prev[name] as string[]) : [];\n return { ...prev, [name]: current.filter((item) => item !== tag) };\n });\n }, []);\n\n const handleSubmit = useCallback(\n async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n if (submitting) {\n return;\n }\n\n // Fold any un-committed tag draft into its field before validating.\n const staged: InternalValues = { ...values };\n for (const field of fields) {\n if (field.type === 'tags') {\n const draft = (tagDrafts[field.name] ?? '').trim();\n if (draft) {\n const current = Array.isArray(staged[field.name])\n ? (staged[field.name] as string[])\n : [];\n if (!current.includes(draft)) {\n staged[field.name] = [...current, draft];\n }\n }\n }\n }\n\n const missing = fields.find((field) => field.required && isEmptyValue(field, staged[field.name]));\n if (missing) {\n showToast(`${missing.label} is required`, 'warning');\n return;\n }\n\n setSubmitting(true);\n try {\n await onSubmit(toOutput(fields, staged));\n onOpenChange(false);\n } catch (error) {\n showToast(error instanceof Error ? error.message : 'Something went wrong', 'error');\n } finally {\n setSubmitting(false);\n }\n },\n [fields, onOpenChange, onSubmit, showToast, submitting, tagDrafts, values]\n );\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-[560px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>{title}</ResponsiveDialogTitle>\n {description ? (\n <ResponsiveDialogDescription>{description}</ResponsiveDialogDescription>\n ) : null}\n </ResponsiveDialogHeader>\n\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n {fields.map((field) => {\n const fieldId = `formmodal-${field.name}`;\n const spanTwo =\n field.colSpan === 2 ||\n (field.colSpan === undefined && (field.type === 'textarea' || field.type === 'tags'));\n const value = values[field.name];\n\n return (\n <div\n key={field.name}\n className={`flex flex-col gap-1.5 ${spanTwo ? 'md:col-span-2' : ''}`}\n >\n {field.type === 'switch' ? (\n <div className=\"flex items-center justify-between gap-3 rounded-input border border-border-default bg-bg-surface px-3 py-2\">\n <Label htmlFor={fieldId}>\n {field.label}\n {field.required ? <span className=\"text-status-error-fg\"> *</span> : null}\n </Label>\n <Switch\n id={fieldId}\n checked={Boolean(value)}\n onCheckedChange={(checked) => setFieldValue(field.name, checked)}\n disabled={submitting}\n />\n </div>\n ) : (\n <>\n <Label htmlFor={fieldId}>\n {field.label}\n {field.required ? <span className=\"text-status-error-fg\"> *</span> : null}\n </Label>\n\n {field.type === 'textarea' ? (\n <Textarea\n id={fieldId}\n value={typeof value === 'string' ? value : ''}\n placeholder={field.placeholder}\n disabled={submitting}\n onChange={(event) => setFieldValue(field.name, event.target.value)}\n />\n ) : field.type === 'select' ? (\n <select\n id={fieldId}\n className={SELECT_CLASS}\n value={typeof value === 'string' ? value : ''}\n disabled={submitting}\n onChange={(event) => setFieldValue(field.name, event.target.value)}\n >\n <option value=\"\" disabled>\n {field.placeholder ?? 'Select…'}\n </option>\n {(field.options ?? []).map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n ) : field.type === 'tags' ? (\n <div className=\"flex min-h-10 flex-wrap items-center gap-1.5 rounded-input border border-border-default bg-bg-surface px-2 py-1.5\">\n {(Array.isArray(value) ? value : []).map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-primary\"\n >\n {tag}\n <button\n type=\"button\"\n aria-label={`Remove ${tag}`}\n className=\"text-text-secondary hover:text-text-primary\"\n onClick={() => removeTag(field.name, tag)}\n disabled={submitting}\n >\n <X className=\"h-3 w-3\" />\n </button>\n </span>\n ))}\n <input\n id={fieldId}\n className=\"min-w-[6rem] flex-1 bg-transparent text-sm text-text-primary outline-none placeholder:text-text-placeholder\"\n value={tagDrafts[field.name] ?? ''}\n placeholder={field.placeholder ?? 'Add and press Enter'}\n disabled={submitting}\n onChange={(event) =>\n setTagDrafts((prev) => ({ ...prev, [field.name]: event.target.value }))\n }\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ',') {\n event.preventDefault();\n commitTag(field.name);\n } else if (\n event.key === 'Backspace' &&\n (tagDrafts[field.name] ?? '') === ''\n ) {\n const current = Array.isArray(value) ? value : [];\n if (current.length > 0) {\n removeTag(field.name, current[current.length - 1]);\n }\n }\n }}\n onBlur={() => commitTag(field.name)}\n />\n </div>\n ) : (\n <Input\n id={fieldId}\n type={\n field.type === 'number'\n ? 'number'\n : field.type === 'date'\n ? 'date'\n : field.type === 'email'\n ? 'email'\n : 'text'\n }\n value={typeof value === 'string' ? value : ''}\n placeholder={field.placeholder}\n disabled={submitting}\n onChange={(event) => setFieldValue(field.name, event.target.value)}\n />\n )}\n </>\n )}\n </div>\n );\n })}\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => onOpenChange(false)}\n disabled={submitting}\n >\n Cancel\n </Button>\n <Button type=\"submit\" disabled={submitting}>\n {submitting ? 'Saving…' : submitLabel}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ── Optional open/close helper ──────────────────────────────────\nexport interface UseFormModalResult<TContext> {\n open: boolean;\n /** The row/context passed to openModal (e.g. the entity being edited). */\n context: TContext | null;\n openModal: (context?: TContext) => void;\n closeModal: () => void;\n onOpenChange: (open: boolean) => void;\n}\n\n/**\n * Tiny helper for pages that toggle a single FormModal and want to remember\n * which row is being edited. `context` is null for a create flow.\n */\nexport function useFormModal<TContext = void>(): UseFormModalResult<TContext> {\n const [open, setOpen] = useState(false);\n const [context, setContext] = useState<TContext | null>(null);\n\n const openModal = useCallback((next?: TContext) => {\n setContext(next ?? null);\n setOpen(true);\n }, []);\n\n const closeModal = useCallback(() => setOpen(false), []);\n\n const onOpenChange = useCallback((nextOpen: boolean) => {\n setOpen(nextOpen);\n if (!nextOpen) {\n setContext(null);\n }\n }, []);\n\n return { open, context, openModal, closeModal, onOpenChange };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"FormModal.js","names":[],"sources":["../../../../src/crewfoundry/components/forms/FormModal.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\nimport {\n Button,\n Input,\n Label,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n Switch,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { X } from 'lucide-react';\n\n// ── Public API ──────────────────────────────────────────────────\nexport type FormFieldType =\n | 'text'\n | 'number'\n | 'date'\n | 'email'\n | 'textarea'\n | 'select'\n | 'tags'\n | 'switch';\n\nexport type FormValue = string | number | boolean | string[];\nexport type FormValues = Record<string, FormValue>;\n\nexport interface FieldOption {\n label: string;\n value: string;\n}\n\nexport interface FieldConfig {\n name: string;\n label: string;\n type: FormFieldType;\n required?: boolean;\n placeholder?: string;\n options?: FieldOption[];\n /** Column span in the 2-col grid on md+ (defaults to 1; textarea/tags default to 2). */\n colSpan?: 1 | 2;\n}\n\nexport interface FormModalProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n title: string;\n description?: string;\n fields: FieldConfig[];\n initialValues?: FormValues;\n submitLabel?: string;\n onSubmit: (values: FormValues) => Promise<void>;\n}\n\n// ── Internal state helpers ──────────────────────────────────────\n// Text-like inputs are held as strings for controlled inputs; switches as\n// booleans; tags as string[]. Values are coerced back to their public shapes\n// on submit.\ntype InternalValue = string | boolean | string[];\ntype InternalValues = Record<string, InternalValue>;\n\nconst buildInitialState = (fields: FieldConfig[], initialValues?: FormValues): InternalValues => {\n const state: InternalValues = {};\n for (const field of fields) {\n const raw = initialValues?.[field.name];\n if (field.type === 'switch') {\n state[field.name] = Boolean(raw);\n } else if (field.type === 'tags') {\n if (Array.isArray(raw)) {\n state[field.name] = raw.map((item) => String(item));\n } else if (typeof raw === 'string' && raw.length > 0) {\n state[field.name] = raw\n .split(',')\n .map((item) => item.trim())\n .filter(Boolean);\n } else {\n state[field.name] = [];\n }\n } else {\n state[field.name] = raw === undefined || raw === null ? '' : String(raw);\n }\n }\n return state;\n};\n\nconst isEmptyValue = (field: FieldConfig, value: InternalValue): boolean => {\n if (field.type === 'switch') {\n return false;\n }\n if (field.type === 'tags') {\n return !Array.isArray(value) || value.length === 0;\n }\n return typeof value !== 'string' || value.trim() === '';\n};\n\nconst toOutput = (fields: FieldConfig[], state: InternalValues): FormValues => {\n const output: FormValues = {};\n for (const field of fields) {\n const value = state[field.name];\n if (field.type === 'switch') {\n output[field.name] = Boolean(value);\n } else if (field.type === 'tags') {\n output[field.name] = Array.isArray(value) ? value : [];\n } else if (field.type === 'number') {\n const raw = typeof value === 'string' ? value.trim() : '';\n output[field.name] = raw === '' ? '' : Number(raw);\n } else {\n output[field.name] = typeof value === 'string' ? value : '';\n }\n }\n return output;\n};\n\n// Native <select> styled with the same semantic tokens as fe-libs Input.\nconst SELECT_CLASS =\n 'flex h-10 w-full rounded-input border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary transition-colors duration-200 focus-visible:border-border-focus focus-visible:outline-none focus-visible:shadow-[var(--shadow-focus)] focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)] disabled:cursor-not-allowed disabled:opacity-50';\n\n// ── Component ───────────────────────────────────────────────────\nexport function FormModal({\n open,\n onOpenChange,\n title,\n description,\n fields,\n initialValues,\n submitLabel = 'Save',\n onSubmit,\n}: FormModalProps) {\n const { showToast } = useGlobalUi();\n const [values, setValues] = useState<InternalValues>(() =>\n buildInitialState(fields, initialValues)\n );\n const [tagDrafts, setTagDrafts] = useState<Record<string, string>>({});\n const [submitting, setSubmitting] = useState(false);\n\n // Keep the latest field config / initial values reachable from the reset\n // effect without making them effect dependencies (page callers rarely\n // memoize these, and depending on them would loop or clobber edits).\n const fieldsRef = useRef(fields);\n const initialValuesRef = useRef(initialValues);\n const wasOpenRef = useRef(false);\n\n // Sync the latest field config / initial values into refs after each commit\n // (never during render). Declared before the reset effect so that on a rising\n // edge of `open` the refs are refreshed first, then read by the reset below.\n useEffect(() => {\n fieldsRef.current = fields;\n initialValuesRef.current = initialValues;\n }, [fields, initialValues]);\n\n // Reset the working copy on each rising edge of `open` so edit vs create\n // contexts never leak into each other.\n useEffect(() => {\n if (open && !wasOpenRef.current) {\n setValues(buildInitialState(fieldsRef.current, initialValuesRef.current));\n setTagDrafts({});\n setSubmitting(false);\n }\n wasOpenRef.current = open;\n }, [open]);\n\n const setFieldValue = useCallback((name: string, value: InternalValue) => {\n setValues((prev) => ({ ...prev, [name]: value }));\n }, []);\n\n const commitTag = useCallback((name: string) => {\n setTagDrafts((prevDrafts) => {\n const draft = (prevDrafts[name] ?? '').trim();\n if (draft) {\n setValues((prev) => {\n const current = Array.isArray(prev[name]) ? (prev[name] as string[]) : [];\n if (current.includes(draft)) {\n return prev;\n }\n return { ...prev, [name]: [...current, draft] };\n });\n }\n return { ...prevDrafts, [name]: '' };\n });\n }, []);\n\n const removeTag = useCallback((name: string, tag: string) => {\n setValues((prev) => {\n const current = Array.isArray(prev[name]) ? (prev[name] as string[]) : [];\n return { ...prev, [name]: current.filter((item) => item !== tag) };\n });\n }, []);\n\n const handleSubmit = useCallback(\n async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n if (submitting) {\n return;\n }\n\n // Fold any un-committed tag draft into its field before validating.\n const staged: InternalValues = { ...values };\n for (const field of fields) {\n if (field.type === 'tags') {\n const draft = (tagDrafts[field.name] ?? '').trim();\n if (draft) {\n const current = Array.isArray(staged[field.name])\n ? (staged[field.name] as string[])\n : [];\n if (!current.includes(draft)) {\n staged[field.name] = [...current, draft];\n }\n }\n }\n }\n\n const missing = fields.find(\n (field) => field.required && isEmptyValue(field, staged[field.name])\n );\n if (missing) {\n showToast(`${missing.label} is required`, 'warning');\n return;\n }\n\n setSubmitting(true);\n try {\n await onSubmit(toOutput(fields, staged));\n onOpenChange(false);\n } catch (error) {\n showToast(error instanceof Error ? error.message : 'Something went wrong', 'error');\n } finally {\n setSubmitting(false);\n }\n },\n [fields, onOpenChange, onSubmit, showToast, submitting, tagDrafts, values]\n );\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-[560px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>{title}</ResponsiveDialogTitle>\n {description ? (\n <ResponsiveDialogDescription>{description}</ResponsiveDialogDescription>\n ) : null}\n </ResponsiveDialogHeader>\n\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n {fields.map((field) => {\n const fieldId = `formmodal-${field.name}`;\n const spanTwo =\n field.colSpan === 2 ||\n (field.colSpan === undefined &&\n (field.type === 'textarea' || field.type === 'tags'));\n const value = values[field.name];\n\n return (\n <div\n key={field.name}\n className={`flex flex-col gap-1.5 ${spanTwo ? 'md:col-span-2' : ''}`}\n >\n {field.type === 'switch' ? (\n <div className=\"flex items-center justify-between gap-3 rounded-input border border-border-default bg-bg-surface px-3 py-2\">\n <Label htmlFor={fieldId}>\n {field.label}\n {field.required ? <span className=\"text-status-error-fg\"> *</span> : null}\n </Label>\n <Switch\n id={fieldId}\n checked={Boolean(value)}\n onCheckedChange={(checked) => setFieldValue(field.name, checked)}\n disabled={submitting}\n />\n </div>\n ) : (\n <>\n <Label htmlFor={fieldId}>\n {field.label}\n {field.required ? <span className=\"text-status-error-fg\"> *</span> : null}\n </Label>\n\n {field.type === 'textarea' ? (\n <Textarea\n id={fieldId}\n value={typeof value === 'string' ? value : ''}\n placeholder={field.placeholder}\n disabled={submitting}\n onChange={(event) => setFieldValue(field.name, event.target.value)}\n />\n ) : field.type === 'select' ? (\n <select\n id={fieldId}\n className={SELECT_CLASS}\n value={typeof value === 'string' ? value : ''}\n disabled={submitting}\n onChange={(event) => setFieldValue(field.name, event.target.value)}\n >\n <option value=\"\" disabled>\n {field.placeholder ?? 'Select…'}\n </option>\n {(field.options ?? []).map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n ) : field.type === 'tags' ? (\n <div className=\"flex min-h-10 flex-wrap items-center gap-1.5 rounded-input border border-border-default bg-bg-surface px-2 py-1.5\">\n {(Array.isArray(value) ? value : []).map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-primary\"\n >\n {tag}\n <button\n type=\"button\"\n aria-label={`Remove ${tag}`}\n className=\"text-text-secondary hover:text-text-primary\"\n onClick={() => removeTag(field.name, tag)}\n disabled={submitting}\n >\n <X className=\"h-3 w-3\" />\n </button>\n </span>\n ))}\n <input\n id={fieldId}\n className=\"min-w-[6rem] flex-1 bg-transparent text-sm text-text-primary outline-none placeholder:text-text-placeholder\"\n value={tagDrafts[field.name] ?? ''}\n placeholder={field.placeholder ?? 'Add and press Enter'}\n disabled={submitting}\n onChange={(event) =>\n setTagDrafts((prev) => ({\n ...prev,\n [field.name]: event.target.value,\n }))\n }\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ',') {\n event.preventDefault();\n commitTag(field.name);\n } else if (\n event.key === 'Backspace' &&\n (tagDrafts[field.name] ?? '') === ''\n ) {\n const current = Array.isArray(value) ? value : [];\n if (current.length > 0) {\n removeTag(field.name, current[current.length - 1]);\n }\n }\n }}\n onBlur={() => commitTag(field.name)}\n />\n </div>\n ) : (\n <Input\n id={fieldId}\n type={\n field.type === 'number'\n ? 'number'\n : field.type === 'date'\n ? 'date'\n : field.type === 'email'\n ? 'email'\n : 'text'\n }\n value={typeof value === 'string' ? value : ''}\n placeholder={field.placeholder}\n disabled={submitting}\n onChange={(event) => setFieldValue(field.name, event.target.value)}\n />\n )}\n </>\n )}\n </div>\n );\n })}\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => onOpenChange(false)}\n disabled={submitting}\n >\n Cancel\n </Button>\n <Button type=\"submit\" disabled={submitting}>\n {submitting ? 'Saving…' : submitLabel}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ── Optional open/close helper ──────────────────────────────────\nexport interface UseFormModalResult<TContext> {\n open: boolean;\n /** The row/context passed to openModal (e.g. the entity being edited). */\n context: TContext | null;\n openModal: (context?: TContext) => void;\n closeModal: () => void;\n onOpenChange: (open: boolean) => void;\n}\n\n/**\n * Tiny helper for pages that toggle a single FormModal and want to remember\n * which row is being edited. `context` is null for a create flow.\n */\nexport function useFormModal<TContext = void>(): UseFormModalResult<TContext> {\n const [open, setOpen] = useState(false);\n const [context, setContext] = useState<TContext | null>(null);\n\n const openModal = useCallback((next?: TContext) => {\n setContext(next ?? null);\n setOpen(true);\n }, []);\n\n const closeModal = useCallback(() => setOpen(false), []);\n\n const onOpenChange = useCallback((nextOpen: boolean) => {\n setOpen(nextOpen);\n if (!nextOpen) {\n setContext(null);\n }\n }, []);\n\n return { open, context, openModal, closeModal, onOpenChange };\n}\n"],"mappings":"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"}
@@ -1,74 +1,47 @@
1
1
  import { useCrewFoundryStore as e } from "../store/crewfoundryStore.js";
2
2
  import { useMemo as t } from "react";
3
- import { Activity as n, Database as r, Globe as i, Lock as a, Monitor as o, Server as s, Shield as c, Users as l, Wrench as u } from "lucide-react";
4
- import { jsx as d, jsxs as f } from "react/jsx-runtime";
5
- import { Badge as p, Button as m, Card as h, CardContent as g, CardDescription as _, CardHeader as v, CardTitle as y, GlassCard as b, GradientText as x, PagePurpose as S } from "@burdenoff/fe-libs/ui";
3
+ import { Activity as n, Database as r, Globe as i, Lock as a, Monitor as o, Server as s, Shield as c, UserCheck as l, Users as u, Wrench as d } from "lucide-react";
4
+ import { jsx as f, jsxs as p } from "react/jsx-runtime";
5
+ import { Badge as m, Button as h, Card as g, CardContent as _, CardDescription as v, CardHeader as y, CardTitle as b, GlassCard as x, GradientText as S, IllustratedEmptyState as C, PagePurpose as w, ResponsiveTable as T } from "@burdenoff/fe-libs/ui";
6
6
  //#region src/crewfoundry/pages/AdminAdminPagePage.tsx
7
- var C = (e) => {
8
- let t = new Date(e);
9
- return Number.isNaN(t.getTime()) ? "Unknown" : t.toLocaleString("en-US", {
10
- year: "numeric",
11
- month: "2-digit",
12
- day: "2-digit",
13
- hour: "2-digit",
14
- minute: "2-digit"
15
- });
16
- };
17
- function w() {
18
- let { employees: w, departments: T, notifications: E, payrollRecords: D, jobPostings: O } = e(), k = t(() => {
19
- let e = E.some((e) => e.type === "warning"), t = E.some((e) => e.type === "error");
7
+ function E() {
8
+ let { employees: E, departments: D, notifications: O, payrollRecords: k, jobPostings: A } = e(), j = t(() => {
9
+ let e = O.some((e) => e.type === "warning"), t = O.some((e) => e.type === "error");
20
10
  return [
21
11
  {
22
12
  name: "Authentication",
23
13
  status: "operational",
24
- uptime: "99.98%",
25
14
  icon: a
26
15
  },
27
16
  {
28
17
  name: "Employee Directory",
29
18
  status: "operational",
30
- uptime: "99.95%",
31
- icon: l
19
+ icon: u
32
20
  },
33
21
  {
34
22
  name: "Payroll Engine",
35
- status: D.some((e) => e.status === "failed") ? "degraded" : "operational",
36
- uptime: "99.91%",
23
+ status: k.some((e) => e.status === "failed") ? "degraded" : "operational",
37
24
  icon: r
38
25
  },
39
26
  {
40
27
  name: "Recruitment ATS",
41
28
  status: "operational",
42
- uptime: "99.94%",
43
29
  icon: i
44
30
  },
45
31
  {
46
32
  name: "Analytics Pipeline",
47
33
  status: e || t ? "degraded" : "operational",
48
- uptime: e || t ? "98.70%" : "99.96%",
49
34
  icon: n
50
35
  },
51
36
  {
52
37
  name: "Notification Service",
53
38
  status: t ? "degraded" : "operational",
54
- uptime: "99.97%",
55
39
  icon: o
56
40
  }
57
41
  ];
58
- }, [E, D]), A = t(() => E.slice(0, 12).map((e, t) => ({
59
- action: e.type === "error" ? "Incident detected" : e.type === "warning" ? "Policy alert generated" : "Operational event recorded",
60
- actor: w[t % Math.max(w.length, 1)] ? `${w[t % w.length].firstName} ${w[t % w.length].lastName}` : "System",
61
- target: e.title,
62
- timestamp: C(e.createdAt),
63
- severity: e.type === "error" ? "high" : e.type === "warning" ? "medium" : "low"
64
- })), [w, E]), j = t(() => {
65
- let e = w.filter((e) => e.position.toLowerCase().includes("manager")).length, t = w.filter((e) => e.position.toLowerCase().includes("lead")).length, n = O.filter((e) => e.status === "active").length;
42
+ }, [O, k]), M = t(() => {
43
+ let e = E.filter((e) => e.position.toLowerCase().includes("manager")).length, t = E.filter((e) => e.position.toLowerCase().includes("lead")).length, n = A.filter((e) => e.status === "active").length;
66
44
  return [
67
- {
68
- role: "Super Admin",
69
- count: 2,
70
- permissions: "Full access"
71
- },
72
45
  {
73
46
  role: "HR Manager",
74
47
  count: Math.max(1, e),
@@ -86,206 +59,186 @@ function w() {
86
59
  },
87
60
  {
88
61
  role: "Employee",
89
- count: Math.max(0, w.length - e - t),
62
+ count: Math.max(0, E.length - e - t),
90
63
  permissions: "Self-service, directory, learning"
91
64
  }
92
65
  ];
93
- }, [w, O]), M = Math.round(k.filter((e) => e.status === "operational").length / Math.max(k.length, 1) * 100);
94
- return /* @__PURE__ */ f("div", {
66
+ }, [E, A]), N = j.filter((e) => e.status === "operational").length, P = E.filter((e) => e.status === "active").length, F = Math.round(N / Math.max(j.length, 1) * 100);
67
+ return /* @__PURE__ */ p("div", {
95
68
  className: "space-y-6 p-6",
96
69
  children: [
97
- /* @__PURE__ */ f("div", {
98
- className: "flex items-center justify-between",
99
- children: [/* @__PURE__ */ f("div", { children: [
100
- /* @__PURE__ */ d("h1", {
70
+ /* @__PURE__ */ p("div", {
71
+ className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between",
72
+ children: [/* @__PURE__ */ p("div", { children: [
73
+ /* @__PURE__ */ f("h1", {
101
74
  className: "text-2xl font-semibold",
102
- children: /* @__PURE__ */ d(x, { children: "Administration" })
75
+ children: /* @__PURE__ */ f(S, { children: "Administration" })
103
76
  }),
104
- /* @__PURE__ */ d("p", {
77
+ /* @__PURE__ */ f("p", {
105
78
  className: "text-sm text-text-secondary",
106
79
  children: "System administration and access management"
107
80
  }),
108
- /* @__PURE__ */ d(S, {
81
+ /* @__PURE__ */ f(w, {
109
82
  className: "mt-1",
110
83
  children: "Manage organization-wide settings, roles, and system controls from one place — use it to onboard admins, adjust permissions, and keep your CrewFoundry workspace secure and compliant."
111
84
  })
112
- ] }), /* @__PURE__ */ f("div", {
85
+ ] }), /* @__PURE__ */ p("div", {
113
86
  className: "flex gap-2",
114
- children: [/* @__PURE__ */ f(m, {
87
+ children: [/* @__PURE__ */ p(h, {
115
88
  variant: "outline",
116
89
  size: "sm",
117
- children: [/* @__PURE__ */ d(u, { className: "mr-1 h-4 w-4" }), "System Settings"]
118
- }), /* @__PURE__ */ f(m, {
90
+ disabled: !0,
91
+ "aria-label": "System settings (coming soon)",
92
+ title: "System settings are coming soon",
93
+ children: [/* @__PURE__ */ f(d, { className: "mr-1 h-4 w-4" }), "System Settings"]
94
+ }), /* @__PURE__ */ p(h, {
119
95
  size: "sm",
120
- children: [/* @__PURE__ */ d(c, { className: "mr-1 h-4 w-4" }), "Security"]
96
+ disabled: !0,
97
+ "aria-label": "Security controls (coming soon)",
98
+ title: "Security controls are coming soon",
99
+ children: [/* @__PURE__ */ f(c, { className: "mr-1 h-4 w-4" }), "Security"]
121
100
  })]
122
101
  })]
123
102
  }),
124
- /* @__PURE__ */ f("div", {
103
+ /* @__PURE__ */ p("div", {
125
104
  className: "grid gap-4 md:grid-cols-2 lg:grid-cols-4",
126
105
  children: [
127
- /* @__PURE__ */ f(b, {
106
+ /* @__PURE__ */ p(x, {
128
107
  treatment: "glass",
129
108
  glow: !0,
130
109
  className: "border-border-seam",
131
- children: [/* @__PURE__ */ f(v, {
110
+ children: [/* @__PURE__ */ p(y, {
132
111
  className: "flex flex-row items-center justify-between space-y-0 pb-2",
133
- children: [/* @__PURE__ */ d(y, {
112
+ children: [/* @__PURE__ */ f(b, {
134
113
  className: "text-sm font-medium",
135
114
  children: "Total Users"
136
- }), /* @__PURE__ */ d(l, { className: "h-4 w-4 text-text-secondary" })]
137
- }), /* @__PURE__ */ f(g, { children: [/* @__PURE__ */ d("div", {
115
+ }), /* @__PURE__ */ f(u, { className: "h-4 w-4 text-text-secondary" })]
116
+ }), /* @__PURE__ */ p(_, { children: [/* @__PURE__ */ f("div", {
138
117
  className: "text-2xl font-bold tabular-nums",
139
- children: w.length
140
- }), /* @__PURE__ */ f("p", {
118
+ children: E.length
119
+ }), /* @__PURE__ */ p("p", {
141
120
  className: "text-xs text-text-secondary",
142
- children: [T.length, " departments"]
121
+ children: [D.length, " departments"]
143
122
  })] })]
144
123
  }),
145
- /* @__PURE__ */ f(b, {
124
+ /* @__PURE__ */ p(x, {
146
125
  treatment: "metric",
147
126
  className: "border-border-seam",
148
- children: [/* @__PURE__ */ f(v, {
127
+ children: [/* @__PURE__ */ p(y, {
149
128
  className: "flex flex-row items-center justify-between space-y-0 pb-2",
150
- children: [/* @__PURE__ */ d(y, {
129
+ children: [/* @__PURE__ */ f(b, {
151
130
  className: "text-sm font-medium",
152
- children: "Active Sessions"
153
- }), /* @__PURE__ */ d(o, { className: "h-4 w-4 text-text-secondary" })]
154
- }), /* @__PURE__ */ f(g, { children: [/* @__PURE__ */ d("div", {
131
+ children: "Active Users"
132
+ }), /* @__PURE__ */ f(l, { className: "h-4 w-4 text-text-secondary" })]
133
+ }), /* @__PURE__ */ p(_, { children: [/* @__PURE__ */ f("div", {
155
134
  className: "text-2xl font-bold tabular-nums",
156
- children: Math.round(w.length * .76)
157
- }), /* @__PURE__ */ d("p", {
135
+ children: P
136
+ }), /* @__PURE__ */ f("p", {
158
137
  className: "text-xs text-text-secondary",
159
- children: "Estimated from active workforce"
138
+ children: "Currently active employees"
160
139
  })] })]
161
140
  }),
162
- /* @__PURE__ */ f(b, {
141
+ /* @__PURE__ */ p(x, {
163
142
  treatment: "metric",
164
143
  className: "border-border-seam",
165
- children: [/* @__PURE__ */ f(v, {
144
+ children: [/* @__PURE__ */ p(y, {
166
145
  className: "flex flex-row items-center justify-between space-y-0 pb-2",
167
- children: [/* @__PURE__ */ d(y, {
146
+ children: [/* @__PURE__ */ f(b, {
168
147
  className: "text-sm font-medium",
169
- children: "System Uptime"
170
- }), /* @__PURE__ */ d(s, { className: "h-4 w-4 text-text-secondary" })]
171
- }), /* @__PURE__ */ f(g, { children: [/* @__PURE__ */ d("div", {
148
+ children: "Operational Modules"
149
+ }), /* @__PURE__ */ f(s, { className: "h-4 w-4 text-text-secondary" })]
150
+ }), /* @__PURE__ */ p(_, { children: [/* @__PURE__ */ p("div", {
172
151
  className: "text-2xl font-bold tabular-nums",
173
- children: "99.95%"
174
- }), /* @__PURE__ */ d("p", {
152
+ children: [
153
+ N,
154
+ "/",
155
+ j.length
156
+ ]
157
+ }), /* @__PURE__ */ f("p", {
175
158
  className: "text-xs text-text-secondary",
176
- children: "Rolling platform uptime"
159
+ children: "Modules reporting healthy"
177
160
  })] })]
178
161
  }),
179
- /* @__PURE__ */ f(b, {
162
+ /* @__PURE__ */ p(x, {
180
163
  treatment: "metric",
181
164
  className: "border-border-seam",
182
- children: [/* @__PURE__ */ f(v, {
165
+ children: [/* @__PURE__ */ p(y, {
183
166
  className: "flex flex-row items-center justify-between space-y-0 pb-2",
184
- children: [/* @__PURE__ */ d(y, {
167
+ children: [/* @__PURE__ */ f(b, {
185
168
  className: "text-sm font-medium",
186
169
  children: "Security Score"
187
- }), /* @__PURE__ */ d(c, { className: "h-4 w-4 text-text-secondary" })]
188
- }), /* @__PURE__ */ f(g, { children: [/* @__PURE__ */ f("div", {
170
+ }), /* @__PURE__ */ f(c, { className: "h-4 w-4 text-text-secondary" })]
171
+ }), /* @__PURE__ */ p(_, { children: [/* @__PURE__ */ p("div", {
189
172
  className: "text-2xl font-bold tabular-nums",
190
- children: [M, "/100"]
191
- }), /* @__PURE__ */ d("p", {
173
+ children: [F, "/100"]
174
+ }), /* @__PURE__ */ f("p", {
192
175
  className: "text-xs text-status-success-text",
193
176
  children: "Derived from module health checks"
194
177
  })] })]
195
178
  })
196
179
  ]
197
180
  }),
198
- /* @__PURE__ */ f("div", {
181
+ /* @__PURE__ */ p("div", {
199
182
  className: "grid gap-4 md:grid-cols-2",
200
- children: [/* @__PURE__ */ f(h, { children: [/* @__PURE__ */ f(v, { children: [/* @__PURE__ */ d(y, { children: "System Status" }), /* @__PURE__ */ d(_, { children: "Module health and uptime" })] }), /* @__PURE__ */ d(g, {
183
+ children: [/* @__PURE__ */ p(g, { children: [/* @__PURE__ */ p(y, { children: [/* @__PURE__ */ f(b, { children: "System Status" }), /* @__PURE__ */ f(v, { children: "Module health derived from live workspace signals" })] }), /* @__PURE__ */ f(_, {
201
184
  className: "space-y-3",
202
- children: k.map((e) => /* @__PURE__ */ f("div", {
185
+ children: j.map((e) => /* @__PURE__ */ p("div", {
203
186
  className: "flex items-center justify-between rounded border p-3",
204
- children: [/* @__PURE__ */ f("div", {
187
+ children: [/* @__PURE__ */ p("div", {
205
188
  className: "flex items-center gap-3",
206
- children: [/* @__PURE__ */ d(e.icon, { className: "h-4 w-4 text-text-secondary" }), /* @__PURE__ */ d("span", {
189
+ children: [/* @__PURE__ */ f(e.icon, { className: "h-4 w-4 text-text-secondary" }), /* @__PURE__ */ f("span", {
207
190
  className: "text-sm font-medium",
208
191
  children: e.name
209
192
  })]
210
- }), /* @__PURE__ */ f("div", {
211
- className: "flex items-center gap-3",
212
- children: [/* @__PURE__ */ d("span", {
213
- className: "text-xs text-text-secondary",
214
- children: e.uptime
215
- }), /* @__PURE__ */ d(p, {
216
- variant: e.status === "operational" ? "default" : "destructive",
217
- className: "text-[10px]",
218
- children: e.status
219
- })]
193
+ }), /* @__PURE__ */ f(m, {
194
+ variant: e.status === "operational" ? "default" : "destructive",
195
+ className: "text-[10px]",
196
+ children: e.status
220
197
  })]
221
198
  }, e.name))
222
- })] }), /* @__PURE__ */ f(h, { children: [/* @__PURE__ */ f(v, { children: [/* @__PURE__ */ d(y, { children: "User Roles" }), /* @__PURE__ */ d(_, { children: "Role distribution and permissions" })] }), /* @__PURE__ */ f(g, { children: [/* @__PURE__ */ f("div", {
223
- className: "mb-2 grid grid-cols-3 gap-2 border-b pb-2 text-xs font-medium text-text-secondary",
224
- children: [
225
- /* @__PURE__ */ d("span", { children: "Role" }),
226
- /* @__PURE__ */ d("span", {
227
- className: "text-right",
228
- children: "Users"
229
- }),
230
- /* @__PURE__ */ d("span", { children: "Permissions" })
231
- ]
232
- }), j.map((e) => /* @__PURE__ */ f("div", {
233
- className: "grid grid-cols-3 gap-2 border-b py-2 text-sm last:border-0",
234
- children: [
235
- /* @__PURE__ */ d("span", {
236
- className: "font-medium",
237
- children: e.role
238
- }),
239
- /* @__PURE__ */ d("span", {
240
- className: "text-right",
241
- children: e.count
242
- }),
243
- /* @__PURE__ */ d("span", {
244
- className: "truncate text-xs text-text-secondary",
245
- children: e.permissions
246
- })
247
- ]
248
- }, e.role))] })] })]
199
+ })] }), /* @__PURE__ */ p(g, { children: [/* @__PURE__ */ p(y, { children: [/* @__PURE__ */ f(b, { children: "User Roles" }), /* @__PURE__ */ f(v, { children: "Role distribution and permissions" })] }), /* @__PURE__ */ f(_, { children: /* @__PURE__ */ f(T, {
200
+ columns: [
201
+ {
202
+ key: "role",
203
+ header: "Role",
204
+ priority: "primary",
205
+ cell: (e) => /* @__PURE__ */ f("span", {
206
+ className: "font-medium",
207
+ children: e.role
208
+ })
209
+ },
210
+ {
211
+ key: "count",
212
+ header: "Users",
213
+ priority: "primary",
214
+ cell: (e) => /* @__PURE__ */ f("span", {
215
+ className: "md:block md:text-right",
216
+ children: e.count
217
+ })
218
+ },
219
+ {
220
+ key: "permissions",
221
+ header: "Permissions",
222
+ priority: "secondary",
223
+ cell: (e) => /* @__PURE__ */ f("span", {
224
+ className: "text-xs text-text-secondary",
225
+ children: e.permissions
226
+ })
227
+ }
228
+ ],
229
+ data: M,
230
+ rowKey: (e) => e.role
231
+ }) })] })]
249
232
  }),
250
- /* @__PURE__ */ f(h, { children: [/* @__PURE__ */ f(v, { children: [/* @__PURE__ */ d(y, { children: "Recent Audit Log" }), /* @__PURE__ */ d(_, { children: "System activity and security events" })] }), /* @__PURE__ */ f(g, { children: [/* @__PURE__ */ f("div", {
251
- className: "mb-2 grid grid-cols-5 gap-2 border-b pb-2 text-xs font-medium text-text-secondary",
252
- children: [
253
- /* @__PURE__ */ d("span", { children: "Action" }),
254
- /* @__PURE__ */ d("span", { children: "Actor" }),
255
- /* @__PURE__ */ d("span", { children: "Target" }),
256
- /* @__PURE__ */ d("span", { children: "Timestamp" }),
257
- /* @__PURE__ */ d("span", { children: "Severity" })
258
- ]
259
- }), A.map((e, t) => /* @__PURE__ */ f("div", {
260
- className: "grid grid-cols-5 gap-2 border-b py-2 text-sm last:border-0",
261
- children: [
262
- /* @__PURE__ */ d("span", {
263
- className: "font-medium",
264
- children: e.action
265
- }),
266
- /* @__PURE__ */ d("span", {
267
- className: "text-text-secondary",
268
- children: e.actor
269
- }),
270
- /* @__PURE__ */ d("span", {
271
- className: "truncate text-text-secondary",
272
- children: e.target
273
- }),
274
- /* @__PURE__ */ d("span", {
275
- className: "text-xs",
276
- children: e.timestamp
277
- }),
278
- /* @__PURE__ */ d(p, {
279
- variant: e.severity === "high" ? "destructive" : e.severity === "medium" ? "secondary" : "outline",
280
- className: "w-fit text-[10px]",
281
- children: e.severity
282
- })
283
- ]
284
- }, `${e.action}-${t}`))] })] })
233
+ /* @__PURE__ */ p(g, { children: [/* @__PURE__ */ p(y, { children: [/* @__PURE__ */ f(b, { children: "Recent Audit Log" }), /* @__PURE__ */ f(v, { children: "System activity and security events" })] }), /* @__PURE__ */ f(_, { children: /* @__PURE__ */ f(C, {
234
+ illustration: "empty-data",
235
+ title: "Audit log coming soon",
236
+ description: "A verified trail of real system and security events will appear here once audit-log integration is connected."
237
+ }) })] })
285
238
  ]
286
239
  });
287
240
  }
288
241
  //#endregion
289
- export { w as AdminAdminPagePage };
242
+ export { E as AdminAdminPagePage };
290
243
 
291
244
  //# sourceMappingURL=AdminAdminPagePage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AdminAdminPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/AdminAdminPagePage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport {\n Activity,\n Database,\n Globe,\n Lock,\n Monitor,\n Server,\n Shield,\n Users,\n Wrench,\n} from 'lucide-react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\ntype SystemModule = {\n name: string;\n status: 'operational' | 'degraded';\n uptime: string;\n icon: typeof Lock;\n};\n\ntype AuditLog = {\n action: string;\n actor: string;\n target: string;\n timestamp: string;\n severity: 'low' | 'medium' | 'high';\n};\n\ntype UserRole = {\n role: string;\n count: number;\n permissions: string;\n};\n\nconst isoDateTime = (value: string): string => {\n const parsed = new Date(value);\n if (Number.isNaN(parsed.getTime())) {\n return 'Unknown';\n }\n return parsed.toLocaleString('en-US', {\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n });\n};\n\nexport function AdminAdminPagePage() {\n const { employees, departments, notifications, payrollRecords, jobPostings } =\n useCrewFoundryStore();\n\n const systemModules = useMemo<SystemModule[]>(() => {\n const hasWarning = notifications.some((notification) => notification.type === 'warning');\n const hasError = notifications.some((notification) => notification.type === 'error');\n\n return [\n { name: 'Authentication', status: 'operational', uptime: '99.98%', icon: Lock },\n { name: 'Employee Directory', status: 'operational', uptime: '99.95%', icon: Users },\n {\n name: 'Payroll Engine',\n status: payrollRecords.some((record) => record.status === 'failed')\n ? 'degraded'\n : 'operational',\n uptime: '99.91%',\n icon: Database,\n },\n { name: 'Recruitment ATS', status: 'operational', uptime: '99.94%', icon: Globe },\n {\n name: 'Analytics Pipeline',\n status: hasWarning || hasError ? 'degraded' : 'operational',\n uptime: hasWarning || hasError ? '98.70%' : '99.96%',\n icon: Activity,\n },\n {\n name: 'Notification Service',\n status: hasError ? 'degraded' : 'operational',\n uptime: '99.97%',\n icon: Monitor,\n },\n ];\n }, [notifications, payrollRecords]);\n\n const recentAuditLogs = useMemo<AuditLog[]>(() => {\n return notifications.slice(0, 12).map((notification, index) => ({\n action:\n notification.type === 'error'\n ? 'Incident detected'\n : notification.type === 'warning'\n ? 'Policy alert generated'\n : 'Operational event recorded',\n actor: employees[index % Math.max(employees.length, 1)]\n ? `${employees[index % employees.length].firstName} ${employees[index % employees.length].lastName}`\n : 'System',\n target: notification.title,\n timestamp: isoDateTime(notification.createdAt),\n severity:\n notification.type === 'error' ? 'high' : notification.type === 'warning' ? 'medium' : 'low',\n }));\n }, [employees, notifications]);\n\n const userRoles = useMemo<UserRole[]>(() => {\n const managers = employees.filter((employee) =>\n employee.position.toLowerCase().includes('manager')\n ).length;\n const leads = employees.filter((employee) =>\n employee.position.toLowerCase().includes('lead')\n ).length;\n const recruiters = jobPostings.filter((posting) => posting.status === 'active').length;\n return [\n { role: 'Super Admin', count: 2, permissions: 'Full access' },\n {\n role: 'HR Manager',\n count: Math.max(1, managers),\n permissions: 'Employee, payroll, leave management',\n },\n {\n role: 'Hiring Manager',\n count: Math.max(1, recruiters),\n permissions: 'Job postings, candidates, interviews',\n },\n {\n role: 'Department Lead',\n count: Math.max(1, leads),\n permissions: 'Team management, reports',\n },\n {\n role: 'Employee',\n count: Math.max(0, employees.length - managers - leads),\n permissions: 'Self-service, directory, learning',\n },\n ];\n }, [employees, jobPostings]);\n\n const securityScore = Math.round(\n (systemModules.filter((module) => module.status === 'operational').length /\n Math.max(systemModules.length, 1)) *\n 100\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>Administration</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">System administration and access management</p>\n <PagePurpose className=\"mt-1\">Manage organization-wide settings, roles, and system controls from one place — use it to onboard admins, adjust permissions, and keep your CrewFoundry workspace secure and compliant.</PagePurpose>\n </div>\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" size=\"sm\">\n <Wrench className=\"mr-1 h-4 w-4\" />\n System Settings\n </Button>\n <Button size=\"sm\">\n <Shield className=\"mr-1 h-4 w-4\" />\n Security\n </Button>\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Total Users</CardTitle>\n <Users className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{employees.length}</div>\n <p className=\"text-xs text-text-secondary\">{departments.length} departments</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Active Sessions</CardTitle>\n <Monitor className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n {Math.round(employees.length * 0.76)}\n </div>\n <p className=\"text-xs text-text-secondary\">Estimated from active workforce</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">System Uptime</CardTitle>\n <Server className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">99.95%</div>\n <p className=\"text-xs text-text-secondary\">Rolling platform uptime</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Security Score</CardTitle>\n <Shield className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{securityScore}/100</div>\n <p className=\"text-xs text-status-success-text\">Derived from module health checks</p>\n </CardContent>\n </GlassCard>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle>System Status</CardTitle>\n <CardDescription>Module health and uptime</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {systemModules.map((module) => (\n <div\n key={module.name}\n className=\"flex items-center justify-between rounded border p-3\"\n >\n <div className=\"flex items-center gap-3\">\n <module.icon className=\"h-4 w-4 text-text-secondary\" />\n <span className=\"text-sm font-medium\">{module.name}</span>\n </div>\n <div className=\"flex items-center gap-3\">\n <span className=\"text-xs text-text-secondary\">{module.uptime}</span>\n <Badge\n variant={module.status === 'operational' ? 'default' : 'destructive'}\n className=\"text-[10px]\"\n >\n {module.status}\n </Badge>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>User Roles</CardTitle>\n <CardDescription>Role distribution and permissions</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"mb-2 grid grid-cols-3 gap-2 border-b pb-2 text-xs font-medium text-text-secondary\">\n <span>Role</span>\n <span className=\"text-right\">Users</span>\n <span>Permissions</span>\n </div>\n {userRoles.map((role) => (\n <div\n key={role.role}\n className=\"grid grid-cols-3 gap-2 border-b py-2 text-sm last:border-0\"\n >\n <span className=\"font-medium\">{role.role}</span>\n <span className=\"text-right\">{role.count}</span>\n <span className=\"truncate text-xs text-text-secondary\">{role.permissions}</span>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Recent Audit Log</CardTitle>\n <CardDescription>System activity and security events</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"mb-2 grid grid-cols-5 gap-2 border-b pb-2 text-xs font-medium text-text-secondary\">\n <span>Action</span>\n <span>Actor</span>\n <span>Target</span>\n <span>Timestamp</span>\n <span>Severity</span>\n </div>\n {recentAuditLogs.map((log, index) => (\n <div\n key={`${log.action}-${index}`}\n className=\"grid grid-cols-5 gap-2 border-b py-2 text-sm last:border-0\"\n >\n <span className=\"font-medium\">{log.action}</span>\n <span className=\"text-text-secondary\">{log.actor}</span>\n <span className=\"truncate text-text-secondary\">{log.target}</span>\n <span className=\"text-xs\">{log.timestamp}</span>\n <Badge\n variant={\n log.severity === 'high'\n ? 'destructive'\n : log.severity === 'medium'\n ? 'secondary'\n : 'outline'\n }\n className=\"w-fit text-[10px]\"\n >\n {log.severity}\n </Badge>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AdminAdminPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/AdminAdminPagePage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport {\n Activity,\n Database,\n Globe,\n Lock,\n Monitor,\n Server,\n Shield,\n UserCheck,\n Users,\n Wrench,\n} from 'lucide-react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n ResponsiveTable,\n type ResponsiveTableColumn,\n} from '@burdenoff/fe-libs/ui';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\ntype SystemModule = {\n name: string;\n status: 'operational' | 'degraded';\n icon: typeof Lock;\n};\n\ntype UserRole = {\n role: string;\n count: number;\n permissions: string;\n};\n\nexport function AdminAdminPagePage() {\n const { employees, departments, notifications, payrollRecords, jobPostings } =\n useCrewFoundryStore();\n\n const systemModules = useMemo<SystemModule[]>(() => {\n const hasWarning = notifications.some((notification) => notification.type === 'warning');\n const hasError = notifications.some((notification) => notification.type === 'error');\n\n return [\n { name: 'Authentication', status: 'operational', icon: Lock },\n { name: 'Employee Directory', status: 'operational', icon: Users },\n {\n name: 'Payroll Engine',\n status: payrollRecords.some((record) => record.status === 'failed')\n ? 'degraded'\n : 'operational',\n icon: Database,\n },\n { name: 'Recruitment ATS', status: 'operational', icon: Globe },\n {\n name: 'Analytics Pipeline',\n status: hasWarning || hasError ? 'degraded' : 'operational',\n icon: Activity,\n },\n {\n name: 'Notification Service',\n status: hasError ? 'degraded' : 'operational',\n icon: Monitor,\n },\n ];\n }, [notifications, payrollRecords]);\n\n // Role distribution derived from real employee positions and active postings.\n const userRoles = useMemo<UserRole[]>(() => {\n const managers = employees.filter((employee) =>\n employee.position.toLowerCase().includes('manager')\n ).length;\n const leads = employees.filter((employee) =>\n employee.position.toLowerCase().includes('lead')\n ).length;\n const recruiters = jobPostings.filter((posting) => posting.status === 'active').length;\n return [\n {\n role: 'HR Manager',\n count: Math.max(1, managers),\n permissions: 'Employee, payroll, leave management',\n },\n {\n role: 'Hiring Manager',\n count: Math.max(1, recruiters),\n permissions: 'Job postings, candidates, interviews',\n },\n {\n role: 'Department Lead',\n count: Math.max(1, leads),\n permissions: 'Team management, reports',\n },\n {\n role: 'Employee',\n count: Math.max(0, employees.length - managers - leads),\n permissions: 'Self-service, directory, learning',\n },\n ];\n }, [employees, jobPostings]);\n\n const operationalModules = systemModules.filter(\n (module) => module.status === 'operational'\n ).length;\n const activeUsers = employees.filter((employee) => employee.status === 'active').length;\n const securityScore = Math.round((operationalModules / Math.max(systemModules.length, 1)) * 100);\n\n const roleColumns: ResponsiveTableColumn<UserRole>[] = [\n {\n key: 'role',\n header: 'Role',\n priority: 'primary',\n cell: (role) => <span className=\"font-medium\">{role.role}</span>,\n },\n {\n key: 'count',\n header: 'Users',\n priority: 'primary',\n cell: (role) => <span className=\"md:block md:text-right\">{role.count}</span>,\n },\n {\n key: 'permissions',\n header: 'Permissions',\n priority: 'secondary',\n cell: (role) => <span className=\"text-xs text-text-secondary\">{role.permissions}</span>,\n },\n ];\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Administration</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">System administration and access management</p>\n <PagePurpose className=\"mt-1\">\n Manage organization-wide settings, roles, and system controls from one place — use it to\n onboard admins, adjust permissions, and keep your CrewFoundry workspace secure and\n compliant.\n </PagePurpose>\n </div>\n <div className=\"flex gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled\n aria-label=\"System settings (coming soon)\"\n title=\"System settings are coming soon\"\n >\n <Wrench className=\"mr-1 h-4 w-4\" />\n System Settings\n </Button>\n <Button\n size=\"sm\"\n disabled\n aria-label=\"Security controls (coming soon)\"\n title=\"Security controls are coming soon\"\n >\n <Shield className=\"mr-1 h-4 w-4\" />\n Security\n </Button>\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Total Users</CardTitle>\n <Users className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{employees.length}</div>\n <p className=\"text-xs text-text-secondary\">{departments.length} departments</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Active Users</CardTitle>\n <UserCheck className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{activeUsers}</div>\n <p className=\"text-xs text-text-secondary\">Currently active employees</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Operational Modules</CardTitle>\n <Server className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n {operationalModules}/{systemModules.length}\n </div>\n <p className=\"text-xs text-text-secondary\">Modules reporting healthy</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Security Score</CardTitle>\n <Shield className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{securityScore}/100</div>\n <p className=\"text-xs text-status-success-text\">Derived from module health checks</p>\n </CardContent>\n </GlassCard>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle>System Status</CardTitle>\n <CardDescription>Module health derived from live workspace signals</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {systemModules.map((module) => (\n <div\n key={module.name}\n className=\"flex items-center justify-between rounded border p-3\"\n >\n <div className=\"flex items-center gap-3\">\n <module.icon className=\"h-4 w-4 text-text-secondary\" />\n <span className=\"text-sm font-medium\">{module.name}</span>\n </div>\n <Badge\n variant={module.status === 'operational' ? 'default' : 'destructive'}\n className=\"text-[10px]\"\n >\n {module.status}\n </Badge>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>User Roles</CardTitle>\n <CardDescription>Role distribution and permissions</CardDescription>\n </CardHeader>\n <CardContent>\n <ResponsiveTable columns={roleColumns} data={userRoles} rowKey={(role) => role.role} />\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Recent Audit Log</CardTitle>\n <CardDescription>System activity and security events</CardDescription>\n </CardHeader>\n <CardContent>\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"Audit log coming soon\"\n description=\"A verified trail of real system and security events will appear here once audit-log integration is connected.\"\n />\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}