@burdenoff/microfe-crewfoundry 2026.704.3 → 2026.704.5

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 (101) hide show
  1. package/dist/crewfoundry/pages/AdminAdminPagePage.js +76 -75
  2. package/dist/crewfoundry/pages/AdminAdminPagePage.js.map +1 -1
  3. package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js +118 -117
  4. package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js.map +1 -1
  5. package/dist/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.js +115 -114
  6. package/dist/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.js.map +1 -1
  7. package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js +105 -104
  8. package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js.map +1 -1
  9. package/dist/crewfoundry/pages/CommunicationsTeamsPagePage.js +52 -51
  10. package/dist/crewfoundry/pages/CommunicationsTeamsPagePage.js.map +1 -1
  11. package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js +94 -93
  12. package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js.map +1 -1
  13. package/dist/crewfoundry/pages/CompanyCompanyProfilePagePage.js +97 -96
  14. package/dist/crewfoundry/pages/CompanyCompanyProfilePagePage.js.map +1 -1
  15. package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js +68 -67
  16. package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js.map +1 -1
  17. package/dist/crewfoundry/pages/DashboardAnalyticsPagePage.js +96 -95
  18. package/dist/crewfoundry/pages/DashboardAnalyticsPagePage.js.map +1 -1
  19. package/dist/crewfoundry/pages/DashboardDashboardPagePage.js +153 -152
  20. package/dist/crewfoundry/pages/DashboardDashboardPagePage.js.map +1 -1
  21. package/dist/crewfoundry/pages/DashboardEnhancedDashboardPagePage.js +111 -110
  22. package/dist/crewfoundry/pages/DashboardEnhancedDashboardPagePage.js.map +1 -1
  23. package/dist/crewfoundry/pages/HiringApplicationsPagePage.js +77 -76
  24. package/dist/crewfoundry/pages/HiringApplicationsPagePage.js.map +1 -1
  25. package/dist/crewfoundry/pages/HiringCandidatesPagePage.js +80 -79
  26. package/dist/crewfoundry/pages/HiringCandidatesPagePage.js.map +1 -1
  27. package/dist/crewfoundry/pages/HiringCreateJobPagePage.js +1 -1
  28. package/dist/crewfoundry/pages/HiringCreateJobPagePage.js.map +1 -1
  29. package/dist/crewfoundry/pages/HiringInterviewsPagePage.js +102 -101
  30. package/dist/crewfoundry/pages/HiringInterviewsPagePage.js.map +1 -1
  31. package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js +126 -125
  32. package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js.map +1 -1
  33. package/dist/crewfoundry/pages/HiringJobsPagePage.js +64 -63
  34. package/dist/crewfoundry/pages/HiringJobsPagePage.js.map +1 -1
  35. package/dist/crewfoundry/pages/HrDepartmentsPagePage.js +96 -95
  36. package/dist/crewfoundry/pages/HrDepartmentsPagePage.js.map +1 -1
  37. package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js +129 -127
  38. package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js.map +1 -1
  39. package/dist/crewfoundry/pages/HrPayrollPagePage.js +118 -117
  40. package/dist/crewfoundry/pages/HrPayrollPagePage.js.map +1 -1
  41. package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js +123 -122
  42. package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js.map +1 -1
  43. package/dist/crewfoundry/pages/LearningAssessmentsPagePage.js +1 -1
  44. package/dist/crewfoundry/pages/LearningAssessmentsPagePage.js.map +1 -1
  45. package/dist/crewfoundry/pages/LearningCertificatesPagePage.js +1 -1
  46. package/dist/crewfoundry/pages/LearningCertificatesPagePage.js.map +1 -1
  47. package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js +77 -76
  48. package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js.map +1 -1
  49. package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js +49 -48
  50. package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js.map +1 -1
  51. package/dist/crewfoundry/pages/LearningCoursesPagePage.js +62 -61
  52. package/dist/crewfoundry/pages/LearningCoursesPagePage.js.map +1 -1
  53. package/dist/crewfoundry/pages/LearningLearningPagePage.js +72 -71
  54. package/dist/crewfoundry/pages/LearningLearningPagePage.js.map +1 -1
  55. package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js +70 -69
  56. package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js.map +1 -1
  57. package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js +57 -56
  58. package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js.map +1 -1
  59. package/dist/crewfoundry/pages/MarketplaceEarningsPagePage.js +58 -57
  60. package/dist/crewfoundry/pages/MarketplaceEarningsPagePage.js.map +1 -1
  61. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.js +122 -121
  62. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.js.map +1 -1
  63. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js +2 -2
  64. package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js.map +1 -1
  65. package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js +70 -69
  66. package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js.map +1 -1
  67. package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js +41 -40
  68. package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js.map +1 -1
  69. package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.js +65 -64
  70. package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.js.map +1 -1
  71. package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.js +75 -74
  72. package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.js.map +1 -1
  73. package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js +4 -4
  74. package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js.map +1 -1
  75. package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js +60 -59
  76. package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js.map +1 -1
  77. package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.js +53 -52
  78. package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.js.map +1 -1
  79. package/dist/crewfoundry/pages/PaymentsPaymentMethodsPagePage.js +37 -36
  80. package/dist/crewfoundry/pages/PaymentsPaymentMethodsPagePage.js.map +1 -1
  81. package/dist/crewfoundry/pages/ProjectsCreateProjectPagePage.js +1 -1
  82. package/dist/crewfoundry/pages/ProjectsCreateProjectPagePage.js.map +1 -1
  83. package/dist/crewfoundry/pages/ProjectsProjectDetailsPagePage.js +126 -125
  84. package/dist/crewfoundry/pages/ProjectsProjectDetailsPagePage.js.map +1 -1
  85. package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js +62 -61
  86. package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js.map +1 -1
  87. package/dist/crewfoundry/pages/TeamsTeamAnalyticsPagePage.js +50 -49
  88. package/dist/crewfoundry/pages/TeamsTeamAnalyticsPagePage.js.map +1 -1
  89. package/dist/crewfoundry/pages/TeamsTeamDetailPagePage.js +72 -71
  90. package/dist/crewfoundry/pages/TeamsTeamDetailPagePage.js.map +1 -1
  91. package/dist/crewfoundry/pages/TeamsTeamDirectoryPagePage.js +40 -39
  92. package/dist/crewfoundry/pages/TeamsTeamDirectoryPagePage.js.map +1 -1
  93. package/dist/crewfoundry/pages/TeamsTeamSettingsPagePage.js +134 -133
  94. package/dist/crewfoundry/pages/TeamsTeamSettingsPagePage.js.map +1 -1
  95. package/dist/crewfoundry/pages/TeamsTeamsPagePage.js +60 -59
  96. package/dist/crewfoundry/pages/TeamsTeamsPagePage.js.map +1 -1
  97. package/dist/crewfoundry/store/crewfoundryStore.js +177 -146
  98. package/dist/crewfoundry/store/crewfoundryStore.js.map +1 -1
  99. package/dist/crewfoundry/utils/formatEnumLabel.js +8 -0
  100. package/dist/crewfoundry/utils/formatEnumLabel.js.map +1 -0
  101. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"HrLeaveManagementPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/HrLeaveManagementPagePage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n PagePurpose,\n Pagination,\n ResponsiveTable,\n type ResponsiveTableColumn,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { useAuth, useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { Calendar, CheckCircle, Clock, XCircle } from 'lucide-react';\nimport { useCrewFoundryStore, type LeaveRequest } from '../store/crewfoundryStore';\nimport {\n FormModal,\n useFormModal,\n type FieldConfig,\n type FormValues,\n} from '../components/forms/FormModal';\nimport { getNumber, getText, newClientId } from '../components/forms/formValues';\n\ntype LeaveBalance = {\n type: string;\n total: number;\n used: number;\n remaining: number;\n};\n\nconst LEAVE_TYPE_OPTIONS = [\n { label: 'Annual', value: 'annual' },\n { label: 'Sick', value: 'sick' },\n { label: 'Personal', value: 'personal' },\n { label: 'Maternity', value: 'maternity' },\n { label: 'Paternity', value: 'paternity' },\n { label: 'Unpaid', value: 'unpaid' },\n];\n\nconst toLabel = (value: string): string =>\n value\n .split('_')\n .map((part) => part[0]?.toUpperCase() + part.slice(1))\n .join(' ');\n\nexport function HrLeaveManagementPagePage() {\n const { leaveRequests, employees, addLeaveRequest, updateLeaveRequest } = useCrewFoundryStore();\n const { showToast } = useGlobalUi();\n const { currentUser } = useAuth();\n const form = useFormModal<void>();\n\n const approverName =\n currentUser?.name ||\n [currentUser?.firstName, currentUser?.lastName].filter(Boolean).join(' ') ||\n currentUser?.email ||\n 'Manager';\n\n const employeeOptions = useMemo(\n () =>\n employees.map((employee) => ({\n label: `${employee.firstName} ${employee.lastName}`,\n value: employee.id,\n })),\n [employees]\n );\n\n const leaveFields = useMemo<FieldConfig[]>(\n () => [\n {\n name: 'employeeId',\n label: 'Employee',\n type: 'select',\n required: true,\n options: employeeOptions,\n colSpan: 2,\n },\n {\n name: 'type',\n label: 'Leave type',\n type: 'select',\n required: true,\n options: LEAVE_TYPE_OPTIONS,\n },\n { name: 'days', label: 'Number of days', type: 'number', required: true },\n { name: 'startDate', label: 'Start date', type: 'date', required: true },\n { name: 'endDate', label: 'End date', type: 'date', required: true },\n { name: 'reason', label: 'Reason', type: 'textarea', required: true, colSpan: 2 },\n ],\n [employeeOptions]\n );\n\n const pendingCount = leaveRequests.filter((leave) => leave.status === 'pending').length;\n const approvedCount = leaveRequests.filter((leave) => leave.status === 'approved').length;\n const totalDays = leaveRequests\n .filter((leave) => leave.status === 'approved')\n .reduce((sum, leave) => sum + leave.days, 0);\n\n const leaveBalances = useMemo<LeaveBalance[]>(() => {\n // Default annual entitlement per leave type (company policy defaults).\n // \"Used\" below is derived from real approved leave requests in the store.\n const entitlementByType: Record<string, number> = {\n annual: 20,\n sick: 12,\n personal: 5,\n maternity: 90,\n paternity: 30,\n unpaid: 15,\n };\n\n const usedByType = leaveRequests\n .filter((leave) => leave.status === 'approved')\n .reduce<Record<string, number>>((acc, leave) => {\n acc[leave.type] = (acc[leave.type] || 0) + leave.days;\n return acc;\n }, {});\n\n return Object.entries(entitlementByType).map(([type, total]) => {\n const used = usedByType[type] || 0;\n return {\n type: toLabel(type),\n total,\n used,\n remaining: Math.max(total - used, 0),\n };\n });\n }, [leaveRequests]);\n\n const onLeaveToday = useMemo(() => {\n const today = new Date().toISOString().split('T')[0];\n return leaveRequests.filter(\n (leave) => leave.status === 'approved' && leave.startDate <= today && leave.endDate >= today\n ).length;\n }, [leaveRequests]);\n\n const pendingList = useMemo(\n () => leaveRequests.filter((leave) => leave.status === 'pending'),\n [leaveRequests]\n );\n const sortedRequests = useMemo(\n () => [...leaveRequests].sort((a, b) => b.startDate.localeCompare(a.startDate)),\n [leaveRequests]\n );\n\n const pendingPg = usePagination({ initialPageSize: 5 });\n const allPg = usePagination({ initialPageSize: 10 });\n const pendingPageItems = pendingList.slice(\n pendingPg.offset,\n pendingPg.offset + pendingPg.pageSize\n );\n const requestPageItems = sortedRequests.slice(allPg.offset, allPg.offset + allPg.pageSize);\n\n const requestColumns: ResponsiveTableColumn<LeaveRequest>[] = [\n {\n key: 'employee',\n header: 'Employee',\n priority: 'primary',\n cell: (leave) => <span className=\"font-medium\">{leave.employeeName}</span>,\n },\n {\n key: 'department',\n header: 'Department',\n cell: (leave) => <span className=\"text-text-secondary\">{leave.department}</span>,\n },\n {\n key: 'type',\n header: 'Type',\n cell: (leave) => (\n <Badge variant=\"outline\" className=\"w-fit text-[10px]\">\n {leave.type}\n </Badge>\n ),\n },\n {\n key: 'period',\n header: 'Period',\n cell: (leave) => (\n <span className=\"text-xs\">\n {leave.startDate} — {leave.endDate}\n </span>\n ),\n },\n {\n key: 'days',\n header: 'Days',\n headerClassName: 'text-right',\n cellClassName: 'md:text-right',\n cell: (leave) => <span>{leave.days}</span>,\n },\n {\n key: 'reason',\n header: 'Reason',\n cell: (leave) => (\n <span className=\"block max-w-[16rem] truncate text-xs text-text-secondary\">\n {leave.reason}\n </span>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n cell: (leave) => (\n <Badge\n variant={\n leave.status === 'approved'\n ? 'default'\n : leave.status === 'pending'\n ? 'secondary'\n : 'destructive'\n }\n className=\"text-[10px]\"\n >\n {leave.status}\n </Badge>\n ),\n },\n ];\n\n const handleSubmit = async (values: FormValues) => {\n const employeeId = getText(values, 'employeeId');\n const employee = employees.find((item) => item.id === employeeId);\n const payload: LeaveRequest = {\n id: newClientId(),\n employeeId,\n employeeName: employee ? `${employee.firstName} ${employee.lastName}` : '',\n department: employee?.department ?? '',\n type: getText(values, 'type') as LeaveRequest['type'],\n startDate: getText(values, 'startDate'),\n endDate: getText(values, 'endDate'),\n days: getNumber(values, 'days'),\n status: 'pending',\n reason: getText(values, 'reason'),\n };\n await addLeaveRequest(payload);\n };\n\n const handleDecision = async (leave: LeaveRequest, status: 'approved' | 'rejected') => {\n try {\n await updateLeaveRequest(leave.id, { status, approverName });\n } catch (error) {\n showToast(error instanceof Error ? error.message : 'Unable to update leave request', 'error');\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>Leave Management</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Track and manage employee leave requests</p>\n <PagePurpose className=\"mt-1\">\n Handle time-off in one place — review and approve leave requests and keep an eye on team\n availability and balances.\n </PagePurpose>\n </div>\n <Button size=\"sm\" onClick={() => form.openModal()}>\n <Calendar className=\"mr-1 h-4 w-4\" />\n Request Leave\n </Button>\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\">Pending</CardTitle>\n <Clock className=\"h-4 w-4 text-status-warning-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{pendingCount}</div>\n <p className=\"text-xs text-text-secondary\">Awaiting approval</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\">Approved</CardTitle>\n <CheckCircle className=\"h-4 w-4 text-status-success-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{approvedCount}</div>\n <p className=\"text-xs text-text-secondary\">Current period</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\">Days Used</CardTitle>\n <Calendar className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{totalDays}</div>\n <p className=\"text-xs text-text-secondary\">Total approved days</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\">On Leave Today</CardTitle>\n <XCircle className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{onLeaveToday}</div>\n <p className=\"text-xs text-text-secondary\">Out of office</p>\n </CardContent>\n </GlassCard>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle>Leave Balances</CardTitle>\n <CardDescription>\n Company-wide leave allocation (default policy) vs. approved days used\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {leaveBalances.map((balance) => {\n const usedPct = Math.round((balance.used / Math.max(balance.total, 1)) * 100);\n return (\n <div key={balance.type} className=\"space-y-1\">\n <div className=\"flex justify-between text-sm\">\n <span className=\"font-medium\">{balance.type}</span>\n <span>\n {balance.used}/{balance.total} days used\n </span>\n </div>\n <div className=\"h-2 overflow-hidden rounded bg-bg-sunken\">\n <div\n className={`h-2 rounded ${\n usedPct > 80\n ? 'bg-status-error-text'\n : usedPct > 50\n ? 'bg-status-warning-text'\n : 'bg-status-success-text'\n }`}\n style={{ width: `${usedPct}%` }}\n />\n </div>\n </div>\n );\n })}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Pending Approvals</CardTitle>\n <CardDescription>Leave requests waiting for your review</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {pendingPageItems.map((leave) => (\n <div key={leave.id} className=\"flex items-center justify-between rounded border p-3\">\n <div>\n <p className=\"text-sm font-medium\">{leave.employeeName}</p>\n <p className=\"text-xs text-text-secondary\">\n {leave.type} · {leave.days} days · {leave.startDate} to {leave.endDate}\n </p>\n <p className=\"mt-1 text-xs text-text-secondary\">{leave.reason}</p>\n </div>\n <div className=\"flex gap-1\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 text-xs\"\n onClick={() => handleDecision(leave, 'rejected')}\n >\n Reject\n </Button>\n <Button\n size=\"sm\"\n className=\"h-7 text-xs\"\n onClick={() => handleDecision(leave, 'approved')}\n >\n Approve\n </Button>\n </div>\n </div>\n ))}\n {pendingCount === 0 && (\n <p className=\"py-6 text-center text-sm text-text-secondary\">\n No leave requests are awaiting approval.\n </p>\n )}\n {pendingList.length > pendingPg.pageSize && (\n <Pagination\n page={pendingPg.page}\n pageSize={pendingPg.pageSize}\n totalItems={pendingList.length}\n onPageChange={pendingPg.setPage}\n />\n )}\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>All Leave Requests</CardTitle>\n <CardDescription>Complete leave request history</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <ResponsiveTable<LeaveRequest>\n data={requestPageItems}\n rowKey={(leave) => leave.id}\n columns={requestColumns}\n emptyState={\n <p className=\"py-6 text-center text-sm text-text-secondary\">No leave requests yet.</p>\n }\n />\n {sortedRequests.length > allPg.pageSize && (\n <Pagination\n page={allPg.page}\n pageSize={allPg.pageSize}\n totalItems={sortedRequests.length}\n onPageChange={allPg.setPage}\n />\n )}\n </CardContent>\n </Card>\n\n <FormModal\n open={form.open}\n onOpenChange={form.onOpenChange}\n title=\"Request leave\"\n description=\"Submit a new time-off request for review.\"\n fields={leaveFields}\n submitLabel=\"Submit request\"\n onSubmit={handleSubmit}\n />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"HrLeaveManagementPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/HrLeaveManagementPagePage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n PagePurpose,\n Pagination,\n ResponsiveTable,\n type ResponsiveTableColumn,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { useAuth, useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { Calendar, CheckCircle, Clock, XCircle } from 'lucide-react';\nimport { useCrewFoundryStore, type LeaveRequest } from '../store/crewfoundryStore';\nimport {\n FormModal,\n useFormModal,\n type FieldConfig,\n type FormValues,\n} from '../components/forms/FormModal';\nimport { getNumber, getText, newClientId } from '../components/forms/formValues';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\n\ntype LeaveBalance = {\n type: string;\n total: number;\n used: number;\n remaining: number;\n};\n\nconst LEAVE_TYPE_OPTIONS = [\n { label: 'Annual', value: 'annual' },\n { label: 'Sick', value: 'sick' },\n { label: 'Personal', value: 'personal' },\n { label: 'Maternity', value: 'maternity' },\n { label: 'Paternity', value: 'paternity' },\n { label: 'Unpaid', value: 'unpaid' },\n];\n\nconst toLabel = (value: string): string =>\n value\n .split('_')\n .map((part) => part[0]?.toUpperCase() + part.slice(1))\n .join(' ');\n\nexport function HrLeaveManagementPagePage() {\n const { leaveRequests, employees, addLeaveRequest, updateLeaveRequest } = useCrewFoundryStore();\n const { showToast } = useGlobalUi();\n const { currentUser } = useAuth();\n const form = useFormModal<void>();\n\n const approverName =\n currentUser?.name ||\n [currentUser?.firstName, currentUser?.lastName].filter(Boolean).join(' ') ||\n currentUser?.email ||\n 'Manager';\n\n const employeeOptions = useMemo(\n () =>\n employees.map((employee) => ({\n label: `${employee.firstName} ${employee.lastName}`,\n value: employee.id,\n })),\n [employees]\n );\n\n const leaveFields = useMemo<FieldConfig[]>(\n () => [\n {\n name: 'employeeId',\n label: 'Employee',\n type: 'select',\n required: true,\n options: employeeOptions,\n colSpan: 2,\n },\n {\n name: 'type',\n label: 'Leave type',\n type: 'select',\n required: true,\n options: LEAVE_TYPE_OPTIONS,\n },\n { name: 'days', label: 'Number of days', type: 'number', required: true },\n { name: 'startDate', label: 'Start date', type: 'date', required: true },\n { name: 'endDate', label: 'End date', type: 'date', required: true },\n { name: 'reason', label: 'Reason', type: 'textarea', required: true, colSpan: 2 },\n ],\n [employeeOptions]\n );\n\n const pendingCount = leaveRequests.filter((leave) => leave.status === 'pending').length;\n const approvedCount = leaveRequests.filter((leave) => leave.status === 'approved').length;\n const totalDays = leaveRequests\n .filter((leave) => leave.status === 'approved')\n .reduce((sum, leave) => sum + leave.days, 0);\n\n const leaveBalances = useMemo<LeaveBalance[]>(() => {\n // Default annual entitlement per leave type (company policy defaults).\n // \"Used\" below is derived from real approved leave requests in the store.\n const entitlementByType: Record<string, number> = {\n annual: 20,\n sick: 12,\n personal: 5,\n maternity: 90,\n paternity: 30,\n unpaid: 15,\n };\n\n const usedByType = leaveRequests\n .filter((leave) => leave.status === 'approved')\n .reduce<Record<string, number>>((acc, leave) => {\n acc[leave.type] = (acc[leave.type] || 0) + leave.days;\n return acc;\n }, {});\n\n return Object.entries(entitlementByType).map(([type, total]) => {\n const used = usedByType[type] || 0;\n return {\n type: toLabel(type),\n total,\n used,\n remaining: Math.max(total - used, 0),\n };\n });\n }, [leaveRequests]);\n\n const onLeaveToday = useMemo(() => {\n const today = new Date().toISOString().split('T')[0];\n return leaveRequests.filter(\n (leave) => leave.status === 'approved' && leave.startDate <= today && leave.endDate >= today\n ).length;\n }, [leaveRequests]);\n\n const pendingList = useMemo(\n () => leaveRequests.filter((leave) => leave.status === 'pending'),\n [leaveRequests]\n );\n const sortedRequests = useMemo(\n () => [...leaveRequests].sort((a, b) => b.startDate.localeCompare(a.startDate)),\n [leaveRequests]\n );\n\n const pendingPg = usePagination({ initialPageSize: 5 });\n const allPg = usePagination({ initialPageSize: 10 });\n const pendingPageItems = pendingList.slice(\n pendingPg.offset,\n pendingPg.offset + pendingPg.pageSize\n );\n const requestPageItems = sortedRequests.slice(allPg.offset, allPg.offset + allPg.pageSize);\n\n const requestColumns: ResponsiveTableColumn<LeaveRequest>[] = [\n {\n key: 'employee',\n header: 'Employee',\n priority: 'primary',\n cell: (leave) => <span className=\"font-medium\">{leave.employeeName}</span>,\n },\n {\n key: 'department',\n header: 'Department',\n cell: (leave) => <span className=\"text-text-secondary\">{leave.department}</span>,\n },\n {\n key: 'type',\n header: 'Type',\n cell: (leave) => (\n <Badge variant=\"outline\" className=\"w-fit text-[10px]\">\n {formatEnumLabel(leave.type)}\n </Badge>\n ),\n },\n {\n key: 'period',\n header: 'Period',\n cell: (leave) => (\n <span className=\"text-xs\">\n {leave.startDate} — {leave.endDate}\n </span>\n ),\n },\n {\n key: 'days',\n header: 'Days',\n headerClassName: 'text-right',\n cellClassName: 'md:text-right',\n cell: (leave) => <span>{leave.days}</span>,\n },\n {\n key: 'reason',\n header: 'Reason',\n cell: (leave) => (\n <span className=\"block max-w-[16rem] truncate text-xs text-text-secondary\">\n {leave.reason}\n </span>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n cell: (leave) => (\n <Badge\n variant={\n leave.status === 'approved'\n ? 'default'\n : leave.status === 'pending'\n ? 'secondary'\n : 'destructive'\n }\n className=\"text-[10px]\"\n >\n {formatEnumLabel(leave.status)}\n </Badge>\n ),\n },\n ];\n\n const handleSubmit = async (values: FormValues) => {\n const employeeId = getText(values, 'employeeId');\n const employee = employees.find((item) => item.id === employeeId);\n const payload: LeaveRequest = {\n id: newClientId(),\n employeeId,\n employeeName: employee ? `${employee.firstName} ${employee.lastName}` : '',\n department: employee?.department ?? '',\n type: getText(values, 'type') as LeaveRequest['type'],\n startDate: getText(values, 'startDate'),\n endDate: getText(values, 'endDate'),\n days: getNumber(values, 'days'),\n status: 'pending',\n reason: getText(values, 'reason'),\n };\n await addLeaveRequest(payload);\n };\n\n const handleDecision = async (leave: LeaveRequest, status: 'approved' | 'rejected') => {\n try {\n await updateLeaveRequest(leave.id, { status, approverName });\n } catch (error) {\n showToast(error instanceof Error ? error.message : 'Unable to update leave request', 'error');\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>Leave Management</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Track and manage employee leave requests</p>\n <PagePurpose className=\"mt-1\">\n Handle time-off in one place — review and approve leave requests and keep an eye on team\n availability and balances.\n </PagePurpose>\n </div>\n <Button size=\"sm\" onClick={() => form.openModal()}>\n <Calendar className=\"mr-1 h-4 w-4\" />\n Request Leave\n </Button>\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\">Pending</CardTitle>\n <Clock className=\"h-4 w-4 text-status-warning-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{pendingCount}</div>\n <p className=\"text-xs text-text-secondary\">Awaiting approval</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\">Approved</CardTitle>\n <CheckCircle className=\"h-4 w-4 text-status-success-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{approvedCount}</div>\n <p className=\"text-xs text-text-secondary\">Current period</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\">Days Used</CardTitle>\n <Calendar className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{totalDays}</div>\n <p className=\"text-xs text-text-secondary\">Total approved days</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\">On Leave Today</CardTitle>\n <XCircle className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{onLeaveToday}</div>\n <p className=\"text-xs text-text-secondary\">Out of office</p>\n </CardContent>\n </GlassCard>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle>Leave Balances</CardTitle>\n <CardDescription>\n Company-wide leave allocation (default policy) vs. approved days used\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {leaveBalances.map((balance) => {\n const usedPct = Math.round((balance.used / Math.max(balance.total, 1)) * 100);\n return (\n <div key={balance.type} className=\"space-y-1\">\n <div className=\"flex justify-between text-sm\">\n <span className=\"font-medium\">{formatEnumLabel(balance.type)}</span>\n <span>\n {balance.used}/{balance.total} days used\n </span>\n </div>\n <div className=\"h-2 overflow-hidden rounded bg-bg-sunken\">\n <div\n className={`h-2 rounded ${\n usedPct > 80\n ? 'bg-status-error-text'\n : usedPct > 50\n ? 'bg-status-warning-text'\n : 'bg-status-success-text'\n }`}\n style={{ width: `${usedPct}%` }}\n />\n </div>\n </div>\n );\n })}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Pending Approvals</CardTitle>\n <CardDescription>Leave requests waiting for your review</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {pendingPageItems.map((leave) => (\n <div key={leave.id} className=\"flex items-center justify-between rounded border p-3\">\n <div>\n <p className=\"text-sm font-medium\">{leave.employeeName}</p>\n <p className=\"text-xs text-text-secondary\">\n {formatEnumLabel(leave.type)} · {leave.days} days · {leave.startDate} to{' '}\n {leave.endDate}\n </p>\n <p className=\"mt-1 text-xs text-text-secondary\">{leave.reason}</p>\n </div>\n <div className=\"flex gap-1\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 text-xs\"\n onClick={() => handleDecision(leave, 'rejected')}\n >\n Reject\n </Button>\n <Button\n size=\"sm\"\n className=\"h-7 text-xs\"\n onClick={() => handleDecision(leave, 'approved')}\n >\n Approve\n </Button>\n </div>\n </div>\n ))}\n {pendingCount === 0 && (\n <p className=\"py-6 text-center text-sm text-text-secondary\">\n No leave requests are awaiting approval.\n </p>\n )}\n {pendingList.length > pendingPg.pageSize && (\n <Pagination\n page={pendingPg.page}\n pageSize={pendingPg.pageSize}\n totalItems={pendingList.length}\n onPageChange={pendingPg.setPage}\n />\n )}\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>All Leave Requests</CardTitle>\n <CardDescription>Complete leave request history</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <ResponsiveTable<LeaveRequest>\n data={requestPageItems}\n rowKey={(leave) => leave.id}\n columns={requestColumns}\n emptyState={\n <p className=\"py-6 text-center text-sm text-text-secondary\">No leave requests yet.</p>\n }\n />\n {sortedRequests.length > allPg.pageSize && (\n <Pagination\n page={allPg.page}\n pageSize={allPg.pageSize}\n totalItems={sortedRequests.length}\n onPageChange={allPg.setPage}\n />\n )}\n </CardContent>\n </Card>\n\n <FormModal\n open={form.open}\n onOpenChange={form.onOpenChange}\n title=\"Request leave\"\n description=\"Submit a new time-off request for review.\"\n fields={leaveFields}\n submitLabel=\"Submit request\"\n onSubmit={handleSubmit}\n />\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,14 +1,15 @@
1
1
  import { useCrewFoundryStore as e } from "../store/crewfoundryStore.js";
2
- import { FormModal as t, useFormModal as n } from "../components/forms/FormModal.js";
3
- import { getNumber as r, getText as i, newClientId as a } from "../components/forms/formValues.js";
4
- import { useMemo as o, useState as s } from "react";
5
- import { CheckCircle as c, Clock as l, DollarSign as u, Download as d, Plus as f, TrendingUp as p } from "lucide-react";
6
- import { Fragment as m, jsx as h, jsxs as g } from "react/jsx-runtime";
7
- import { Badge as _, Button as v, Card as y, CardContent as b, CardDescription as x, CardHeader as S, CardTitle as C, GlassCard as w, GradientText as T, PagePurpose as E, Pagination as D, ResponsiveTable as O, Separator as k } from "@burdenoff/fe-libs/ui";
8
- import { usePagination as A } from "@burdenoff/fe-libs/shared/hooks";
9
- import { useGlobalUi as j } from "@burdenoff/fe-libs/shared/providers/shell";
2
+ import { formatEnumLabel as t } from "../utils/formatEnumLabel.js";
3
+ import { FormModal as n, useFormModal as r } from "../components/forms/FormModal.js";
4
+ import { getNumber as i, getText as a, newClientId as o } from "../components/forms/formValues.js";
5
+ import { useMemo as s, useState as c } from "react";
6
+ import { CheckCircle as l, Clock as u, DollarSign as d, Download as f, Plus as p, TrendingUp as m } from "lucide-react";
7
+ import { Fragment as h, jsx as g, jsxs as _ } from "react/jsx-runtime";
8
+ import { Badge as v, Button as y, Card as b, CardContent as x, CardDescription as S, CardHeader as C, CardTitle as w, GlassCard as T, GradientText as E, PagePurpose as D, Pagination as O, ResponsiveTable as k, Separator as A } from "@burdenoff/fe-libs/ui";
9
+ import { usePagination as j } from "@burdenoff/fe-libs/shared/hooks";
10
+ import { useGlobalUi as M } from "@burdenoff/fe-libs/shared/providers/shell";
10
11
  //#region src/crewfoundry/pages/HrPayrollPagePage.tsx
11
- var M = [
12
+ var N = [
12
13
  {
13
14
  label: "Draft",
14
15
  value: "draft"
@@ -30,17 +31,17 @@ var M = [
30
31
  value: "failed"
31
32
  }
32
33
  ];
33
- function N() {
34
- let { payrollRecords: N, employees: P, addPayrollRecord: F, updatePayrollRecord: I } = e(), { showToast: L } = j(), [R, z] = s("January 2026"), B = n(), { page: V, pageSize: H, offset: U, setPage: W } = A({ initialPageSize: 10 }), G = o(() => P.map((e) => ({
34
+ function P() {
35
+ let { payrollRecords: P, employees: F, addPayrollRecord: I, updatePayrollRecord: L } = e(), { showToast: R } = M(), [z, B] = c("January 2026"), V = r(), { page: H, pageSize: U, offset: W, setPage: G } = j({ initialPageSize: 10 }), K = s(() => F.map((e) => ({
35
36
  label: `${e.firstName} ${e.lastName}`,
36
37
  value: e.id
37
- })), [P]), K = o(() => [
38
+ })), [F]), q = s(() => [
38
39
  {
39
40
  name: "employeeId",
40
41
  label: "Employee",
41
42
  type: "select",
42
43
  required: !0,
43
- options: G,
44
+ options: K,
44
45
  colSpan: 2
45
46
  },
46
47
  {
@@ -83,16 +84,16 @@ function N() {
83
84
  name: "status",
84
85
  label: "Status",
85
86
  type: "select",
86
- options: M
87
+ options: N
87
88
  }
88
- ], [G]), q = N.filter((e) => e.period === R), J = q.reduce((e, t) => e + t.grossPay, 0), Y = q.reduce((e, t) => e + t.netPay, 0), X = q.reduce((e, t) => e + t.deductions, 0), Z = q.filter((e) => e.status === "paid").length, Q = q.filter((e) => e.status === "processing").length, $ = q.slice(U, U + H), ee = [...new Set(N.map((e) => e.period))], te = (e) => {
89
- z(e), W(1);
90
- }, ne = [
89
+ ], [K]), J = P.filter((e) => e.period === z), Y = J.reduce((e, t) => e + t.grossPay, 0), X = J.reduce((e, t) => e + t.netPay, 0), Z = J.reduce((e, t) => e + t.deductions, 0), Q = J.filter((e) => e.status === "paid").length, $ = J.filter((e) => e.status === "processing").length, ee = J.slice(W, W + U), te = [...new Set(P.map((e) => e.period))], ne = (e) => {
90
+ B(e), G(1);
91
+ }, re = [
91
92
  {
92
93
  key: "employee",
93
94
  header: "Employee",
94
95
  priority: "primary",
95
- cell: (e) => /* @__PURE__ */ h("span", {
96
+ cell: (e) => /* @__PURE__ */ g("span", {
96
97
  className: "font-medium",
97
98
  children: e.employeeName
98
99
  })
@@ -100,7 +101,7 @@ function N() {
100
101
  {
101
102
  key: "department",
102
103
  header: "Department",
103
- cell: (e) => /* @__PURE__ */ h("span", {
104
+ cell: (e) => /* @__PURE__ */ g("span", {
104
105
  className: "text-text-secondary",
105
106
  children: e.department
106
107
  })
@@ -110,14 +111,14 @@ function N() {
110
111
  header: "Gross Pay",
111
112
  headerClassName: "text-right",
112
113
  cellClassName: "md:text-right",
113
- cell: (e) => /* @__PURE__ */ g("span", { children: ["$", e.grossPay.toLocaleString()] })
114
+ cell: (e) => /* @__PURE__ */ _("span", { children: ["$", e.grossPay.toLocaleString()] })
114
115
  },
115
116
  {
116
117
  key: "deductions",
117
118
  header: "Deductions",
118
119
  headerClassName: "text-right",
119
120
  cellClassName: "md:text-right",
120
- cell: (e) => /* @__PURE__ */ g("span", {
121
+ cell: (e) => /* @__PURE__ */ _("span", {
121
122
  className: "text-status-error-text",
122
123
  children: ["-$", e.deductions.toLocaleString()]
123
124
  })
@@ -127,7 +128,7 @@ function N() {
127
128
  header: "Net Pay",
128
129
  headerClassName: "text-right",
129
130
  cellClassName: "md:text-right",
130
- cell: (e) => /* @__PURE__ */ g("span", {
131
+ cell: (e) => /* @__PURE__ */ _("span", {
131
132
  className: "font-medium",
132
133
  children: ["$", e.netPay.toLocaleString()]
133
134
  })
@@ -135,7 +136,7 @@ function N() {
135
136
  {
136
137
  key: "method",
137
138
  header: "Payment Method",
138
- cell: (e) => /* @__PURE__ */ h("span", {
139
+ cell: (e) => /* @__PURE__ */ g("span", {
139
140
  className: "text-text-secondary",
140
141
  children: e.paymentMethod
141
142
  })
@@ -143,243 +144,243 @@ function N() {
143
144
  {
144
145
  key: "status",
145
146
  header: "Status",
146
- cell: (e) => /* @__PURE__ */ g("div", {
147
+ cell: (e) => /* @__PURE__ */ _("div", {
147
148
  className: "flex items-center gap-2",
148
- children: [/* @__PURE__ */ h(_, {
149
+ children: [/* @__PURE__ */ g(v, {
149
150
  variant: e.status === "paid" ? "default" : e.status === "processing" ? "secondary" : "outline",
150
151
  className: "text-[10px]",
151
- children: e.status
152
- }), e.status !== "paid" && /* @__PURE__ */ h(v, {
152
+ children: t(e.status)
153
+ }), e.status !== "paid" && /* @__PURE__ */ g(y, {
153
154
  variant: "ghost",
154
155
  size: "sm",
155
156
  className: "h-6 px-2 text-[10px]",
156
- onClick: () => ie(e),
157
+ onClick: () => ae(e),
157
158
  children: "Mark paid"
158
159
  })]
159
160
  })
160
161
  }
161
- ], re = async (e) => {
162
- let t = i(e, "employeeId"), n = P.find((e) => e.id === t), o = r(e, "grossPay"), s = r(e, "deductions"), c = r(e, "netPay");
163
- await F({
164
- id: a(),
162
+ ], ie = async (e) => {
163
+ let t = a(e, "employeeId"), n = F.find((e) => e.id === t), r = i(e, "grossPay"), s = i(e, "deductions"), c = i(e, "netPay");
164
+ await I({
165
+ id: o(),
165
166
  employeeId: t,
166
167
  employeeName: n ? `${n.firstName} ${n.lastName}` : "",
167
168
  department: n?.department ?? "",
168
- period: i(e, "period"),
169
- grossPay: o,
169
+ period: a(e, "period"),
170
+ grossPay: r,
170
171
  deductions: s,
171
- netPay: c > 0 ? c : Math.max(o - s, 0),
172
- status: i(e, "status") || "draft",
173
- payDate: i(e, "payDate"),
174
- paymentMethod: i(e, "paymentMethod")
172
+ netPay: c > 0 ? c : Math.max(r - s, 0),
173
+ status: a(e, "status") || "draft",
174
+ payDate: a(e, "payDate"),
175
+ paymentMethod: a(e, "paymentMethod")
175
176
  });
176
- }, ie = async (e) => {
177
+ }, ae = async (e) => {
177
178
  try {
178
- await I(e.id, {
179
+ await L(e.id, {
179
180
  status: "paid",
180
181
  payDate: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
181
182
  });
182
183
  } catch (e) {
183
- L(e instanceof Error ? e.message : "Unable to update payroll record", "error");
184
+ R(e instanceof Error ? e.message : "Unable to update payroll record", "error");
184
185
  }
185
186
  };
186
- return /* @__PURE__ */ g("div", {
187
+ return /* @__PURE__ */ _("div", {
187
188
  className: "space-y-6 p-6",
188
189
  children: [
189
- /* @__PURE__ */ g("div", {
190
+ /* @__PURE__ */ _("div", {
190
191
  className: "flex items-center justify-between",
191
- children: [/* @__PURE__ */ g("div", { children: [
192
- /* @__PURE__ */ h("h1", {
192
+ children: [/* @__PURE__ */ _("div", { children: [
193
+ /* @__PURE__ */ g("h1", {
193
194
  className: "text-2xl font-semibold",
194
- children: /* @__PURE__ */ h(T, { children: "Payroll Management" })
195
+ children: /* @__PURE__ */ g(E, { children: "Payroll Management" })
195
196
  }),
196
- /* @__PURE__ */ h("p", {
197
+ /* @__PURE__ */ g("p", {
197
198
  className: "text-sm text-text-secondary",
198
199
  children: "Process and manage employee payroll"
199
200
  }),
200
- /* @__PURE__ */ h(E, {
201
+ /* @__PURE__ */ g(D, {
201
202
  className: "mt-1",
202
203
  children: "Run and review payroll — track salaries, deductions, and pay runs so your team gets paid accurately and on time."
203
204
  })
204
- ] }), /* @__PURE__ */ g("div", {
205
+ ] }), /* @__PURE__ */ _("div", {
205
206
  className: "flex gap-2",
206
- children: [/* @__PURE__ */ g(v, {
207
+ children: [/* @__PURE__ */ _(y, {
207
208
  variant: "outline",
208
209
  size: "sm",
209
210
  disabled: !0,
210
211
  "aria-label": "Export payroll (coming soon)",
211
212
  title: "Payroll export is coming soon",
212
213
  children: [
213
- /* @__PURE__ */ h(d, { className: "h-4 w-4 mr-1" }),
214
+ /* @__PURE__ */ g(f, { className: "h-4 w-4 mr-1" }),
214
215
  "Export",
215
- /* @__PURE__ */ h(_, {
216
+ /* @__PURE__ */ g(v, {
216
217
  variant: "secondary",
217
218
  className: "ml-1 text-[9px]",
218
219
  children: "Soon"
219
220
  })
220
221
  ]
221
- }), /* @__PURE__ */ g(v, {
222
+ }), /* @__PURE__ */ _(y, {
222
223
  size: "sm",
223
- onClick: () => B.openModal(),
224
- children: [/* @__PURE__ */ h(f, { className: "h-4 w-4 mr-1" }), "Add Record"]
224
+ onClick: () => V.openModal(),
225
+ children: [/* @__PURE__ */ g(p, { className: "h-4 w-4 mr-1" }), "Add Record"]
225
226
  })]
226
227
  })]
227
228
  }),
228
- /* @__PURE__ */ g("div", {
229
+ /* @__PURE__ */ _("div", {
229
230
  className: "grid gap-4 md:grid-cols-2 lg:grid-cols-4",
230
231
  children: [
231
- /* @__PURE__ */ g(w, {
232
+ /* @__PURE__ */ _(T, {
232
233
  treatment: "glass",
233
234
  glow: !0,
234
235
  className: "border-border-seam",
235
- children: [/* @__PURE__ */ g(S, {
236
+ children: [/* @__PURE__ */ _(C, {
236
237
  className: "flex flex-row items-center justify-between space-y-0 pb-2",
237
- children: [/* @__PURE__ */ h(C, {
238
+ children: [/* @__PURE__ */ g(w, {
238
239
  className: "text-sm font-medium",
239
240
  children: "Gross Payroll"
240
- }), /* @__PURE__ */ h(u, { className: "h-4 w-4 text-text-secondary" })]
241
- }), /* @__PURE__ */ g(b, { children: [/* @__PURE__ */ g("div", {
241
+ }), /* @__PURE__ */ g(d, { className: "h-4 w-4 text-text-secondary" })]
242
+ }), /* @__PURE__ */ _(x, { children: [/* @__PURE__ */ _("div", {
242
243
  className: "text-2xl font-bold tabular-nums",
243
- children: ["$", J.toLocaleString()]
244
- }), /* @__PURE__ */ h("p", {
244
+ children: ["$", Y.toLocaleString()]
245
+ }), /* @__PURE__ */ g("p", {
245
246
  className: "text-xs text-text-secondary",
246
- children: R
247
+ children: z
247
248
  })] })]
248
249
  }),
249
- /* @__PURE__ */ g(w, {
250
+ /* @__PURE__ */ _(T, {
250
251
  treatment: "metric",
251
252
  className: "border-border-seam",
252
- children: [/* @__PURE__ */ g(S, {
253
+ children: [/* @__PURE__ */ _(C, {
253
254
  className: "flex flex-row items-center justify-between space-y-0 pb-2",
254
- children: [/* @__PURE__ */ h(C, {
255
+ children: [/* @__PURE__ */ g(w, {
255
256
  className: "text-sm font-medium",
256
257
  children: "Net Payroll"
257
- }), /* @__PURE__ */ h(p, { className: "h-4 w-4 text-text-secondary" })]
258
- }), /* @__PURE__ */ g(b, { children: [/* @__PURE__ */ g("div", {
258
+ }), /* @__PURE__ */ g(m, { className: "h-4 w-4 text-text-secondary" })]
259
+ }), /* @__PURE__ */ _(x, { children: [/* @__PURE__ */ _("div", {
259
260
  className: "text-2xl font-bold tabular-nums",
260
- children: ["$", Y.toLocaleString()]
261
- }), /* @__PURE__ */ h("p", {
261
+ children: ["$", X.toLocaleString()]
262
+ }), /* @__PURE__ */ g("p", {
262
263
  className: "text-xs text-text-secondary",
263
264
  children: "After deductions"
264
265
  })] })]
265
266
  }),
266
- /* @__PURE__ */ g(w, {
267
+ /* @__PURE__ */ _(T, {
267
268
  treatment: "metric",
268
269
  className: "border-border-seam",
269
- children: [/* @__PURE__ */ g(S, {
270
+ children: [/* @__PURE__ */ _(C, {
270
271
  className: "flex flex-row items-center justify-between space-y-0 pb-2",
271
- children: [/* @__PURE__ */ h(C, {
272
+ children: [/* @__PURE__ */ g(w, {
272
273
  className: "text-sm font-medium",
273
274
  children: "Paid"
274
- }), /* @__PURE__ */ h(c, { className: "h-4 w-4 text-status-success-text" })]
275
- }), /* @__PURE__ */ g(b, { children: [/* @__PURE__ */ h("div", {
275
+ }), /* @__PURE__ */ g(l, { className: "h-4 w-4 text-status-success-text" })]
276
+ }), /* @__PURE__ */ _(x, { children: [/* @__PURE__ */ g("div", {
276
277
  className: "text-2xl font-bold tabular-nums",
277
- children: Z
278
- }), /* @__PURE__ */ h("p", {
278
+ children: Q
279
+ }), /* @__PURE__ */ g("p", {
279
280
  className: "text-xs text-text-secondary",
280
281
  children: "employees paid"
281
282
  })] })]
282
283
  }),
283
- /* @__PURE__ */ g(w, {
284
+ /* @__PURE__ */ _(T, {
284
285
  treatment: "metric",
285
286
  className: "border-border-seam",
286
- children: [/* @__PURE__ */ g(S, {
287
+ children: [/* @__PURE__ */ _(C, {
287
288
  className: "flex flex-row items-center justify-between space-y-0 pb-2",
288
- children: [/* @__PURE__ */ h(C, {
289
+ children: [/* @__PURE__ */ g(w, {
289
290
  className: "text-sm font-medium",
290
291
  children: "Processing"
291
- }), /* @__PURE__ */ h(l, { className: "h-4 w-4 text-status-warning-text" })]
292
- }), /* @__PURE__ */ g(b, { children: [/* @__PURE__ */ h("div", {
292
+ }), /* @__PURE__ */ g(u, { className: "h-4 w-4 text-status-warning-text" })]
293
+ }), /* @__PURE__ */ _(x, { children: [/* @__PURE__ */ g("div", {
293
294
  className: "text-2xl font-bold tabular-nums",
294
- children: Q
295
- }), /* @__PURE__ */ h("p", {
295
+ children: $
296
+ }), /* @__PURE__ */ g("p", {
296
297
  className: "text-xs text-text-secondary",
297
298
  children: "in progress"
298
299
  })] })]
299
300
  })
300
301
  ]
301
302
  }),
302
- /* @__PURE__ */ h("div", {
303
+ /* @__PURE__ */ g("div", {
303
304
  className: "flex flex-wrap gap-2",
304
- children: ee.map((e) => /* @__PURE__ */ h(v, {
305
- variant: R === e ? "default" : "outline",
305
+ children: te.map((e) => /* @__PURE__ */ g(y, {
306
+ variant: z === e ? "default" : "outline",
306
307
  size: "sm",
307
- onClick: () => te(e),
308
+ onClick: () => ne(e),
308
309
  children: e
309
310
  }, e))
310
311
  }),
311
- /* @__PURE__ */ g(y, { children: [/* @__PURE__ */ g(S, { children: [/* @__PURE__ */ h(C, { children: "Payroll Records" }), /* @__PURE__ */ g(x, { children: ["Individual employee payroll details for ", R] })] }), /* @__PURE__ */ g(b, {
312
+ /* @__PURE__ */ _(b, { children: [/* @__PURE__ */ _(C, { children: [/* @__PURE__ */ g(w, { children: "Payroll Records" }), /* @__PURE__ */ _(S, { children: ["Individual employee payroll details for ", z] })] }), /* @__PURE__ */ _(x, {
312
313
  className: "space-y-4",
313
314
  children: [
314
- /* @__PURE__ */ h(O, {
315
- data: $,
315
+ /* @__PURE__ */ g(k, {
316
+ data: ee,
316
317
  rowKey: (e) => e.id,
317
- columns: ne,
318
- emptyState: /* @__PURE__ */ g("p", {
318
+ columns: re,
319
+ emptyState: /* @__PURE__ */ _("p", {
319
320
  className: "py-6 text-center text-sm text-text-secondary",
320
321
  children: [
321
322
  "No payroll records for ",
322
- R,
323
+ z,
323
324
  "."
324
325
  ]
325
326
  })
326
327
  }),
327
- q.length > H && /* @__PURE__ */ h(D, {
328
- page: V,
329
- pageSize: H,
330
- totalItems: q.length,
331
- onPageChange: W
328
+ J.length > U && /* @__PURE__ */ g(O, {
329
+ page: H,
330
+ pageSize: U,
331
+ totalItems: J.length,
332
+ onPageChange: G
332
333
  }),
333
- q.length > 0 && /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ h(k, {}), /* @__PURE__ */ g("div", {
334
+ J.length > 0 && /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g(A, {}), /* @__PURE__ */ _("div", {
334
335
  className: "flex flex-wrap items-center justify-between gap-4 text-sm font-semibold",
335
- children: [/* @__PURE__ */ g("span", { children: ["Total for ", R] }), /* @__PURE__ */ g("div", {
336
+ children: [/* @__PURE__ */ _("span", { children: ["Total for ", z] }), /* @__PURE__ */ _("div", {
336
337
  className: "flex flex-wrap gap-x-6 gap-y-1",
337
338
  children: [
338
- /* @__PURE__ */ g("span", { children: [
339
- /* @__PURE__ */ h("span", {
339
+ /* @__PURE__ */ _("span", { children: [
340
+ /* @__PURE__ */ g("span", {
340
341
  className: "text-text-secondary",
341
342
  children: "Gross "
342
343
  }),
343
344
  "$",
344
- J.toLocaleString()
345
+ Y.toLocaleString()
345
346
  ] }),
346
- /* @__PURE__ */ g("span", {
347
+ /* @__PURE__ */ _("span", {
347
348
  className: "text-status-error-text",
348
349
  children: [
349
- /* @__PURE__ */ h("span", {
350
+ /* @__PURE__ */ g("span", {
350
351
  className: "text-text-secondary",
351
352
  children: "Deductions "
352
353
  }),
353
354
  "-$",
354
- X.toLocaleString()
355
+ Z.toLocaleString()
355
356
  ]
356
357
  }),
357
- /* @__PURE__ */ g("span", { children: [
358
- /* @__PURE__ */ h("span", {
358
+ /* @__PURE__ */ _("span", { children: [
359
+ /* @__PURE__ */ g("span", {
359
360
  className: "text-text-secondary",
360
361
  children: "Net "
361
362
  }),
362
363
  "$",
363
- Y.toLocaleString()
364
+ X.toLocaleString()
364
365
  ] })
365
366
  ]
366
367
  })]
367
368
  })] })
368
369
  ]
369
370
  })] }),
370
- /* @__PURE__ */ h(t, {
371
- open: B.open,
372
- onOpenChange: B.onOpenChange,
371
+ /* @__PURE__ */ g(n, {
372
+ open: V.open,
373
+ onOpenChange: V.onOpenChange,
373
374
  title: "Add payroll record",
374
375
  description: "Create a payroll entry for a team member.",
375
- fields: K,
376
+ fields: q,
376
377
  submitLabel: "Add record",
377
- onSubmit: re
378
+ onSubmit: ie
378
379
  })
379
380
  ]
380
381
  });
381
382
  }
382
383
  //#endregion
383
- export { N as HrPayrollPagePage };
384
+ export { P as HrPayrollPagePage };
384
385
 
385
386
  //# sourceMappingURL=HrPayrollPagePage.js.map