@burdenoff/microfe-crewfoundry 2026.704.6 → 2026.704.7
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.
- package/dist/crewfoundry/CrewFoundryRoutes.js +51 -43
- package/dist/crewfoundry/CrewFoundryRoutes.js.map +1 -1
- package/dist/crewfoundry/components/forms/formValues.js +5 -2
- package/dist/crewfoundry/components/forms/formValues.js.map +1 -1
- package/dist/crewfoundry/components/viz/AllocationBar.js +68 -0
- package/dist/crewfoundry/components/viz/AllocationBar.js.map +1 -0
- package/dist/crewfoundry/components/viz/AreaChart.js +102 -0
- package/dist/crewfoundry/components/viz/AreaChart.js.map +1 -0
- package/dist/crewfoundry/components/viz/AvatarStack.js +36 -0
- package/dist/crewfoundry/components/viz/AvatarStack.js.map +1 -0
- package/dist/crewfoundry/components/viz/BarChart.js +83 -0
- package/dist/crewfoundry/components/viz/BarChart.js.map +1 -0
- package/dist/crewfoundry/components/viz/CertificateCard.js +118 -0
- package/dist/crewfoundry/components/viz/CertificateCard.js.map +1 -0
- package/dist/crewfoundry/components/viz/DonutChart.js +109 -0
- package/dist/crewfoundry/components/viz/DonutChart.js.map +1 -0
- package/dist/crewfoundry/components/viz/HBarFunnel.js +54 -0
- package/dist/crewfoundry/components/viz/HBarFunnel.js.map +1 -0
- package/dist/crewfoundry/components/viz/Heatmap.js +91 -0
- package/dist/crewfoundry/components/viz/Heatmap.js.map +1 -0
- package/dist/crewfoundry/components/viz/KanbanBoard.js +50 -0
- package/dist/crewfoundry/components/viz/KanbanBoard.js.map +1 -0
- package/dist/crewfoundry/components/viz/LineChart.js +123 -0
- package/dist/crewfoundry/components/viz/LineChart.js.map +1 -0
- package/dist/crewfoundry/components/viz/OrgTree.js +83 -0
- package/dist/crewfoundry/components/viz/OrgTree.js.map +1 -0
- package/dist/crewfoundry/components/viz/PathSteps.js +68 -0
- package/dist/crewfoundry/components/viz/PathSteps.js.map +1 -0
- package/dist/crewfoundry/components/viz/ProgressRing.js +68 -0
- package/dist/crewfoundry/components/viz/ProgressRing.js.map +1 -0
- package/dist/crewfoundry/components/viz/RatingStars.js +51 -0
- package/dist/crewfoundry/components/viz/RatingStars.js.map +1 -0
- package/dist/crewfoundry/components/viz/SegmentedTabs.js +69 -0
- package/dist/crewfoundry/components/viz/SegmentedTabs.js.map +1 -0
- package/dist/crewfoundry/components/viz/SkillBar.js +3 -0
- package/dist/crewfoundry/components/viz/Sparkline.js +74 -0
- package/dist/crewfoundry/components/viz/Sparkline.js.map +1 -0
- package/dist/crewfoundry/components/viz/StatCard.js +60 -0
- package/dist/crewfoundry/components/viz/StatCard.js.map +1 -0
- package/dist/crewfoundry/components/viz/StepProgress.js +3 -0
- package/dist/crewfoundry/components/viz/Timeline.js +66 -0
- package/dist/crewfoundry/components/viz/Timeline.js.map +1 -0
- package/dist/crewfoundry/components/viz/index.js +23 -0
- package/dist/crewfoundry/components/viz/lib/Avatar.js +30 -0
- package/dist/crewfoundry/components/viz/lib/Avatar.js.map +1 -0
- package/dist/crewfoundry/components/viz/lib/CartesianFrame.js +119 -0
- package/dist/crewfoundry/components/viz/lib/CartesianFrame.js.map +1 -0
- package/dist/crewfoundry/components/viz/lib/ResponsiveSvg.js +35 -0
- package/dist/crewfoundry/components/viz/lib/ResponsiveSvg.js.map +1 -0
- package/dist/crewfoundry/components/viz/lib/format.js +21 -0
- package/dist/crewfoundry/components/viz/lib/format.js.map +1 -0
- package/dist/crewfoundry/components/viz/lib/geometry.js +69 -0
- package/dist/crewfoundry/components/viz/lib/geometry.js.map +1 -0
- package/dist/crewfoundry/components/viz/lib/hooks.js +39 -0
- package/dist/crewfoundry/components/viz/lib/hooks.js.map +1 -0
- package/dist/crewfoundry/components/viz/lib/palette.js +27 -0
- package/dist/crewfoundry/components/viz/lib/palette.js.map +1 -0
- package/dist/crewfoundry/lib/analytics.js +323 -0
- package/dist/crewfoundry/lib/analytics.js.map +1 -0
- package/dist/crewfoundry/lib/companyFinance.js +16 -0
- package/dist/crewfoundry/lib/companyFinance.js.map +1 -0
- package/dist/crewfoundry/pages/AnalyticsAnalyticsPagePage.js +226 -283
- package/dist/crewfoundry/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsEmployeeAnalyticsPagePage.js +107 -120
- package/dist/crewfoundry/pages/AnalyticsEmployeeAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js +122 -162
- package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.js +121 -158
- package/dist/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js +316 -211
- package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.js +75 -27
- package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js +167 -165
- package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js +168 -109
- package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardDashboardPagePage.js +401 -290
- package/dist/crewfoundry/pages/DashboardDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringApplicationsPagePage.js +84 -75
- package/dist/crewfoundry/pages/HiringApplicationsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringCandidatesPagePage.js +368 -174
- package/dist/crewfoundry/pages/HiringCandidatesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringCreateJobPagePage.js +236 -201
- package/dist/crewfoundry/pages/HiringCreateJobPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringInterviewsPagePage.js +369 -196
- package/dist/crewfoundry/pages/HiringInterviewsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js +269 -270
- package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringJobsPagePage.js +210 -110
- package/dist/crewfoundry/pages/HiringJobsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrDepartmentsPagePage.js +216 -178
- package/dist/crewfoundry/pages/HrDepartmentsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrEmployeeProfilePagePage.js +385 -0
- package/dist/crewfoundry/pages/HrEmployeeProfilePagePage.js.map +1 -0
- package/dist/crewfoundry/pages/HrEmployeesPagePage.js +189 -130
- package/dist/crewfoundry/pages/HrEmployeesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js +397 -241
- package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrPayrollPagePage.js +262 -147
- package/dist/crewfoundry/pages/HrPayrollPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js +345 -167
- package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCertificatesPagePage.js +98 -24
- package/dist/crewfoundry/pages/LearningCertificatesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js +1 -1
- package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js +289 -108
- package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCoursesPagePage.js +221 -114
- package/dist/crewfoundry/pages/LearningCoursesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js +134 -135
- package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js +148 -97
- package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningMyLearningPagePage.js +264 -157
- package/dist/crewfoundry/pages/LearningMyLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js +85 -93
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js +211 -122
- package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js +94 -54
- package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js +289 -140
- package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js +169 -79
- package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js +296 -125
- package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsResourcingPagePage.js +187 -0
- package/dist/crewfoundry/pages/ProjectsResourcingPagePage.js.map +1 -0
- package/dist/crewfoundry/pages/TeamsTeamDetailPagePage.js +140 -78
- package/dist/crewfoundry/pages/TeamsTeamDetailPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamDirectoryPagePage.js +93 -43
- package/dist/crewfoundry/pages/TeamsTeamDirectoryPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamsPagePage.js +250 -98
- package/dist/crewfoundry/pages/TeamsTeamsPagePage.js.map +1 -1
- package/dist/crewfoundry/store/crewfoundryStore.js +1 -1
- package/dist/crewfoundry/store/crewfoundryStore.js.map +1 -1
- package/dist/crewfoundry/utils/cn.js +10 -0
- package/dist/crewfoundry/utils/cn.js.map +1 -0
- package/dist/crewfoundry/utils/courseProgress.js +46 -0
- package/dist/crewfoundry/utils/courseProgress.js.map +1 -0
- package/dist/node_modules/clsx/dist/clsx.js +18 -0
- package/dist/node_modules/clsx/dist/clsx.js.map +1 -0
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +1716 -0
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DashboardDashboardPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/DashboardDashboardPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n AuroraBackground,\n EmphasisPanel,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n Pagination,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { Activity, Briefcase, Calendar, TrendingUp, Users } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\nimport { useCrewFoundry } from '../providers/CrewFoundryProvider';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\n\nexport function DashboardDashboardPagePage() {\n const { t: translate } = useI18n();\n const navigate = useNavigate();\n const { showToast } = useGlobalUi();\n const { currentUser } = useCrewFoundry();\n const {\n employees,\n jobPostings,\n interviews,\n leaveRequests,\n notifications,\n projects,\n updateLeaveRequest,\n } = useCrewFoundryStore();\n const [processingLeaveId, setProcessingLeaveId] = useState<string | null>(null);\n\n const approverName =\n currentUser?.name ||\n [currentUser?.firstName, currentUser?.lastName].filter(Boolean).join(' ') ||\n currentUser?.email ||\n 'Manager';\n\n const decideLeave = async (id: string, status: 'approved' | 'rejected') => {\n setProcessingLeaveId(id);\n try {\n await updateLeaveRequest(id, { status, approverName });\n showToast(status === 'approved' ? 'Leave approved' : 'Leave rejected', 'success');\n } catch (error) {\n showToast(error instanceof Error ? error.message : 'Unable to update leave request', 'error');\n } finally {\n setProcessingLeaveId(null);\n }\n };\n const t = (key: string, fallback: string) => {\n const value = translate(key);\n return value === key ? fallback : value;\n };\n\n const activeEmployees = employees.filter((e) => e.status === 'active').length;\n const onLeave = employees.filter((e) => e.status === 'on_leave').length;\n const openPositions = jobPostings.filter((j) => j.status === 'active').length;\n const totalApplicants = jobPostings.reduce((sum, j) => sum + j.applicants, 0);\n const pendingLeaves = leaveRequests.filter((l) => l.status === 'pending').length;\n const unreadNotifications = notifications.filter((n) => !n.read).length;\n const activeProjects = projects.filter((p) => p.status === 'active').length;\n const avgPerformance = (\n employees.reduce((sum, e) => sum + e.performanceScore, 0) / Math.max(employees.length, 1)\n ).toFixed(1);\n\n const departmentHeadcount = employees.reduce<Record<string, number>>((acc, e) => {\n acc[e.department] = (acc[e.department] || 0) + 1;\n return acc;\n }, {});\n\n const headcountValues = Object.values(departmentHeadcount);\n const maxHeadcount = headcountValues.length ? Math.max(...headcountValues) : 1;\n\n const scheduledInterviews = [...interviews]\n .filter((i) => i.status === 'scheduled')\n .sort((a, b) => `${a.date} ${a.time}`.localeCompare(`${b.date} ${b.time}`));\n const upcomingInterviews = scheduledInterviews.slice(0, 5);\n\n const recentLeaveRequests = [...leaveRequests].sort((a, b) => {\n if (a.status === 'pending' && b.status !== 'pending') return -1;\n if (b.status === 'pending' && a.status !== 'pending') return 1;\n return b.startDate.localeCompare(a.startDate);\n });\n const {\n page: leavePage,\n pageSize: leavePageSize,\n offset: leaveOffset,\n setPage: setLeavePage,\n } = usePagination({ initialPageSize: 6 });\n const leavePageItems = recentLeaveRequests.slice(leaveOffset, leaveOffset + leavePageSize);\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"relative overflow-hidden rounded-xl border border-border-seam bg-gloss-card p-6\">\n <AuroraBackground intensity=\"subtle\" />\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>{t('crewfoundry.dashboard.title', 'Workforce Dashboard')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {t(\n 'crewfoundry.dashboard.subtitle',\n \"Overview of your organization's workforce metrics\"\n )}\n </p>\n <PagePurpose className=\"mt-2\">\n {t(\n 'crewfoundry.dashboard.purpose',\n 'Your workforce command center — see headcount, hiring, performance, and projects at a glance, then jump straight to the area that needs your attention.'\n )}\n </PagePurpose>\n </div>\n </div>\n </div>\n\n <NextSteps\n storageKey=\"crewfoundry-dashboard\"\n steps={[\n {\n id: 'employees',\n label: t('crewfoundry.dashboard.next.employees', 'Review your team'),\n description: t(\n 'crewfoundry.dashboard.next.employeesDesc',\n 'Open the employee directory to see who is active and on leave.'\n ),\n icon: <Users className=\"h-4 w-4\" />,\n onClick: () => navigate('../hr/employees-page'),\n },\n {\n id: 'hiring',\n label: t('crewfoundry.dashboard.next.hiring', 'Post a new job'),\n description: t(\n 'crewfoundry.dashboard.next.hiringDesc',\n 'Create an opening to start collecting applicants.'\n ),\n icon: <Briefcase className=\"h-4 w-4\" />,\n onClick: () => navigate('../hiring/create-job-page'),\n },\n {\n id: 'projects',\n label: t('crewfoundry.dashboard.next.projects', 'Check active projects'),\n description: t(\n 'crewfoundry.dashboard.next.projectsDesc',\n 'Track progress and budgets across in-flight work.'\n ),\n icon: <Activity className=\"h-4 w-4\" />,\n onClick: () => navigate('../projects/projects-page'),\n },\n ]}\n />\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\">\n {t('crewfoundry.dashboard.stats.totalEmployees', 'Total Employees')}\n </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\">\n {activeEmployees} {t('crewfoundry.dashboard.stats.active', 'active')}, {onLeave}{' '}\n {t('crewfoundry.dashboard.stats.onLeave', 'on leave')}\n </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\">\n {t('crewfoundry.dashboard.stats.openPositions', 'Open Positions')}\n </CardTitle>\n <Briefcase className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{openPositions}</div>\n <p className=\"text-xs text-text-secondary\">\n {totalApplicants}{' '}\n {t('crewfoundry.dashboard.stats.totalApplicants', 'total applicants')}\n </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\">\n {t('crewfoundry.dashboard.stats.avgPerformance', 'Avg Performance')}\n </CardTitle>\n <TrendingUp className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{avgPerformance}/5</div>\n <p className=\"text-xs text-text-secondary\">\n {t(\n 'crewfoundry.dashboard.stats.performanceTrend',\n `Team average across ${employees.length} employees`\n )}\n </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\">\n {t('crewfoundry.dashboard.stats.activeProjects', 'Active Projects')}\n </CardTitle>\n <Activity className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{activeProjects}</div>\n <p className=\"text-xs text-text-secondary\">\n {pendingLeaves}{' '}\n {t('crewfoundry.dashboard.stats.pendingLeaveRequests', 'pending leave requests')}\n </p>\n </CardContent>\n </GlassCard>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-7\">\n <EmphasisPanel className=\"col-span-4\">\n <div className=\"mb-1 text-lg font-semibold\">\n {t('crewfoundry.dashboard.departmentHeadcount.title', 'Department Headcount')}\n </div>\n <p className=\"mb-4 text-sm text-text-secondary\">\n {t(\n 'crewfoundry.dashboard.departmentHeadcount.description',\n 'Employee distribution across departments'\n )}\n </p>\n <div className=\"space-y-3\">\n {Object.entries(departmentHeadcount)\n .sort(([, a], [, b]) => b - a)\n .map(([dept, count]) => (\n <div key={dept} className=\"flex items-center gap-3\">\n <span className=\"w-24 text-sm text-text-secondary truncate\">{dept}</span>\n <div className=\"flex-1 h-4 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-4 bg-action-primary-bg rounded\"\n style={{ width: `${Math.round((count / maxHeadcount) * 100)}%` }}\n />\n </div>\n <span className=\"text-sm font-medium w-8 text-right tabular-nums\">{count}</span>\n </div>\n ))}\n </div>\n </EmphasisPanel>\n\n <Card className=\"col-span-3\">\n <CardHeader className=\"flex flex-row items-center justify-between\">\n <CardTitle>\n {t('crewfoundry.dashboard.notifications.title', 'Recent Notifications')}\n </CardTitle>\n <Badge variant=\"secondary\">\n {unreadNotifications} {t('crewfoundry.dashboard.notifications.unread', 'unread')}\n </Badge>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {notifications.slice(0, 5).map((notif) => (\n <div key={notif.id} className=\"flex items-start gap-3\">\n <div\n className={`mt-1 h-2 w-2 rounded-full flex-shrink-0 motion-safe:animate-vc-glow-pulse ${\n notif.type === 'success'\n ? 'bg-status-success-text'\n : notif.type === 'warning'\n ? 'bg-status-warning-text'\n : notif.type === 'error'\n ? 'bg-status-error-text'\n : 'bg-status-info-text'\n }`}\n />\n <div className=\"flex-1 min-w-0\">\n <p className={`text-sm ${notif.read ? 'text-text-secondary' : 'font-medium'}`}>\n {notif.title}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">{notif.message}</p>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle>\n {t('crewfoundry.dashboard.upcomingInterviews.title', 'Upcoming Interviews')}\n </CardTitle>\n <CardDescription>\n {t('crewfoundry.dashboard.upcomingInterviews.description', 'Scheduled for this week')}\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {upcomingInterviews.length === 0 && (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t(\n 'crewfoundry.dashboard.upcomingInterviews.emptyTitle',\n 'No interviews scheduled'\n )}\n description={t(\n 'crewfoundry.dashboard.upcomingInterviews.emptyDescription',\n 'Scheduled interviews will appear here.'\n )}\n />\n )}\n {upcomingInterviews.map((interview) => (\n <div\n key={interview.id}\n className=\"flex items-center justify-between rounded border p-3\"\n >\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium truncate\">{interview.candidateName}</p>\n <p className=\"text-xs text-text-secondary truncate\">{interview.position}</p>\n </div>\n <div className=\"flex items-center gap-2 flex-shrink-0\">\n <div className=\"text-right\">\n <p className=\"text-xs font-medium\">{interview.date}</p>\n <p className=\"text-xs text-text-secondary\">{interview.time}</p>\n </div>\n <Badge variant=\"outline\" className=\"text-[10px]\">\n {formatEnumLabel(interview.type)}\n </Badge>\n </div>\n </div>\n ))}\n {scheduledInterviews.length > upcomingInterviews.length && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"w-full\"\n onClick={() => navigate('../hiring/interviews-page')}\n >\n {t('crewfoundry.dashboard.upcomingInterviews.viewAll', 'View all interviews')}\n </Button>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>\n {t('crewfoundry.dashboard.leaveRequests.title', 'Recent Leave Requests')}\n </CardTitle>\n <CardDescription>\n {t('crewfoundry.dashboard.leaveRequests.description', 'Pending approval')}\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {leaveRequests.length === 0 && (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('crewfoundry.dashboard.leaveRequests.emptyTitle', 'No leave requests')}\n description={t(\n 'crewfoundry.dashboard.leaveRequests.emptyDescription',\n 'Pending leave requests will appear here.'\n )}\n />\n )}\n {leavePageItems.map((leave) => (\n <div key={leave.id} className=\"flex items-center justify-between rounded border p-3\">\n <div className=\"flex items-center gap-3\">\n <div>\n <p className=\"text-sm font-medium\">{leave.employeeName}</p>\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <Calendar className=\"h-3 w-3\" />\n <span>\n {leave.startDate} - {leave.endDate}\n </span>\n </div>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n {leave.status === 'pending' && (\n <div className=\"flex gap-1\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 text-xs\"\n disabled={processingLeaveId === leave.id}\n onClick={() => void decideLeave(leave.id, 'rejected')}\n >\n {t('crewfoundry.dashboard.leaveRequests.reject', 'Reject')}\n </Button>\n <Button\n size=\"sm\"\n className=\"h-7 text-xs\"\n disabled={processingLeaveId === leave.id}\n onClick={() => void decideLeave(leave.id, 'approved')}\n >\n {t('crewfoundry.dashboard.leaveRequests.approve', 'Approve')}\n </Button>\n </div>\n )}\n <Badge\n variant={\n leave.status === 'approved'\n ? 'default'\n : leave.status === 'pending'\n ? 'secondary'\n : 'destructive'\n }\n >\n {formatEnumLabel(leave.status)}\n </Badge>\n <Badge variant=\"outline\">{formatEnumLabel(leave.type)}</Badge>\n </div>\n </div>\n ))}\n {recentLeaveRequests.length > leavePageSize && (\n <Pagination\n page={leavePage}\n pageSize={leavePageSize}\n totalItems={recentLeaveRequests.length}\n onPageChange={setLeavePage}\n />\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DashboardDashboardPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/DashboardDashboardPagePage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n AuroraBackground,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n GradientText,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n Pagination,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport {\n Activity,\n Briefcase,\n Calendar,\n FolderPlus,\n Plus,\n TrendingUp,\n UserPlus,\n Users,\n} from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\nimport { useCrewFoundry } from '../providers/CrewFoundryProvider';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\nimport { AreaChart, HBarFunnel, StatCard, getInitials } from '../components/viz';\nimport { headcountByMonth, momDelta } from '../lib/analytics';\n\n/** Cumulative count of entities per absolute month index, from their date field. */\nfunction cumulativeSeries(dates: (string | undefined | null)[]): number[] {\n const indices = dates\n .map((value) => (value ? new Date(value) : null))\n .filter((date): date is Date => date !== null && !Number.isNaN(date.getTime()))\n .map((date) => date.getUTCFullYear() * 12 + date.getUTCMonth());\n if (indices.length === 0) {\n return [];\n }\n const min = Math.min(...indices);\n const max = Math.max(...indices);\n const series: number[] = [];\n for (let index = min; index <= max; index += 1) {\n series.push(indices.filter((start) => start <= index).length);\n }\n return series;\n}\n\n/** Last-vs-prior percentage change of a raw series, for KPI trend arrows. */\nfunction seriesDeltaPercent(values: number[]): number | undefined {\n if (values.length < 2) {\n return undefined;\n }\n const current = values[values.length - 1];\n const previous = values[values.length - 2];\n if (previous <= 0) {\n return undefined;\n }\n return Math.round(((current - previous) / previous) * 100);\n}\n\n/** Circular initials avatar (backend has no interview-candidate photos). */\nfunction InitialsAvatar({ name, size = 38 }: { name: string; size?: number }) {\n return (\n <span\n aria-hidden=\"true\"\n className=\"inline-flex shrink-0 items-center justify-center rounded-full bg-bg-elevated font-medium text-text-secondary\"\n style={{ width: size, height: size, fontSize: Math.round(size * 0.36) }}\n >\n {getInitials(name)}\n </span>\n );\n}\n\nconst STAGE_ORDER = ['new', 'screening', 'interview', 'assessment', 'offer', 'hired'] as const;\n\nexport function DashboardDashboardPagePage() {\n const { t: translate } = useI18n();\n const navigate = useNavigate();\n const { showToast } = useGlobalUi();\n const { currentUser } = useCrewFoundry();\n const {\n employees,\n jobPostings,\n interviews,\n leaveRequests,\n projects,\n candidates,\n announcements,\n updateLeaveRequest,\n } = useCrewFoundryStore();\n const [processingLeaveId, setProcessingLeaveId] = useState<string | null>(null);\n const [expandedAnnouncements, setExpandedAnnouncements] = useState<Record<string, boolean>>({});\n\n const approverName =\n currentUser?.name ||\n [currentUser?.firstName, currentUser?.lastName].filter(Boolean).join(' ') ||\n currentUser?.email ||\n 'Manager';\n\n const decideLeave = async (id: string, status: 'approved' | 'rejected') => {\n setProcessingLeaveId(id);\n try {\n await updateLeaveRequest(id, { status, approverName });\n showToast(status === 'approved' ? 'Leave approved' : 'Leave rejected', 'success');\n } catch (error) {\n showToast(error instanceof Error ? error.message : 'Unable to update leave request', 'error');\n } finally {\n setProcessingLeaveId(null);\n }\n };\n const t = (key: string, fallback: string) => {\n const value = translate(key);\n return value === key ? fallback : value;\n };\n\n const activeEmployees = employees.filter((e) => e.status === 'active').length;\n const onLeave = employees.filter((e) => e.status === 'on_leave').length;\n const openPositions = jobPostings.filter((j) => j.status === 'active').length;\n const totalApplicants = jobPostings.reduce((sum, j) => sum + j.applicants, 0);\n const pendingLeaves = leaveRequests.filter((l) => l.status === 'pending').length;\n const activeProjects = projects.filter((p) => p.status === 'active').length;\n const avgPerformance = (\n employees.reduce((sum, e) => sum + e.performanceScore, 0) / Math.max(employees.length, 1)\n ).toFixed(1);\n\n // Real cumulative headcount time-series from employee start dates.\n const headcountSeries = useMemo(() => headcountByMonth(employees), [employees]);\n const headcountValues = headcountSeries.map((point) => point.value);\n const headcountDelta = momDelta(headcountSeries).deltaPercent;\n\n const openingsSeries = useMemo(\n () => cumulativeSeries(jobPostings.filter((job) => job.status === 'active').map((j) => j.postedDate)),\n [jobPostings]\n );\n const projectsSeries = useMemo(\n () => cumulativeSeries(projects.filter((p) => p.status === 'active').map((p) => p.startDate)),\n [projects]\n );\n\n // Descending hiring funnel: candidates who have reached at least each stage.\n const pipelineCounts = useMemo(() => {\n const rankOf = (status: string) => STAGE_ORDER.indexOf(status as (typeof STAGE_ORDER)[number]);\n const reached = (minRank: number) =>\n candidates.filter((candidate) => rankOf(candidate.status) >= minRank).length;\n return {\n applied: candidates.length,\n screening: reached(1),\n interview: reached(2),\n offer: reached(4),\n hired: reached(5),\n };\n }, [candidates]);\n const pipeline = [\n { label: t('crewfoundry.dashboard.pipeline.applied', 'Applied'), value: pipelineCounts.applied },\n {\n label: t('crewfoundry.dashboard.pipeline.screening', 'Screening'),\n value: pipelineCounts.screening,\n },\n {\n label: t('crewfoundry.dashboard.pipeline.interview', 'Interview'),\n value: pipelineCounts.interview,\n },\n { label: t('crewfoundry.dashboard.pipeline.offer', 'Offer'), value: pipelineCounts.offer },\n { label: t('crewfoundry.dashboard.pipeline.hired', 'Hired'), value: pipelineCounts.hired },\n ];\n\n const employeeByName = useMemo(() => {\n const map = new Map<string, string | undefined>();\n for (const employee of employees) {\n map.set(`${employee.firstName} ${employee.lastName}`.trim().toLowerCase(), employee.avatar);\n }\n return map;\n }, [employees]);\n\n const scheduledInterviews = [...interviews]\n .filter((i) => i.status === 'scheduled')\n .sort((a, b) => `${a.date} ${a.time}`.localeCompare(`${b.date} ${b.time}`));\n const upcomingInterviews = scheduledInterviews.slice(0, 5);\n\n const sortedAnnouncements = useMemo(\n () =>\n [...announcements].sort((a, b) => {\n if (a.isPinned !== b.isPinned) {\n return a.isPinned ? -1 : 1;\n }\n return b.createdAt.localeCompare(a.createdAt);\n }),\n [announcements]\n );\n const topAnnouncements = sortedAnnouncements.slice(0, 4);\n\n const recentLeaveRequests = [...leaveRequests].sort((a, b) => {\n if (a.status === 'pending' && b.status !== 'pending') return -1;\n if (b.status === 'pending' && a.status !== 'pending') return 1;\n return b.startDate.localeCompare(a.startDate);\n });\n const {\n page: leavePage,\n pageSize: leavePageSize,\n offset: leaveOffset,\n setPage: setLeavePage,\n } = usePagination({ initialPageSize: 6 });\n const leavePageItems = recentLeaveRequests.slice(leaveOffset, leaveOffset + leavePageSize);\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"relative overflow-hidden rounded-xl border border-border-seam bg-gloss-card p-6\">\n <AuroraBackground intensity=\"subtle\" />\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>{t('crewfoundry.dashboard.title', 'Command Center')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {t('crewfoundry.dashboard.subtitle', 'Your workforce at a glance')}\n </p>\n <PagePurpose className=\"mt-2\">\n {t(\n 'crewfoundry.dashboard.purpose',\n 'Your workforce command center — see headcount, hiring, performance, and projects at a glance, then jump straight to the area that needs your attention.'\n )}\n </PagePurpose>\n </div>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button className=\"self-start\">\n <Plus className=\"mr-1 h-4 w-4\" />\n {t('crewfoundry.dashboard.new', 'New')}\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-52\">\n <DropdownMenuLabel>{t('crewfoundry.dashboard.newMenu', 'Create')}</DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuItem onClick={() => navigate('../hr/employees-page')}>\n <UserPlus className=\"mr-2 h-4 w-4\" />\n {t('crewfoundry.dashboard.newEmployee', 'Add employee')}\n </DropdownMenuItem>\n <DropdownMenuItem onClick={() => navigate('../hiring/create-job-page')}>\n <Briefcase className=\"mr-2 h-4 w-4\" />\n {t('crewfoundry.dashboard.newJob', 'Post a job')}\n </DropdownMenuItem>\n <DropdownMenuItem onClick={() => navigate('../projects/projects-page')}>\n <FolderPlus className=\"mr-2 h-4 w-4\" />\n {t('crewfoundry.dashboard.newProject', 'New project')}\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n\n <NextSteps\n storageKey=\"crewfoundry-dashboard\"\n steps={[\n {\n id: 'employees',\n label: t('crewfoundry.dashboard.next.employees', 'Review your team'),\n description: t(\n 'crewfoundry.dashboard.next.employeesDesc',\n 'Open the employee directory to see who is active and on leave.'\n ),\n icon: <Users className=\"h-4 w-4\" />,\n onClick: () => navigate('../hr/employees-page'),\n },\n {\n id: 'hiring',\n label: t('crewfoundry.dashboard.next.hiring', 'Post a new job'),\n description: t(\n 'crewfoundry.dashboard.next.hiringDesc',\n 'Create an opening to start collecting applicants.'\n ),\n icon: <Briefcase className=\"h-4 w-4\" />,\n onClick: () => navigate('../hiring/create-job-page'),\n },\n {\n id: 'projects',\n label: t('crewfoundry.dashboard.next.projects', 'Check active projects'),\n description: t(\n 'crewfoundry.dashboard.next.projectsDesc',\n 'Track progress and budgets across in-flight work.'\n ),\n icon: <Activity className=\"h-4 w-4\" />,\n onClick: () => navigate('../projects/projects-page'),\n },\n ]}\n />\n\n {/* Bento: KPI grid + headcount growth + upcoming interviews */}\n <div className=\"grid gap-4 lg:grid-cols-12\">\n <div className=\"grid gap-4 sm:grid-cols-2 lg:col-span-5\">\n <StatCard\n label={t('crewfoundry.dashboard.stats.totalEmployees', 'Total Employees')}\n value={employees.length}\n delta={headcountDelta || undefined}\n deltaLabel={t('crewfoundry.dashboard.stats.fromLastMonth', 'from last month')}\n icon={<Users className=\"h-4 w-4\" />}\n sparkline={headcountValues}\n sparklineVariant=\"area\"\n />\n <StatCard\n label={t('crewfoundry.dashboard.stats.openPositions', 'Open Positions')}\n value={openPositions}\n delta={seriesDeltaPercent(openingsSeries)}\n deltaLabel={t('crewfoundry.dashboard.stats.fromLastMonth', 'from last month')}\n icon={<Briefcase className=\"h-4 w-4\" />}\n sparkline={openingsSeries}\n sparklineVariant=\"area\"\n />\n <StatCard\n label={t('crewfoundry.dashboard.stats.activeProjects', 'Active Projects')}\n value={activeProjects}\n delta={seriesDeltaPercent(projectsSeries)}\n deltaLabel={t('crewfoundry.dashboard.stats.fromLastMonth', 'from last month')}\n icon={<Activity className=\"h-4 w-4\" />}\n sparkline={projectsSeries}\n sparklineVariant=\"area\"\n />\n <StatCard\n label={t('crewfoundry.dashboard.stats.avgPerformance', 'Avg Performance')}\n value={`${avgPerformance}`}\n icon={<TrendingUp className=\"h-4 w-4\" />}\n />\n </div>\n\n <Card className=\"lg:col-span-4\">\n <CardHeader>\n <CardTitle className=\"text-base\">\n {t('crewfoundry.dashboard.headcountGrowth.title', 'Headcount growth')}\n </CardTitle>\n </CardHeader>\n <CardContent>\n {headcountSeries.length > 1 ? (\n <AreaChart\n series={headcountSeries}\n height={210}\n title=\"Cumulative headcount by month\"\n />\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('crewfoundry.dashboard.headcountGrowth.emptyTitle', 'Not enough history')}\n description={t(\n 'crewfoundry.dashboard.headcountGrowth.emptyDescription',\n 'Headcount growth appears once employees span more than one hiring month.'\n )}\n />\n )}\n </CardContent>\n </Card>\n\n <Card className=\"lg:col-span-3\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0\">\n <CardTitle className=\"text-base\">\n {t('crewfoundry.dashboard.upcomingInterviews.title', 'Upcoming interviews')}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {upcomingInterviews.length === 0 && (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t(\n 'crewfoundry.dashboard.upcomingInterviews.emptyTitle',\n 'No interviews scheduled'\n )}\n description={t(\n 'crewfoundry.dashboard.upcomingInterviews.emptyDescription',\n 'Scheduled interviews will appear here.'\n )}\n />\n )}\n {upcomingInterviews.map((interview) => {\n const avatar = employeeByName.get(interview.candidateName.trim().toLowerCase());\n return (\n <div key={interview.id} className=\"flex items-center gap-3\">\n {avatar ? (\n <img\n src={avatar}\n alt=\"\"\n className=\"h-9 w-9 shrink-0 rounded-full object-cover\"\n />\n ) : (\n <InitialsAvatar name={interview.candidateName} />\n )}\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium\">{interview.candidateName}</p>\n <p className=\"truncate text-xs text-text-secondary\">{interview.position}</p>\n </div>\n <div className=\"flex shrink-0 items-center gap-1.5 text-right\">\n <div>\n <p className=\"text-xs font-medium\">{interview.time}</p>\n <p className=\"text-[10px] text-text-secondary\">{interview.date}</p>\n </div>\n <Calendar className=\"h-3.5 w-3.5 text-text-secondary\" />\n </div>\n </div>\n );\n })}\n {scheduledInterviews.length > upcomingInterviews.length && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"w-full\"\n onClick={() => navigate('../hiring/interviews-page')}\n >\n {t('crewfoundry.dashboard.upcomingInterviews.viewAll', 'View All')}\n </Button>\n )}\n </CardContent>\n </Card>\n </div>\n\n {/* Hiring pipeline funnel + announcements rail */}\n <div className=\"grid gap-4 lg:grid-cols-12\">\n <Card className=\"lg:col-span-8\">\n <CardHeader>\n <CardTitle className=\"text-base\">\n {t('crewfoundry.dashboard.pipeline.title', 'Hiring pipeline')}\n </CardTitle>\n </CardHeader>\n <CardContent>\n {candidates.length > 0 ? (\n <HBarFunnel stages={pipeline} valueFormatter={(value) => `${value}`} />\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('crewfoundry.dashboard.pipeline.emptyTitle', 'No candidates yet')}\n description={t(\n 'crewfoundry.dashboard.pipeline.emptyDescription',\n 'Your hiring funnel appears here once candidates enter the pipeline.'\n )}\n />\n )}\n </CardContent>\n </Card>\n\n <Card className=\"lg:col-span-4\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0\">\n <CardTitle className=\"text-base\">\n {t('crewfoundry.dashboard.announcements.title', 'Announcements')}\n </CardTitle>\n {sortedAnnouncements.length > topAnnouncements.length && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => navigate('../communications/announcements-page')}\n >\n {t('crewfoundry.dashboard.announcements.viewAll', 'View All')}\n </Button>\n )}\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {topAnnouncements.length === 0 && (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('crewfoundry.dashboard.announcements.emptyTitle', 'No announcements')}\n description={t(\n 'crewfoundry.dashboard.announcements.emptyDescription',\n 'Company announcements will appear here.'\n )}\n />\n )}\n {topAnnouncements.map((announcement) => {\n const expanded = expandedAnnouncements[announcement.id];\n const isLong = announcement.content.length > 120;\n return (\n <div key={announcement.id} className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <p className=\"text-sm font-medium\">{announcement.title}</p>\n {announcement.isPinned && (\n <Badge variant=\"outline\" className=\"text-[10px]\">\n {t('crewfoundry.dashboard.announcements.pinned', 'Pinned')}\n </Badge>\n )}\n </div>\n <p className=\"text-xs text-text-secondary\">\n {announcement.createdAt} · {announcement.author}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {expanded || !isLong\n ? announcement.content\n : `${announcement.content.slice(0, 120)}…`}\n </p>\n {isLong && (\n <button\n type=\"button\"\n className=\"text-xs font-medium text-action-primary-bg hover:underline\"\n onClick={() =>\n setExpandedAnnouncements((prev) => ({\n ...prev,\n [announcement.id]: !prev[announcement.id],\n }))\n }\n >\n {expanded\n ? t('crewfoundry.dashboard.announcements.readLess', 'Read Less')\n : t('crewfoundry.dashboard.announcements.readMore', 'Read More')}\n </button>\n )}\n </div>\n );\n })}\n </CardContent>\n </Card>\n </div>\n\n {/* Leave approvals (real approve / reject) */}\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">\n {t('crewfoundry.dashboard.leaveRequests.title', 'Recent Leave Requests')}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {leaveRequests.length === 0 && (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('crewfoundry.dashboard.leaveRequests.emptyTitle', 'No leave requests')}\n description={t(\n 'crewfoundry.dashboard.leaveRequests.emptyDescription',\n 'Pending leave requests will appear here.'\n )}\n />\n )}\n {leavePageItems.map((leave) => (\n <div key={leave.id} className=\"flex items-center justify-between rounded border p-3\">\n <div className=\"flex items-center gap-3\">\n <InitialsAvatar name={leave.employeeName} size={34} />\n <div>\n <p className=\"text-sm font-medium\">{leave.employeeName}</p>\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <Calendar className=\"h-3 w-3\" />\n <span>\n {leave.startDate} - {leave.endDate}\n </span>\n </div>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n {leave.status === 'pending' && (\n <div className=\"flex gap-1\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 text-xs\"\n disabled={processingLeaveId === leave.id}\n onClick={() => void decideLeave(leave.id, 'rejected')}\n >\n {t('crewfoundry.dashboard.leaveRequests.reject', 'Reject')}\n </Button>\n <Button\n size=\"sm\"\n className=\"h-7 text-xs\"\n disabled={processingLeaveId === leave.id}\n onClick={() => void decideLeave(leave.id, 'approved')}\n >\n {t('crewfoundry.dashboard.leaveRequests.approve', 'Approve')}\n </Button>\n </div>\n )}\n <Badge\n variant={\n leave.status === 'approved'\n ? 'default'\n : leave.status === 'pending'\n ? 'secondary'\n : 'destructive'\n }\n >\n {formatEnumLabel(leave.status)}\n </Badge>\n <Badge variant=\"outline\">{formatEnumLabel(leave.type)}</Badge>\n </div>\n </div>\n ))}\n {recentLeaveRequests.length > leavePageSize && (\n <Pagination\n page={leavePage}\n pageSize={leavePageSize}\n totalItems={recentLeaveRequests.length}\n onPageChange={setLeavePage}\n />\n )}\n </CardContent>\n </Card>\n\n <p className=\"text-center text-xs text-text-muted\">\n {activeEmployees} {t('crewfoundry.dashboard.stats.active', 'active')} · {onLeave}{' '}\n {t('crewfoundry.dashboard.stats.onLeave', 'on leave')} · {totalApplicants}{' '}\n {t('crewfoundry.dashboard.stats.totalApplicants', 'total applicants')} · {pendingLeaves}{' '}\n {t('crewfoundry.dashboard.stats.pendingLeaveRequests', 'pending leave requests')}\n </p>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import { useCrewFoundryStore as e } from "../store/crewfoundryStore.js";
|
|
2
2
|
import { formatEnumLabel as t } from "../utils/formatEnumLabel.js";
|
|
3
|
-
import { Clock as n, FileText as r, Star as i, UserCheck as a, Users as o } from "lucide-react";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { Clock as n, FileText as r, Star as i, UserCheck as a, Users as o, X as s } from "lucide-react";
|
|
4
|
+
import { useSearchParams as c } from "react-router-dom";
|
|
5
|
+
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
6
|
+
import { Badge as d, Button as f, Card as p, CardContent as m, CardDescription as h, CardHeader as g, CardTitle as _, GlassCard as v, GradientText as y, IllustratedEmptyState as b, PagePurpose as x, Pagination as S, ResponsiveTable as C } from "@burdenoff/fe-libs/ui";
|
|
7
|
+
import { usePagination as w } from "@burdenoff/fe-libs/shared/hooks";
|
|
7
8
|
//#region src/crewfoundry/pages/HiringApplicationsPagePage.tsx
|
|
8
|
-
var
|
|
9
|
+
var T = [
|
|
9
10
|
{
|
|
10
11
|
key: "candidate",
|
|
11
12
|
header: "Candidate",
|
|
12
13
|
priority: "primary",
|
|
13
|
-
cell: (e) => /* @__PURE__ */
|
|
14
|
+
cell: (e) => /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ u("p", {
|
|
14
15
|
className: "font-medium",
|
|
15
16
|
children: [
|
|
16
17
|
e.firstName,
|
|
17
18
|
" ",
|
|
18
19
|
e.lastName
|
|
19
20
|
]
|
|
20
|
-
}), /* @__PURE__ */
|
|
21
|
+
}), /* @__PURE__ */ l("p", {
|
|
21
22
|
className: "text-xs text-text-secondary",
|
|
22
23
|
children: e.email
|
|
23
24
|
})] })
|
|
@@ -26,7 +27,7 @@ var S = [
|
|
|
26
27
|
key: "position",
|
|
27
28
|
header: "Position",
|
|
28
29
|
priority: "secondary",
|
|
29
|
-
cell: (e) => /* @__PURE__ */
|
|
30
|
+
cell: (e) => /* @__PURE__ */ l("span", {
|
|
30
31
|
className: "text-text-secondary",
|
|
31
32
|
children: e.position
|
|
32
33
|
})
|
|
@@ -35,7 +36,7 @@ var S = [
|
|
|
35
36
|
key: "company",
|
|
36
37
|
header: "Company",
|
|
37
38
|
priority: "secondary",
|
|
38
|
-
cell: (e) => /* @__PURE__ */
|
|
39
|
+
cell: (e) => /* @__PURE__ */ l("span", {
|
|
39
40
|
className: "text-text-secondary",
|
|
40
41
|
children: e.currentCompany
|
|
41
42
|
})
|
|
@@ -44,24 +45,24 @@ var S = [
|
|
|
44
45
|
key: "experience",
|
|
45
46
|
header: "Experience",
|
|
46
47
|
priority: "hidden-on-mobile",
|
|
47
|
-
cell: (e) => /* @__PURE__ */
|
|
48
|
+
cell: (e) => /* @__PURE__ */ u("span", { children: [e.experience, " yrs"] })
|
|
48
49
|
},
|
|
49
50
|
{
|
|
50
51
|
key: "rating",
|
|
51
52
|
header: "Rating",
|
|
52
53
|
priority: "hidden-on-mobile",
|
|
53
|
-
cell: (e) => /* @__PURE__ */
|
|
54
|
+
cell: (e) => /* @__PURE__ */ u("span", {
|
|
54
55
|
className: "flex items-center gap-1",
|
|
55
|
-
children: [/* @__PURE__ */
|
|
56
|
+
children: [/* @__PURE__ */ l(i, { className: "h-3 w-3 text-status-warning-text" }), e.rating]
|
|
56
57
|
})
|
|
57
58
|
},
|
|
58
59
|
{
|
|
59
60
|
key: "skills",
|
|
60
61
|
header: "Skills",
|
|
61
62
|
priority: "secondary",
|
|
62
|
-
cell: (e) => /* @__PURE__ */
|
|
63
|
+
cell: (e) => /* @__PURE__ */ l("div", {
|
|
63
64
|
className: "flex flex-wrap gap-1",
|
|
64
|
-
children: e.skills.slice(0, 2).map((e) => /* @__PURE__ */
|
|
65
|
+
children: e.skills.slice(0, 2).map((e) => /* @__PURE__ */ l(d, {
|
|
65
66
|
variant: "outline",
|
|
66
67
|
className: "text-[9px]",
|
|
67
68
|
children: e
|
|
@@ -72,134 +73,142 @@ var S = [
|
|
|
72
73
|
key: "status",
|
|
73
74
|
header: "Status",
|
|
74
75
|
priority: "secondary",
|
|
75
|
-
cell: (e) => /* @__PURE__ */
|
|
76
|
+
cell: (e) => /* @__PURE__ */ l(d, {
|
|
76
77
|
variant: e.status === "hired" || e.status === "offer" ? "default" : e.status === "rejected" ? "destructive" : "secondary",
|
|
77
78
|
className: "text-[10px]",
|
|
78
79
|
children: t(e.status)
|
|
79
80
|
})
|
|
80
81
|
}
|
|
81
82
|
];
|
|
82
|
-
function
|
|
83
|
-
let { candidates: i, jobPostings:
|
|
84
|
-
|
|
83
|
+
function E() {
|
|
84
|
+
let { candidates: i, jobPostings: E } = e(), [D, O] = c(), { page: k, pageSize: A, offset: j, setPage: M } = w({ initialPageSize: 25 }), N = D.get("position"), P = N?.trim().toLowerCase() ?? null, F = P ? i.filter((e) => e.position.trim().toLowerCase() === P) : i, I = () => {
|
|
85
|
+
let e = new URLSearchParams(D);
|
|
86
|
+
e.delete("position"), O(e, { replace: !0 }), M(1);
|
|
87
|
+
}, L = F.filter((e) => e.status === "new"), R = F.filter((e) => e.status === "screening"), z = F.filter((e) => e.status === "interview"), B = F.filter((e) => e.status === "hired").length, V = P ? F.length : E.reduce((e, t) => e + t.applicants, 0), H = V > 0 ? B / V * 100 : 0, U = Math.max(1, ...E.map((e) => e.applicants)), W = [...E].sort((e, t) => t.applicants - e.applicants), G = F.slice(j, j + A);
|
|
88
|
+
return /* @__PURE__ */ u("div", {
|
|
85
89
|
className: "space-y-6 p-6",
|
|
86
90
|
children: [
|
|
87
|
-
/* @__PURE__ */
|
|
91
|
+
/* @__PURE__ */ u("div", {
|
|
88
92
|
className: "flex items-center justify-between",
|
|
89
|
-
children: /* @__PURE__ */
|
|
90
|
-
/* @__PURE__ */
|
|
93
|
+
children: [/* @__PURE__ */ u("div", { children: [
|
|
94
|
+
/* @__PURE__ */ l("h1", {
|
|
91
95
|
className: "text-2xl font-semibold",
|
|
92
|
-
children: /* @__PURE__ */
|
|
96
|
+
children: /* @__PURE__ */ l(y, { children: "Applications" })
|
|
93
97
|
}),
|
|
94
|
-
/* @__PURE__ */
|
|
98
|
+
/* @__PURE__ */ l("p", {
|
|
95
99
|
className: "text-sm text-text-secondary",
|
|
96
|
-
children:
|
|
100
|
+
children: N ? `${F.length} applications for ${N}` : `${V} total applications across all positions`
|
|
97
101
|
}),
|
|
98
|
-
/* @__PURE__ */
|
|
102
|
+
/* @__PURE__ */ l(x, {
|
|
99
103
|
className: "mt-1",
|
|
100
104
|
children: "Review and triage incoming job applications — move candidates through your pipeline and decide who to interview next."
|
|
101
105
|
})
|
|
102
|
-
] })
|
|
106
|
+
] }), N && /* @__PURE__ */ u(f, {
|
|
107
|
+
variant: "outline",
|
|
108
|
+
size: "sm",
|
|
109
|
+
onClick: I,
|
|
110
|
+
children: [/* @__PURE__ */ l(s, { className: "mr-1 h-4 w-4" }), N]
|
|
111
|
+
})]
|
|
103
112
|
}),
|
|
104
|
-
/* @__PURE__ */
|
|
113
|
+
/* @__PURE__ */ u("div", {
|
|
105
114
|
className: "grid gap-4 md:grid-cols-2 lg:grid-cols-4",
|
|
106
115
|
children: [
|
|
107
|
-
/* @__PURE__ */
|
|
116
|
+
/* @__PURE__ */ u(v, {
|
|
108
117
|
treatment: "glass",
|
|
109
118
|
glow: !0,
|
|
110
119
|
className: "border-border-seam",
|
|
111
|
-
children: [/* @__PURE__ */
|
|
120
|
+
children: [/* @__PURE__ */ u(g, {
|
|
112
121
|
className: "flex flex-row items-center justify-between space-y-0 pb-2",
|
|
113
|
-
children: [/* @__PURE__ */
|
|
122
|
+
children: [/* @__PURE__ */ l(_, {
|
|
114
123
|
className: "text-sm font-medium",
|
|
115
124
|
children: "New"
|
|
116
|
-
}), /* @__PURE__ */
|
|
117
|
-
}), /* @__PURE__ */
|
|
125
|
+
}), /* @__PURE__ */ l(r, { className: "h-4 w-4 text-status-info-text" })]
|
|
126
|
+
}), /* @__PURE__ */ u(m, { children: [/* @__PURE__ */ l("div", {
|
|
118
127
|
className: "text-2xl font-bold tabular-nums",
|
|
119
|
-
children:
|
|
120
|
-
}), /* @__PURE__ */
|
|
128
|
+
children: L.length
|
|
129
|
+
}), /* @__PURE__ */ l("p", {
|
|
121
130
|
className: "text-xs text-text-secondary",
|
|
122
131
|
children: "Unreviewed applications"
|
|
123
132
|
})] })]
|
|
124
133
|
}),
|
|
125
|
-
/* @__PURE__ */
|
|
134
|
+
/* @__PURE__ */ u(v, {
|
|
126
135
|
treatment: "metric",
|
|
127
136
|
className: "border-border-seam",
|
|
128
|
-
children: [/* @__PURE__ */
|
|
137
|
+
children: [/* @__PURE__ */ u(g, {
|
|
129
138
|
className: "flex flex-row items-center justify-between space-y-0 pb-2",
|
|
130
|
-
children: [/* @__PURE__ */
|
|
139
|
+
children: [/* @__PURE__ */ l(_, {
|
|
131
140
|
className: "text-sm font-medium",
|
|
132
141
|
children: "Screening"
|
|
133
|
-
}), /* @__PURE__ */
|
|
134
|
-
}), /* @__PURE__ */
|
|
142
|
+
}), /* @__PURE__ */ l(n, { className: "h-4 w-4 text-status-warning-text" })]
|
|
143
|
+
}), /* @__PURE__ */ u(m, { children: [/* @__PURE__ */ l("div", {
|
|
135
144
|
className: "text-2xl font-bold tabular-nums",
|
|
136
|
-
children:
|
|
137
|
-
}), /* @__PURE__ */
|
|
145
|
+
children: R.length
|
|
146
|
+
}), /* @__PURE__ */ l("p", {
|
|
138
147
|
className: "text-xs text-text-secondary",
|
|
139
148
|
children: "Under review"
|
|
140
149
|
})] })]
|
|
141
150
|
}),
|
|
142
|
-
/* @__PURE__ */
|
|
151
|
+
/* @__PURE__ */ u(v, {
|
|
143
152
|
treatment: "metric",
|
|
144
153
|
className: "border-border-seam",
|
|
145
|
-
children: [/* @__PURE__ */
|
|
154
|
+
children: [/* @__PURE__ */ u(g, {
|
|
146
155
|
className: "flex flex-row items-center justify-between space-y-0 pb-2",
|
|
147
|
-
children: [/* @__PURE__ */
|
|
156
|
+
children: [/* @__PURE__ */ l(_, {
|
|
148
157
|
className: "text-sm font-medium",
|
|
149
158
|
children: "Interview Stage"
|
|
150
|
-
}), /* @__PURE__ */
|
|
151
|
-
}), /* @__PURE__ */
|
|
159
|
+
}), /* @__PURE__ */ l(o, { className: "h-4 w-4 text-action-primary-bg" })]
|
|
160
|
+
}), /* @__PURE__ */ u(m, { children: [/* @__PURE__ */ l("div", {
|
|
152
161
|
className: "text-2xl font-bold tabular-nums",
|
|
153
|
-
children:
|
|
154
|
-
}), /* @__PURE__ */
|
|
162
|
+
children: z.length
|
|
163
|
+
}), /* @__PURE__ */ l("p", {
|
|
155
164
|
className: "text-xs text-text-secondary",
|
|
156
165
|
children: "Moving forward"
|
|
157
166
|
})] })]
|
|
158
167
|
}),
|
|
159
|
-
/* @__PURE__ */
|
|
168
|
+
/* @__PURE__ */ u(v, {
|
|
160
169
|
treatment: "metric",
|
|
161
170
|
className: "border-border-seam",
|
|
162
|
-
children: [/* @__PURE__ */
|
|
171
|
+
children: [/* @__PURE__ */ u(g, {
|
|
163
172
|
className: "flex flex-row items-center justify-between space-y-0 pb-2",
|
|
164
|
-
children: [/* @__PURE__ */
|
|
173
|
+
children: [/* @__PURE__ */ l(_, {
|
|
165
174
|
className: "text-sm font-medium",
|
|
166
175
|
children: "Conversion Rate"
|
|
167
|
-
}), /* @__PURE__ */
|
|
168
|
-
}), /* @__PURE__ */
|
|
176
|
+
}), /* @__PURE__ */ l(a, { className: "h-4 w-4 text-status-success-text" })]
|
|
177
|
+
}), /* @__PURE__ */ u(m, { children: [/* @__PURE__ */ u("div", {
|
|
169
178
|
className: "text-2xl font-bold tabular-nums",
|
|
170
|
-
children: [
|
|
171
|
-
}), /* @__PURE__ */
|
|
179
|
+
children: [H.toFixed(1), "%"]
|
|
180
|
+
}), /* @__PURE__ */ l("p", {
|
|
172
181
|
className: "text-xs text-text-secondary",
|
|
173
182
|
children: "Hired vs. total applications"
|
|
174
183
|
})] })]
|
|
175
184
|
})
|
|
176
185
|
]
|
|
177
186
|
}),
|
|
178
|
-
/* @__PURE__ */
|
|
187
|
+
/* @__PURE__ */ u(p, { children: [/* @__PURE__ */ u(g, { children: [/* @__PURE__ */ l(_, { children: "Applications by Position" }), /* @__PURE__ */ l(h, { children: "Applicant count per open role" })] }), /* @__PURE__ */ l(m, {
|
|
179
188
|
className: "space-y-3",
|
|
180
|
-
children:
|
|
189
|
+
children: W.length === 0 ? /* @__PURE__ */ l(b, {
|
|
181
190
|
illustration: "empty-search",
|
|
182
191
|
title: "No positions yet",
|
|
183
192
|
description: "Create a job posting to start collecting applications."
|
|
184
|
-
}) :
|
|
193
|
+
}) : W.map((e) => /* @__PURE__ */ u("div", {
|
|
185
194
|
className: "flex items-center gap-3",
|
|
186
195
|
children: [
|
|
187
|
-
/* @__PURE__ */
|
|
196
|
+
/* @__PURE__ */ l("span", {
|
|
188
197
|
className: "w-56 text-sm font-medium truncate",
|
|
189
198
|
children: e.title
|
|
190
199
|
}),
|
|
191
|
-
/* @__PURE__ */
|
|
200
|
+
/* @__PURE__ */ l("div", {
|
|
192
201
|
className: "flex-1 h-4 bg-bg-sunken rounded overflow-hidden",
|
|
193
|
-
children: /* @__PURE__ */
|
|
202
|
+
children: /* @__PURE__ */ l("div", {
|
|
194
203
|
className: "h-4 bg-action-primary-bg rounded",
|
|
195
|
-
style: { width: `${Math.round(e.applicants /
|
|
204
|
+
style: { width: `${Math.round(e.applicants / U * 100)}%` }
|
|
196
205
|
})
|
|
197
206
|
}),
|
|
198
|
-
/* @__PURE__ */
|
|
207
|
+
/* @__PURE__ */ l("span", {
|
|
199
208
|
className: "text-sm font-medium w-10 text-right",
|
|
200
209
|
children: e.applicants
|
|
201
210
|
}),
|
|
202
|
-
/* @__PURE__ */
|
|
211
|
+
/* @__PURE__ */ l(d, {
|
|
203
212
|
variant: e.status === "active" ? "default" : "secondary",
|
|
204
213
|
className: "text-[10px]",
|
|
205
214
|
children: t(e.status)
|
|
@@ -207,28 +216,28 @@ function C() {
|
|
|
207
216
|
]
|
|
208
217
|
}, e.id))
|
|
209
218
|
})] }),
|
|
210
|
-
/* @__PURE__ */
|
|
211
|
-
data:
|
|
219
|
+
/* @__PURE__ */ u(p, { children: [/* @__PURE__ */ u(g, { children: [/* @__PURE__ */ l(_, { children: N ? `Applicants — ${N}` : "All Candidates" }), /* @__PURE__ */ l(h, { children: N ? "Candidates who applied to this role" : "Complete candidate pipeline" })] }), /* @__PURE__ */ u(m, { children: [/* @__PURE__ */ l(C, {
|
|
220
|
+
data: G,
|
|
212
221
|
rowKey: (e) => e.id,
|
|
213
|
-
columns:
|
|
214
|
-
emptyState: /* @__PURE__ */
|
|
222
|
+
columns: T,
|
|
223
|
+
emptyState: /* @__PURE__ */ l(b, {
|
|
215
224
|
illustration: "empty-search",
|
|
216
225
|
title: "No candidates yet",
|
|
217
226
|
description: "Candidates you add to your pipeline will appear here."
|
|
218
227
|
})
|
|
219
|
-
}),
|
|
228
|
+
}), F.length > A && /* @__PURE__ */ l("div", {
|
|
220
229
|
className: "mt-4",
|
|
221
|
-
children: /* @__PURE__ */
|
|
222
|
-
page:
|
|
223
|
-
pageSize:
|
|
224
|
-
totalItems:
|
|
225
|
-
onPageChange:
|
|
230
|
+
children: /* @__PURE__ */ l(S, {
|
|
231
|
+
page: k,
|
|
232
|
+
pageSize: A,
|
|
233
|
+
totalItems: F.length,
|
|
234
|
+
onPageChange: M
|
|
226
235
|
})
|
|
227
236
|
})] })] })
|
|
228
237
|
]
|
|
229
238
|
});
|
|
230
239
|
}
|
|
231
240
|
//#endregion
|
|
232
|
-
export {
|
|
241
|
+
export { E as HiringApplicationsPagePage };
|
|
233
242
|
|
|
234
243
|
//# sourceMappingURL=HiringApplicationsPagePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HiringApplicationsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/HiringApplicationsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n Pagination,\n ResponsiveTable,\n type ResponsiveTableColumn,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { Clock, FileText, Star, UserCheck, Users } from 'lucide-react';\nimport { useCrewFoundryStore, type Candidate } from '../store/crewfoundryStore';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\n\nconst CANDIDATE_COLUMNS: ResponsiveTableColumn<Candidate>[] = [\n {\n key: 'candidate',\n header: 'Candidate',\n priority: 'primary',\n cell: (c) => (\n <div>\n <p className=\"font-medium\">\n {c.firstName} {c.lastName}\n </p>\n <p className=\"text-xs text-text-secondary\">{c.email}</p>\n </div>\n ),\n },\n {\n key: 'position',\n header: 'Position',\n priority: 'secondary',\n cell: (c) => <span className=\"text-text-secondary\">{c.position}</span>,\n },\n {\n key: 'company',\n header: 'Company',\n priority: 'secondary',\n cell: (c) => <span className=\"text-text-secondary\">{c.currentCompany}</span>,\n },\n {\n key: 'experience',\n header: 'Experience',\n priority: 'hidden-on-mobile',\n cell: (c) => <span>{c.experience} yrs</span>,\n },\n {\n key: 'rating',\n header: 'Rating',\n priority: 'hidden-on-mobile',\n cell: (c) => (\n <span className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-status-warning-text\" />\n {c.rating}\n </span>\n ),\n },\n {\n key: 'skills',\n header: 'Skills',\n priority: 'secondary',\n cell: (c) => (\n <div className=\"flex flex-wrap gap-1\">\n {c.skills.slice(0, 2).map((s) => (\n <Badge key={s} variant=\"outline\" className=\"text-[9px]\">\n {s}\n </Badge>\n ))}\n </div>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n priority: 'secondary',\n cell: (c) => (\n <Badge\n variant={\n c.status === 'hired'\n ? 'default'\n : c.status === 'offer'\n ? 'default'\n : c.status === 'rejected'\n ? 'destructive'\n : 'secondary'\n }\n className=\"text-[10px]\"\n >\n {formatEnumLabel(c.status)}\n </Badge>\n ),\n },\n];\n\nexport function HiringApplicationsPagePage() {\n const { candidates, jobPostings } = useCrewFoundryStore();\n const { page, pageSize, offset, setPage } = usePagination({ initialPageSize: 25 });\n\n const newApps = candidates.filter((c) => c.status === 'new');\n const screeningApps = candidates.filter((c) => c.status === 'screening');\n const interviewApps = candidates.filter((c) => c.status === 'interview');\n const hiredCount = candidates.filter((c) => c.status === 'hired').length;\n const totalApps = jobPostings.reduce((s, j) => s + j.applicants, 0);\n const conversionRate = totalApps > 0 ? (hiredCount / totalApps) * 100 : 0;\n const maxApplicants = Math.max(1, ...jobPostings.map((j) => j.applicants));\n const rankedPositions = [...jobPostings].sort((a, b) => b.applicants - a.applicants);\n const pagedCandidates = candidates.slice(offset, offset + pageSize);\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>Applications</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {totalApps} total applications across all positions\n </p>\n <PagePurpose className=\"mt-1\">\n Review and triage incoming job applications — move candidates through your pipeline and\n decide who to interview next.\n </PagePurpose>\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\">New</CardTitle>\n <FileText className=\"h-4 w-4 text-status-info-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{newApps.length}</div>\n <p className=\"text-xs text-text-secondary\">Unreviewed applications</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\">Screening</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\">{screeningApps.length}</div>\n <p className=\"text-xs text-text-secondary\">Under review</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\">Interview Stage</CardTitle>\n <Users className=\"h-4 w-4 text-action-primary-bg\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{interviewApps.length}</div>\n <p className=\"text-xs text-text-secondary\">Moving forward</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\">Conversion Rate</CardTitle>\n <UserCheck className=\"h-4 w-4 text-status-success-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{conversionRate.toFixed(1)}%</div>\n <p className=\"text-xs text-text-secondary\">Hired vs. total applications</p>\n </CardContent>\n </GlassCard>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Applications by Position</CardTitle>\n <CardDescription>Applicant count per open role</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {rankedPositions.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No positions yet\"\n description=\"Create a job posting to start collecting applications.\"\n />\n ) : (\n rankedPositions.map((job) => (\n <div key={job.id} className=\"flex items-center gap-3\">\n <span className=\"w-56 text-sm font-medium truncate\">{job.title}</span>\n <div className=\"flex-1 h-4 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-4 bg-action-primary-bg rounded\"\n style={{ width: `${Math.round((job.applicants / maxApplicants) * 100)}%` }}\n />\n </div>\n <span className=\"text-sm font-medium w-10 text-right\">{job.applicants}</span>\n <Badge\n variant={job.status === 'active' ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {formatEnumLabel(job.status)}\n </Badge>\n </div>\n ))\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>All Candidates</CardTitle>\n <CardDescription>Complete candidate pipeline</CardDescription>\n </CardHeader>\n <CardContent>\n <ResponsiveTable\n data={pagedCandidates}\n rowKey={(c) => c.id}\n columns={CANDIDATE_COLUMNS}\n emptyState={\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No candidates yet\"\n description=\"Candidates you add to your pipeline will appear here.\"\n />\n }\n />\n {candidates.length > pageSize && (\n <div className=\"mt-4\">\n <Pagination\n page={page}\n pageSize={pageSize}\n totalItems={candidates.length}\n onPageChange={setPage}\n />\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"HiringApplicationsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/HiringApplicationsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n Pagination,\n ResponsiveTable,\n type ResponsiveTableColumn,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { Clock, FileText, Star, UserCheck, Users, X } from 'lucide-react';\nimport { useSearchParams } from 'react-router-dom';\nimport { useCrewFoundryStore, type Candidate } from '../store/crewfoundryStore';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\n\nconst CANDIDATE_COLUMNS: ResponsiveTableColumn<Candidate>[] = [\n {\n key: 'candidate',\n header: 'Candidate',\n priority: 'primary',\n cell: (c) => (\n <div>\n <p className=\"font-medium\">\n {c.firstName} {c.lastName}\n </p>\n <p className=\"text-xs text-text-secondary\">{c.email}</p>\n </div>\n ),\n },\n {\n key: 'position',\n header: 'Position',\n priority: 'secondary',\n cell: (c) => <span className=\"text-text-secondary\">{c.position}</span>,\n },\n {\n key: 'company',\n header: 'Company',\n priority: 'secondary',\n cell: (c) => <span className=\"text-text-secondary\">{c.currentCompany}</span>,\n },\n {\n key: 'experience',\n header: 'Experience',\n priority: 'hidden-on-mobile',\n cell: (c) => <span>{c.experience} yrs</span>,\n },\n {\n key: 'rating',\n header: 'Rating',\n priority: 'hidden-on-mobile',\n cell: (c) => (\n <span className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-status-warning-text\" />\n {c.rating}\n </span>\n ),\n },\n {\n key: 'skills',\n header: 'Skills',\n priority: 'secondary',\n cell: (c) => (\n <div className=\"flex flex-wrap gap-1\">\n {c.skills.slice(0, 2).map((s) => (\n <Badge key={s} variant=\"outline\" className=\"text-[9px]\">\n {s}\n </Badge>\n ))}\n </div>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n priority: 'secondary',\n cell: (c) => (\n <Badge\n variant={\n c.status === 'hired'\n ? 'default'\n : c.status === 'offer'\n ? 'default'\n : c.status === 'rejected'\n ? 'destructive'\n : 'secondary'\n }\n className=\"text-[10px]\"\n >\n {formatEnumLabel(c.status)}\n </Badge>\n ),\n },\n];\n\nexport function HiringApplicationsPagePage() {\n const { candidates, jobPostings } = useCrewFoundryStore();\n const [searchParams, setSearchParams] = useSearchParams();\n const { page, pageSize, offset, setPage } = usePagination({ initialPageSize: 25 });\n\n // Optional role scope from the Job Detail \"View Applications\" deep-link.\n const positionFilter = searchParams.get('position');\n const positionKey = positionFilter?.trim().toLowerCase() ?? null;\n const scoped = positionKey\n ? candidates.filter((c) => c.position.trim().toLowerCase() === positionKey)\n : candidates;\n\n const clearPositionFilter = () => {\n const next = new URLSearchParams(searchParams);\n next.delete('position');\n setSearchParams(next, { replace: true });\n setPage(1);\n };\n\n const newApps = scoped.filter((c) => c.status === 'new');\n const screeningApps = scoped.filter((c) => c.status === 'screening');\n const interviewApps = scoped.filter((c) => c.status === 'interview');\n const hiredCount = scoped.filter((c) => c.status === 'hired').length;\n const totalApps = positionKey\n ? scoped.length\n : jobPostings.reduce((s, j) => s + j.applicants, 0);\n const conversionRate = totalApps > 0 ? (hiredCount / totalApps) * 100 : 0;\n const maxApplicants = Math.max(1, ...jobPostings.map((j) => j.applicants));\n const rankedPositions = [...jobPostings].sort((a, b) => b.applicants - a.applicants);\n const pagedCandidates = scoped.slice(offset, offset + pageSize);\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>Applications</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {positionFilter\n ? `${scoped.length} applications for ${positionFilter}`\n : `${totalApps} total applications across all positions`}\n </p>\n <PagePurpose className=\"mt-1\">\n Review and triage incoming job applications — move candidates through your pipeline and\n decide who to interview next.\n </PagePurpose>\n </div>\n {positionFilter && (\n <Button variant=\"outline\" size=\"sm\" onClick={clearPositionFilter}>\n <X className=\"mr-1 h-4 w-4\" />\n {positionFilter}\n </Button>\n )}\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\">New</CardTitle>\n <FileText className=\"h-4 w-4 text-status-info-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{newApps.length}</div>\n <p className=\"text-xs text-text-secondary\">Unreviewed applications</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\">Screening</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\">{screeningApps.length}</div>\n <p className=\"text-xs text-text-secondary\">Under review</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\">Interview Stage</CardTitle>\n <Users className=\"h-4 w-4 text-action-primary-bg\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{interviewApps.length}</div>\n <p className=\"text-xs text-text-secondary\">Moving forward</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\">Conversion Rate</CardTitle>\n <UserCheck className=\"h-4 w-4 text-status-success-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{conversionRate.toFixed(1)}%</div>\n <p className=\"text-xs text-text-secondary\">Hired vs. total applications</p>\n </CardContent>\n </GlassCard>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Applications by Position</CardTitle>\n <CardDescription>Applicant count per open role</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {rankedPositions.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No positions yet\"\n description=\"Create a job posting to start collecting applications.\"\n />\n ) : (\n rankedPositions.map((job) => (\n <div key={job.id} className=\"flex items-center gap-3\">\n <span className=\"w-56 text-sm font-medium truncate\">{job.title}</span>\n <div className=\"flex-1 h-4 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-4 bg-action-primary-bg rounded\"\n style={{ width: `${Math.round((job.applicants / maxApplicants) * 100)}%` }}\n />\n </div>\n <span className=\"text-sm font-medium w-10 text-right\">{job.applicants}</span>\n <Badge\n variant={job.status === 'active' ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {formatEnumLabel(job.status)}\n </Badge>\n </div>\n ))\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>{positionFilter ? `Applicants — ${positionFilter}` : 'All Candidates'}</CardTitle>\n <CardDescription>\n {positionFilter ? 'Candidates who applied to this role' : 'Complete candidate pipeline'}\n </CardDescription>\n </CardHeader>\n <CardContent>\n <ResponsiveTable\n data={pagedCandidates}\n rowKey={(c) => c.id}\n columns={CANDIDATE_COLUMNS}\n emptyState={\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No candidates yet\"\n description=\"Candidates you add to your pipeline will appear here.\"\n />\n }\n />\n {scoped.length > pageSize && (\n <div className=\"mt-4\">\n <Pagination\n page={page}\n pageSize={pageSize}\n totalItems={scoped.length}\n onPageChange={setPage}\n />\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|