@burdenoff/microfe-crewfoundry 2026.827.1 → 2026.924.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/crewfoundry/CrewFoundryRoot.d.ts.map +1 -1
  2. package/dist/crewfoundry/CrewFoundryRoot.js +190 -185
  3. package/dist/crewfoundry/CrewFoundryRoot.js.map +1 -1
  4. package/dist/crewfoundry/components/forms/ConfirmDialog.d.ts.map +1 -1
  5. package/dist/crewfoundry/components/forms/ConfirmDialog.js +25 -24
  6. package/dist/crewfoundry/components/forms/ConfirmDialog.js.map +1 -1
  7. package/dist/crewfoundry/components/forms/FormModal.d.ts.map +1 -1
  8. package/dist/crewfoundry/components/forms/FormModal.js +103 -101
  9. package/dist/crewfoundry/components/forms/FormModal.js.map +1 -1
  10. package/dist/crewfoundry/pages/DashboardDashboardPagePage.d.ts.map +1 -1
  11. package/dist/crewfoundry/pages/DashboardDashboardPagePage.js +207 -210
  12. package/dist/crewfoundry/pages/DashboardDashboardPagePage.js.map +1 -1
  13. package/dist/crewfoundry/pages/HiringCreateJobPagePage.d.ts.map +1 -1
  14. package/dist/crewfoundry/pages/HiringCreateJobPagePage.js +0 -4
  15. package/dist/crewfoundry/pages/HiringCreateJobPagePage.js.map +1 -1
  16. package/dist/crewfoundry/pages/HiringJobDetailsPagePage.d.ts.map +1 -1
  17. package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js +0 -4
  18. package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js.map +1 -1
  19. package/dist/crewfoundry/pages/HiringJobsPagePage.d.ts.map +1 -1
  20. package/dist/crewfoundry/pages/HiringJobsPagePage.js +75 -78
  21. package/dist/crewfoundry/pages/HiringJobsPagePage.js.map +1 -1
  22. package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.d.ts.map +1 -1
  23. package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js +101 -104
  24. package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js.map +1 -1
  25. package/dist/crewfoundry/pages/ProjectsProjectsPagePage.d.ts.map +1 -1
  26. package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js +95 -98
  27. package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js.map +1 -1
  28. package/dist/crewfoundry/pages/TeamsTeamsPagePage.d.ts.map +1 -1
  29. package/dist/crewfoundry/pages/TeamsTeamsPagePage.js +101 -104
  30. package/dist/crewfoundry/pages/TeamsTeamsPagePage.js.map +1 -1
  31. package/dist/shared/hooks/useTr.d.ts +17 -0
  32. package/dist/shared/hooks/useTr.d.ts.map +1 -0
  33. package/dist/shared/hooks/useTr.js +14 -0
  34. package/dist/shared/hooks/useTr.js.map +1 -0
  35. package/package.json +1 -1
@@ -1 +1 @@
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';\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 () =>\n cumulativeSeries(\n jobPostings.filter((job) => job.status === 'active').map((j) => j.postedDate)\n ),\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 {\n label: t('crewfoundry.dashboard.pipeline.applied', 'Applied'),\n value: pipelineCounts.applied,\n },\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
+ {"version":3,"file":"DashboardDashboardPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/DashboardDashboardPagePage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useTr } from '../../shared/hooks/useTr';\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';\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 tr = useTr();\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 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 () =>\n cumulativeSeries(\n jobPostings.filter((job) => job.status === 'active').map((j) => j.postedDate)\n ),\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 {\n label: tr('crewfoundry.dashboard.pipeline.applied', 'Applied'),\n value: pipelineCounts.applied,\n },\n {\n label: tr('crewfoundry.dashboard.pipeline.screening', 'Screening'),\n value: pipelineCounts.screening,\n },\n {\n label: tr('crewfoundry.dashboard.pipeline.interview', 'Interview'),\n value: pipelineCounts.interview,\n },\n { label: tr('crewfoundry.dashboard.pipeline.offer', 'Offer'), value: pipelineCounts.offer },\n { label: tr('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>{tr('crewfoundry.dashboard.title', 'Command Center')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {tr('crewfoundry.dashboard.subtitle', 'Your workforce at a glance')}\n </p>\n <PagePurpose className=\"mt-2\">\n {tr(\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 {tr('crewfoundry.dashboard.new', 'New')}\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-52\">\n <DropdownMenuLabel>{tr('crewfoundry.dashboard.newMenu', 'Create')}</DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuItem onClick={() => navigate('../hr/employees-page')}>\n <UserPlus className=\"mr-2 h-4 w-4\" />\n {tr('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 {tr('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 {tr('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: tr('crewfoundry.dashboard.next.employees', 'Review your team'),\n description: tr(\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: tr('crewfoundry.dashboard.next.hiring', 'Post a new job'),\n description: tr(\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: tr('crewfoundry.dashboard.next.projects', 'Check active projects'),\n description: tr(\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={tr('crewfoundry.dashboard.stats.totalEmployees', 'Total Employees')}\n value={employees.length}\n delta={headcountDelta || undefined}\n deltaLabel={tr('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={tr('crewfoundry.dashboard.stats.openPositions', 'Open Positions')}\n value={openPositions}\n delta={seriesDeltaPercent(openingsSeries)}\n deltaLabel={tr('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={tr('crewfoundry.dashboard.stats.activeProjects', 'Active Projects')}\n value={activeProjects}\n delta={seriesDeltaPercent(projectsSeries)}\n deltaLabel={tr('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={tr('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 {tr('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={tr('crewfoundry.dashboard.headcountGrowth.emptyTitle', 'Not enough history')}\n description={tr(\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 {tr('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={tr(\n 'crewfoundry.dashboard.upcomingInterviews.emptyTitle',\n 'No interviews scheduled'\n )}\n description={tr(\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 {tr('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 {tr('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={tr('crewfoundry.dashboard.pipeline.emptyTitle', 'No candidates yet')}\n description={tr(\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 {tr('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 {tr('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={tr('crewfoundry.dashboard.announcements.emptyTitle', 'No announcements')}\n description={tr(\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 {tr('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 ? tr('crewfoundry.dashboard.announcements.readLess', 'Read Less')\n : tr('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 {tr('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={tr('crewfoundry.dashboard.leaveRequests.emptyTitle', 'No leave requests')}\n description={tr(\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 {tr('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 {tr('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} {tr('crewfoundry.dashboard.stats.active', 'active')} · {onLeave}{' '}\n {tr('crewfoundry.dashboard.stats.onLeave', 'on leave')} · {totalApplicants}{' '}\n {tr('crewfoundry.dashboard.stats.totalApplicants', 'total applicants')} · {pendingLeaves}{' '}\n {tr('crewfoundry.dashboard.stats.pendingLeaveRequests', 'pending leave requests')}\n </p>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"HiringCreateJobPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/HiringCreateJobPagePage.tsx"],"names":[],"mappings":"AA4CA,wBAAgB,uBAAuB,gCAmUtC"}
1
+ {"version":3,"file":"HiringCreateJobPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/HiringCreateJobPagePage.tsx"],"names":[],"mappings":"AA2CA,wBAAgB,uBAAuB,gCAmUtC"}
@@ -19,10 +19,6 @@ var v = "flex h-10 w-full rounded-input border border-border-default bg-bg-surfa
19
19
  {
20
20
  label: "Contract",
21
21
  value: "contract"
22
- },
23
- {
24
- label: "Remote",
25
- value: "remote"
26
22
  }
27
23
  ], x = [
28
24
  {
@@ -1 +1 @@
1
- {"version":3,"file":"HiringCreateJobPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/HiringCreateJobPagePage.tsx"],"sourcesContent":["import { useMemo, useState, type ReactNode } from 'react';\nimport {\n Button,\n GlassCard,\n GradientText,\n Input,\n PagePurpose,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { Briefcase, ChevronRight, Plus, Save, X } from 'lucide-react';\nimport { useNavigate } from 'react-router';\nimport { useCrewFoundryStore, type JobPosting } from '../store/crewfoundryStore';\nimport { newClientId } from '../components/forms/formValues';\n\n// Native <select> styled with the same semantic tokens as fe-libs Input.\nconst SELECT_CLASS =\n 'flex h-10 w-full rounded-input border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary transition-colors duration-200 focus-visible:border-border-focus focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50';\n\nconst FIELD_LABEL_CLASS = 'text-sm font-medium text-text-primary';\n\nconst JOB_TYPE_OPTIONS: { label: string; value: JobPosting['type'] }[] = [\n { label: 'Full time', value: 'full_time' },\n { label: 'Part time', value: 'part_time' },\n { label: 'Contract', value: 'contract' },\n { label: 'Remote', value: 'remote' },\n];\n\nconst EXPERIENCE_OPTIONS: { label: string; value: JobPosting['experienceLevel'] }[] = [\n { label: 'Entry', value: 'entry' },\n { label: 'Mid', value: 'mid' },\n { label: 'Senior', value: 'senior' },\n { label: 'Lead', value: 'lead' },\n { label: 'Executive', value: 'executive' },\n];\n\nfunction FieldLabel({ htmlFor, children }: { htmlFor: string; children: ReactNode }) {\n return (\n <label className={FIELD_LABEL_CLASS} htmlFor={htmlFor}>\n {children}\n </label>\n );\n}\n\nexport function HiringCreateJobPagePage() {\n const navigate = useNavigate();\n const { showToast } = useGlobalUi();\n const addJobPosting = useCrewFoundryStore((state) => state.addJobPosting);\n const departments = useCrewFoundryStore((state) => state.departments);\n\n const [title, setTitle] = useState('');\n const [department, setDepartment] = useState('');\n const [location, setLocation] = useState('');\n const [description, setDescription] = useState('');\n const [type, setType] = useState<JobPosting['type']>('full_time');\n const [experienceLevel, setExperienceLevel] = useState<JobPosting['experienceLevel']>('mid');\n const [salaryMin, setSalaryMin] = useState('');\n const [salaryMax, setSalaryMax] = useState('');\n const [deadline, setDeadline] = useState('');\n const [requirements, setRequirements] = useState<string[]>(['']);\n const [benefits, setBenefits] = useState<string[]>(['']);\n const [submitting, setSubmitting] = useState(false);\n\n const departmentNames = useMemo(\n () => Array.from(new Set(departments.map((d) => d.name).filter(Boolean))).sort(),\n [departments]\n );\n\n const addRequirement = () => setRequirements((prev) => [...prev, '']);\n const addBenefit = () => setBenefits((prev) => [...prev, '']);\n const updateRequirement = (idx: number, value: string) =>\n setRequirements((prev) => prev.map((item, i) => (i === idx ? value : item)));\n const updateBenefit = (idx: number, value: string) =>\n setBenefits((prev) => prev.map((item, i) => (i === idx ? value : item)));\n const removeRequirement = (idx: number) =>\n setRequirements((prev) => prev.filter((_, i) => i !== idx));\n const removeBenefit = (idx: number) => setBenefits((prev) => prev.filter((_, i) => i !== idx));\n\n const submit = async (status: JobPosting['status']) => {\n if (submitting) {\n return;\n }\n if (!title.trim()) {\n showToast('Job Title is required', 'warning');\n return;\n }\n if (!department.trim()) {\n showToast('Department is required', 'warning');\n return;\n }\n const today = new Date().toISOString().slice(0, 10);\n const job: JobPosting = {\n id: newClientId(),\n title: title.trim(),\n department: department.trim(),\n location: location.trim(),\n type,\n experienceLevel,\n salaryMin: salaryMin.trim() === '' ? 0 : Number(salaryMin),\n salaryMax: salaryMax.trim() === '' ? 0 : Number(salaryMax),\n status,\n applicants: 0,\n postedDate: today,\n deadline: deadline.trim() === '' ? today : deadline,\n description: description.trim(),\n requirements: requirements.map((item) => item.trim()).filter(Boolean),\n benefits: benefits.map((item) => item.trim()).filter(Boolean),\n };\n\n setSubmitting(true);\n try {\n await addJobPosting(job);\n showToast(status === 'draft' ? 'Draft saved' : 'Job published', 'success');\n navigate('../hiring/jobs-page');\n } catch (error) {\n showToast(error instanceof Error ? error.message : 'Unable to save job posting', 'error');\n } finally {\n setSubmitting(false);\n }\n };\n\n const renderChipRows = (\n items: string[],\n update: (idx: number, value: string) => void,\n remove: (idx: number) => void,\n add: () => void,\n kind: 'Requirement' | 'Benefit',\n placeholder: string\n ) => (\n <div className=\"space-y-2\">\n {items.map((value, idx) => (\n <div key={idx} className=\"relative\">\n <Input\n className=\"pr-9\"\n placeholder={placeholder}\n value={value}\n onChange={(e) => update(idx, e.target.value)}\n />\n <button\n type=\"button\"\n aria-label={`Remove ${kind.toLowerCase()} ${idx + 1}`}\n onClick={() => remove(idx)}\n className=\"absolute right-2.5 top-1/2 -translate-y-1/2 rounded-sm p-0.5 text-text-secondary transition-colors hover:text-text-primary focus-visible:outline-none focus-visible:text-text-primary\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n ))}\n <Button variant=\"link\" size=\"sm\" className=\"h-auto p-0\" onClick={add} type=\"button\">\n <Plus className=\"h-4 w-4 mr-1\" />\n Add {kind}\n </Button>\n </div>\n );\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"space-y-2\">\n <nav\n aria-label=\"Breadcrumb\"\n className=\"flex items-center gap-1 text-sm text-text-secondary\"\n >\n <button\n type=\"button\"\n onClick={() => navigate('../hiring/jobs-page')}\n className=\"rounded-sm transition-colors hover:text-text-primary focus-visible:outline-none focus-visible:text-text-primary\"\n >\n Hiring\n </button>\n <ChevronRight className=\"h-3.5 w-3.5\" />\n <span className=\"text-text-primary\">Post New Job</span>\n </nav>\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Create Job Posting</GradientText>\n </h1>\n <PagePurpose className=\"mt-1\">\n Create a new job posting — define the role, requirements, and compensation here to start\n attracting and collecting applicants.\n </PagePurpose>\n </div>\n </div>\n\n <GlassCard treatment=\"glass\" className=\"max-w-5xl p-6\">\n <div className=\"mb-6 flex items-center gap-2 text-lg font-semibold text-text-primary\">\n <Briefcase className=\"h-5 w-5\" />\n Job details\n </div>\n\n <div className=\"space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <FieldLabel htmlFor=\"job-title\">Job Title</FieldLabel>\n <Input\n id=\"job-title\"\n placeholder=\"e.g. Senior Frontend Developer\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <FieldLabel htmlFor=\"job-type\">Employment Type</FieldLabel>\n <select\n id=\"job-type\"\n className={SELECT_CLASS}\n value={type}\n onChange={(e) => setType(e.target.value as JobPosting['type'])}\n >\n {JOB_TYPE_OPTIONS.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-5 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <FieldLabel htmlFor=\"job-department\">Department</FieldLabel>\n <Input\n id=\"job-department\"\n list=\"crewfoundry-department-options\"\n placeholder=\"e.g. Engineering\"\n value={department}\n onChange={(e) => setDepartment(e.target.value)}\n />\n <datalist id=\"crewfoundry-department-options\">\n {departmentNames.map((name) => (\n <option key={name} value={name} />\n ))}\n </datalist>\n </div>\n <div className=\"space-y-2\">\n <FieldLabel htmlFor=\"job-experience\">Experience Level</FieldLabel>\n <select\n id=\"job-experience\"\n className={SELECT_CLASS}\n value={experienceLevel}\n onChange={(e) =>\n setExperienceLevel(e.target.value as JobPosting['experienceLevel'])\n }\n >\n {EXPERIENCE_OPTIONS.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-5 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <FieldLabel htmlFor=\"job-location\">Location</FieldLabel>\n <Input\n id=\"job-location\"\n placeholder=\"e.g. San Francisco, CA\"\n value={location}\n onChange={(e) => setLocation(e.target.value)}\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-2\">\n <FieldLabel htmlFor=\"job-salary-min\">Salary Min</FieldLabel>\n <div className=\"relative\">\n <span className=\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-sm text-text-secondary\">\n $\n </span>\n <Input\n id=\"job-salary-min\"\n type=\"number\"\n className=\"pl-7\"\n placeholder=\"120,000\"\n value={salaryMin}\n onChange={(e) => setSalaryMin(e.target.value)}\n />\n </div>\n </div>\n <div className=\"space-y-2\">\n <FieldLabel htmlFor=\"job-salary-max\">Salary Max</FieldLabel>\n <div className=\"relative\">\n <span className=\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-sm text-text-secondary\">\n $\n </span>\n <Input\n id=\"job-salary-max\"\n type=\"number\"\n className=\"pl-7\"\n placeholder=\"180,000\"\n value={salaryMax}\n onChange={(e) => setSalaryMax(e.target.value)}\n />\n </div>\n </div>\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-5 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <FieldLabel htmlFor=\"job-deadline\">Application Deadline</FieldLabel>\n <Input\n id=\"job-deadline\"\n type=\"date\"\n value={deadline}\n onChange={(e) => setDeadline(e.target.value)}\n />\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <FieldLabel htmlFor=\"job-description\">Description</FieldLabel>\n <Textarea\n id=\"job-description\"\n placeholder=\"Describe the role, responsibilities, and ideal candidate...\"\n rows={4}\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-6 border-t border-border-subtle pt-5 md:grid-cols-2\">\n <div className=\"space-y-3\">\n <div>\n <div className={FIELD_LABEL_CLASS}>Requirements</div>\n <p className=\"text-xs text-text-secondary\">\n List the key requirements for this role\n </p>\n </div>\n {renderChipRows(\n requirements,\n updateRequirement,\n removeRequirement,\n addRequirement,\n 'Requirement',\n 'e.g. 5+ years with React'\n )}\n </div>\n <div className=\"space-y-3\">\n <div>\n <div className={FIELD_LABEL_CLASS}>Benefits</div>\n <p className=\"text-xs text-text-secondary\">What does this position offer?</p>\n </div>\n {renderChipRows(\n benefits,\n updateBenefit,\n removeBenefit,\n addBenefit,\n 'Benefit',\n 'e.g. Flexible remote work'\n )}\n </div>\n </div>\n\n <div className=\"flex flex-col-reverse justify-end gap-2 border-t border-border-subtle pt-5 sm:flex-row\">\n <Button variant=\"outline\" disabled={submitting} onClick={() => submit('draft')}>\n Save as Draft\n </Button>\n <Button disabled={submitting} onClick={() => submit('active')}>\n <Save className=\"h-4 w-4 mr-1\" />\n {submitting ? 'Saving…' : 'Publish Job'}\n </Button>\n </div>\n </div>\n </GlassCard>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"HiringCreateJobPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/HiringCreateJobPagePage.tsx"],"sourcesContent":["import { useMemo, useState, type ReactNode } from 'react';\nimport {\n Button,\n GlassCard,\n GradientText,\n Input,\n PagePurpose,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { Briefcase, ChevronRight, Plus, Save, X } from 'lucide-react';\nimport { useNavigate } from 'react-router';\nimport { useCrewFoundryStore, type JobPosting } from '../store/crewfoundryStore';\nimport { newClientId } from '../components/forms/formValues';\n\n// Native <select> styled with the same semantic tokens as fe-libs Input.\nconst SELECT_CLASS =\n 'flex h-10 w-full rounded-input border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary transition-colors duration-200 focus-visible:border-border-focus focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50';\n\nconst FIELD_LABEL_CLASS = 'text-sm font-medium text-text-primary';\n\nconst JOB_TYPE_OPTIONS: { label: string; value: JobPosting['type'] }[] = [\n { label: 'Full time', value: 'full_time' },\n { label: 'Part time', value: 'part_time' },\n { label: 'Contract', value: 'contract' },\n];\n\nconst EXPERIENCE_OPTIONS: { label: string; value: JobPosting['experienceLevel'] }[] = [\n { label: 'Entry', value: 'entry' },\n { label: 'Mid', value: 'mid' },\n { label: 'Senior', value: 'senior' },\n { label: 'Lead', value: 'lead' },\n { label: 'Executive', value: 'executive' },\n];\n\nfunction FieldLabel({ htmlFor, children }: { htmlFor: string; children: ReactNode }) {\n return (\n <label className={FIELD_LABEL_CLASS} htmlFor={htmlFor}>\n {children}\n </label>\n );\n}\n\nexport function HiringCreateJobPagePage() {\n const navigate = useNavigate();\n const { showToast } = useGlobalUi();\n const addJobPosting = useCrewFoundryStore((state) => state.addJobPosting);\n const departments = useCrewFoundryStore((state) => state.departments);\n\n const [title, setTitle] = useState('');\n const [department, setDepartment] = useState('');\n const [location, setLocation] = useState('');\n const [description, setDescription] = useState('');\n const [type, setType] = useState<JobPosting['type']>('full_time');\n const [experienceLevel, setExperienceLevel] = useState<JobPosting['experienceLevel']>('mid');\n const [salaryMin, setSalaryMin] = useState('');\n const [salaryMax, setSalaryMax] = useState('');\n const [deadline, setDeadline] = useState('');\n const [requirements, setRequirements] = useState<string[]>(['']);\n const [benefits, setBenefits] = useState<string[]>(['']);\n const [submitting, setSubmitting] = useState(false);\n\n const departmentNames = useMemo(\n () => Array.from(new Set(departments.map((d) => d.name).filter(Boolean))).sort(),\n [departments]\n );\n\n const addRequirement = () => setRequirements((prev) => [...prev, '']);\n const addBenefit = () => setBenefits((prev) => [...prev, '']);\n const updateRequirement = (idx: number, value: string) =>\n setRequirements((prev) => prev.map((item, i) => (i === idx ? value : item)));\n const updateBenefit = (idx: number, value: string) =>\n setBenefits((prev) => prev.map((item, i) => (i === idx ? value : item)));\n const removeRequirement = (idx: number) =>\n setRequirements((prev) => prev.filter((_, i) => i !== idx));\n const removeBenefit = (idx: number) => setBenefits((prev) => prev.filter((_, i) => i !== idx));\n\n const submit = async (status: JobPosting['status']) => {\n if (submitting) {\n return;\n }\n if (!title.trim()) {\n showToast('Job Title is required', 'warning');\n return;\n }\n if (!department.trim()) {\n showToast('Department is required', 'warning');\n return;\n }\n const today = new Date().toISOString().slice(0, 10);\n const job: JobPosting = {\n id: newClientId(),\n title: title.trim(),\n department: department.trim(),\n location: location.trim(),\n type,\n experienceLevel,\n salaryMin: salaryMin.trim() === '' ? 0 : Number(salaryMin),\n salaryMax: salaryMax.trim() === '' ? 0 : Number(salaryMax),\n status,\n applicants: 0,\n postedDate: today,\n deadline: deadline.trim() === '' ? today : deadline,\n description: description.trim(),\n requirements: requirements.map((item) => item.trim()).filter(Boolean),\n benefits: benefits.map((item) => item.trim()).filter(Boolean),\n };\n\n setSubmitting(true);\n try {\n await addJobPosting(job);\n showToast(status === 'draft' ? 'Draft saved' : 'Job published', 'success');\n navigate('../hiring/jobs-page');\n } catch (error) {\n showToast(error instanceof Error ? error.message : 'Unable to save job posting', 'error');\n } finally {\n setSubmitting(false);\n }\n };\n\n const renderChipRows = (\n items: string[],\n update: (idx: number, value: string) => void,\n remove: (idx: number) => void,\n add: () => void,\n kind: 'Requirement' | 'Benefit',\n placeholder: string\n ) => (\n <div className=\"space-y-2\">\n {items.map((value, idx) => (\n <div key={idx} className=\"relative\">\n <Input\n className=\"pr-9\"\n placeholder={placeholder}\n value={value}\n onChange={(e) => update(idx, e.target.value)}\n />\n <button\n type=\"button\"\n aria-label={`Remove ${kind.toLowerCase()} ${idx + 1}`}\n onClick={() => remove(idx)}\n className=\"absolute right-2.5 top-1/2 -translate-y-1/2 rounded-sm p-0.5 text-text-secondary transition-colors hover:text-text-primary focus-visible:outline-none focus-visible:text-text-primary\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n ))}\n <Button variant=\"link\" size=\"sm\" className=\"h-auto p-0\" onClick={add} type=\"button\">\n <Plus className=\"h-4 w-4 mr-1\" />\n Add {kind}\n </Button>\n </div>\n );\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"space-y-2\">\n <nav\n aria-label=\"Breadcrumb\"\n className=\"flex items-center gap-1 text-sm text-text-secondary\"\n >\n <button\n type=\"button\"\n onClick={() => navigate('../hiring/jobs-page')}\n className=\"rounded-sm transition-colors hover:text-text-primary focus-visible:outline-none focus-visible:text-text-primary\"\n >\n Hiring\n </button>\n <ChevronRight className=\"h-3.5 w-3.5\" />\n <span className=\"text-text-primary\">Post New Job</span>\n </nav>\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Create Job Posting</GradientText>\n </h1>\n <PagePurpose className=\"mt-1\">\n Create a new job posting — define the role, requirements, and compensation here to start\n attracting and collecting applicants.\n </PagePurpose>\n </div>\n </div>\n\n <GlassCard treatment=\"glass\" className=\"max-w-5xl p-6\">\n <div className=\"mb-6 flex items-center gap-2 text-lg font-semibold text-text-primary\">\n <Briefcase className=\"h-5 w-5\" />\n Job details\n </div>\n\n <div className=\"space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <FieldLabel htmlFor=\"job-title\">Job Title</FieldLabel>\n <Input\n id=\"job-title\"\n placeholder=\"e.g. Senior Frontend Developer\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <FieldLabel htmlFor=\"job-type\">Employment Type</FieldLabel>\n <select\n id=\"job-type\"\n className={SELECT_CLASS}\n value={type}\n onChange={(e) => setType(e.target.value as JobPosting['type'])}\n >\n {JOB_TYPE_OPTIONS.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-5 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <FieldLabel htmlFor=\"job-department\">Department</FieldLabel>\n <Input\n id=\"job-department\"\n list=\"crewfoundry-department-options\"\n placeholder=\"e.g. Engineering\"\n value={department}\n onChange={(e) => setDepartment(e.target.value)}\n />\n <datalist id=\"crewfoundry-department-options\">\n {departmentNames.map((name) => (\n <option key={name} value={name} />\n ))}\n </datalist>\n </div>\n <div className=\"space-y-2\">\n <FieldLabel htmlFor=\"job-experience\">Experience Level</FieldLabel>\n <select\n id=\"job-experience\"\n className={SELECT_CLASS}\n value={experienceLevel}\n onChange={(e) =>\n setExperienceLevel(e.target.value as JobPosting['experienceLevel'])\n }\n >\n {EXPERIENCE_OPTIONS.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-5 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <FieldLabel htmlFor=\"job-location\">Location</FieldLabel>\n <Input\n id=\"job-location\"\n placeholder=\"e.g. San Francisco, CA\"\n value={location}\n onChange={(e) => setLocation(e.target.value)}\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-2\">\n <FieldLabel htmlFor=\"job-salary-min\">Salary Min</FieldLabel>\n <div className=\"relative\">\n <span className=\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-sm text-text-secondary\">\n $\n </span>\n <Input\n id=\"job-salary-min\"\n type=\"number\"\n className=\"pl-7\"\n placeholder=\"120,000\"\n value={salaryMin}\n onChange={(e) => setSalaryMin(e.target.value)}\n />\n </div>\n </div>\n <div className=\"space-y-2\">\n <FieldLabel htmlFor=\"job-salary-max\">Salary Max</FieldLabel>\n <div className=\"relative\">\n <span className=\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-sm text-text-secondary\">\n $\n </span>\n <Input\n id=\"job-salary-max\"\n type=\"number\"\n className=\"pl-7\"\n placeholder=\"180,000\"\n value={salaryMax}\n onChange={(e) => setSalaryMax(e.target.value)}\n />\n </div>\n </div>\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-5 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <FieldLabel htmlFor=\"job-deadline\">Application Deadline</FieldLabel>\n <Input\n id=\"job-deadline\"\n type=\"date\"\n value={deadline}\n onChange={(e) => setDeadline(e.target.value)}\n />\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <FieldLabel htmlFor=\"job-description\">Description</FieldLabel>\n <Textarea\n id=\"job-description\"\n placeholder=\"Describe the role, responsibilities, and ideal candidate...\"\n rows={4}\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-6 border-t border-border-subtle pt-5 md:grid-cols-2\">\n <div className=\"space-y-3\">\n <div>\n <div className={FIELD_LABEL_CLASS}>Requirements</div>\n <p className=\"text-xs text-text-secondary\">\n List the key requirements for this role\n </p>\n </div>\n {renderChipRows(\n requirements,\n updateRequirement,\n removeRequirement,\n addRequirement,\n 'Requirement',\n 'e.g. 5+ years with React'\n )}\n </div>\n <div className=\"space-y-3\">\n <div>\n <div className={FIELD_LABEL_CLASS}>Benefits</div>\n <p className=\"text-xs text-text-secondary\">What does this position offer?</p>\n </div>\n {renderChipRows(\n benefits,\n updateBenefit,\n removeBenefit,\n addBenefit,\n 'Benefit',\n 'e.g. Flexible remote work'\n )}\n </div>\n </div>\n\n <div className=\"flex flex-col-reverse justify-end gap-2 border-t border-border-subtle pt-5 sm:flex-row\">\n <Button variant=\"outline\" disabled={submitting} onClick={() => submit('draft')}>\n Save as Draft\n </Button>\n <Button disabled={submitting} onClick={() => submit('active')}>\n <Save className=\"h-4 w-4 mr-1\" />\n {submitting ? 'Saving…' : 'Publish Job'}\n </Button>\n </div>\n </div>\n </GlassCard>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"HiringJobDetailsPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/HiringJobDetailsPagePage.tsx"],"names":[],"mappings":"AAkHA,wBAAgB,wBAAwB,gCAsbvC"}
1
+ {"version":3,"file":"HiringJobDetailsPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/HiringJobDetailsPagePage.tsx"],"names":[],"mappings":"AAiHA,wBAAgB,wBAAwB,gCAsbvC"}
@@ -52,10 +52,6 @@ var j = [
52
52
  {
53
53
  label: "Contract",
54
54
  value: "contract"
55
- },
56
- {
57
- label: "Remote",
58
- value: "remote"
59
55
  }
60
56
  ]
61
57
  },
@@ -1 +1 @@
1
- {"version":3,"file":"HiringJobDetailsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/HiringJobDetailsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n Spinner,\n} from '@burdenoff/fe-libs/ui';\nimport { useState } from 'react';\nimport {\n Briefcase,\n Calendar,\n CheckCircle,\n MapPin,\n Pencil,\n Share2,\n Sparkles,\n Trash2,\n Users,\n XCircle,\n} from 'lucide-react';\nimport { useNavigate, useSearchParams } from 'react-router';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useCrewFoundryStore, type Candidate, type JobPosting } from '../store/crewfoundryStore';\nimport { FormModal, type FieldConfig, type FormValues } from '../components/forms/FormModal';\nimport { ConfirmDialog } from '../components/forms/ConfirmDialog';\nimport { getNumber, getTags, getText } from '../components/forms/formValues';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\nimport {\n HBarFunnel,\n RatingStars,\n SkillBar,\n STATUS,\n getInitials,\n type LabeledValue,\n} from '../components/viz';\n\nconst JOB_FIELDS: FieldConfig[] = [\n { name: 'title', label: 'Job Title', type: 'text', required: true, colSpan: 2 },\n { name: 'department', label: 'Department', type: 'text', required: true },\n { name: 'location', label: 'Location', type: 'text', required: true },\n {\n name: 'type',\n label: 'Employment Type',\n type: 'select',\n options: [\n { label: 'Full time', value: 'full_time' },\n { label: 'Part time', value: 'part_time' },\n { label: 'Contract', value: 'contract' },\n { label: 'Remote', value: 'remote' },\n ],\n },\n {\n name: 'experienceLevel',\n label: 'Experience Level',\n type: 'select',\n options: [\n { label: 'Entry', value: 'entry' },\n { label: 'Mid', value: 'mid' },\n { label: 'Senior', value: 'senior' },\n { label: 'Lead', value: 'lead' },\n { label: 'Executive', value: 'executive' },\n ],\n },\n {\n name: 'status',\n label: 'Status',\n type: 'select',\n options: [\n { label: 'Draft', value: 'draft' },\n { label: 'Active', value: 'active' },\n { label: 'Paused', value: 'paused' },\n { label: 'Closed', value: 'closed' },\n { label: 'Filled', value: 'filled' },\n ],\n },\n { name: 'salaryMin', label: 'Salary Min ($)', type: 'number' },\n { name: 'salaryMax', label: 'Salary Max ($)', type: 'number' },\n { name: 'deadline', label: 'Application Deadline', type: 'date' },\n { name: 'description', label: 'Description', type: 'textarea', colSpan: 2 },\n { name: 'requirements', label: 'Requirements', type: 'tags' },\n { name: 'benefits', label: 'Benefits', type: 'tags' },\n];\n\nconst STAGE_BADGE: Record<\n Candidate['status'],\n 'default' | 'secondary' | 'destructive' | 'outline'\n> = {\n new: 'outline',\n screening: 'secondary',\n interview: 'secondary',\n assessment: 'secondary',\n offer: 'default',\n hired: 'default',\n rejected: 'destructive',\n};\n\nfunction InitialsAvatar({ name, size = 32 }: { name: string; size?: number }) {\n return (\n <span\n className=\"inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full border border-border-subtle bg-bg-sunken font-medium text-text-secondary\"\n style={{ width: size, height: size, fontSize: Math.round(size * 0.36) }}\n aria-hidden=\"true\"\n >\n {getInitials(name)}\n </span>\n );\n}\n\nexport function HiringJobDetailsPagePage() {\n const navigate = useNavigate();\n const [searchParams] = useSearchParams();\n const { showToast } = useGlobalUi();\n const {\n jobPostings,\n candidates,\n updateJobPosting,\n removeJobPosting,\n initialized,\n jobPostingMatches,\n jobPostingMatchesLoading,\n fetchJobPostingMatches,\n } = useCrewFoundryStore();\n const jobId = searchParams.get('id');\n const job = jobId ? jobPostings.find((j) => j.id === jobId) : jobPostings[0];\n const [editOpen, setEditOpen] = useState(false);\n const [deleteOpen, setDeleteOpen] = useState(false);\n\n if (!initialized && !job) {\n return (\n <div className=\"flex min-h-[60vh] items-center justify-center p-6\">\n <Spinner size=\"lg\" />\n </div>\n );\n }\n\n if (!job) {\n return (\n <div className=\"p-6\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No job posting found\"\n description=\"This job posting is no longer available.\"\n />\n </div>\n );\n }\n\n // Applicants for this role. CrewCandidate has no jobId link, so match on the\n // (normalized) position title — the only real linkage the schema exposes.\n const titleKey = job.title.trim().toLowerCase();\n const jobCandidates = candidates.filter((c) => c.position.trim().toLowerCase() === titleKey);\n const countBy = (status: Candidate['status']) =>\n jobCandidates.filter((c) => c.status === status).length;\n\n const interviewCount = countBy('interview');\n\n // Real-data hiring funnel: \"Applied\" = everyone who applied to this role, then\n // the count currently at each downstream stage (offer folds in hired).\n const funnelStages: LabeledValue[] = [\n { label: 'Applied', value: jobCandidates.length },\n { label: 'Screening', value: countBy('screening') },\n { label: 'Interview', value: interviewCount },\n { label: 'Assessment', value: countBy('assessment') },\n { label: 'Offer', value: countBy('offer') + countBy('hired') },\n ];\n\n const editInitial: FormValues = {\n title: job.title,\n department: job.department,\n location: job.location,\n type: job.type,\n experienceLevel: job.experienceLevel,\n status: job.status,\n salaryMin: job.salaryMin,\n salaryMax: job.salaryMax,\n deadline: job.deadline,\n description: job.description,\n requirements: job.requirements,\n benefits: job.benefits,\n };\n\n const handleEditSubmit = async (values: FormValues) => {\n const updates: Partial<JobPosting> = {\n title: getText(values, 'title'),\n department: getText(values, 'department'),\n location: getText(values, 'location'),\n type: values.type as JobPosting['type'],\n experienceLevel: values.experienceLevel as JobPosting['experienceLevel'],\n status: values.status as JobPosting['status'],\n salaryMin: getNumber(values, 'salaryMin'),\n salaryMax: getNumber(values, 'salaryMax'),\n deadline: getText(values, 'deadline'),\n description: getText(values, 'description'),\n requirements: getTags(values, 'requirements'),\n benefits: getTags(values, 'benefits'),\n };\n await updateJobPosting(job.id, updates);\n showToast('Job posting updated', 'success');\n };\n\n const handleDelete = async () => {\n await removeJobPosting(job.id);\n showToast('Job posting deleted', 'success');\n navigate('../hiring/jobs-page');\n };\n\n const handleShare = async () => {\n try {\n await navigator.clipboard.writeText(window.location.href);\n showToast('Job link copied to clipboard', 'success');\n } catch {\n showToast('Could not copy the job link', 'error');\n }\n };\n\n const viewApplications = () =>\n navigate(`../hiring/applications-page?position=${encodeURIComponent(job.title)}`);\n\n // BOFF-4195 — AI-assisted candidate matching (deterministic skill-overlap +\n // experience-fit scoring; see wspace-crewfoundry-svc matching.service.ts).\n const matches = jobPostingMatches[job.id];\n const matchesLoaded = matches !== undefined;\n const scoreTone = (score: number): string =>\n score >= 75 ? STATUS.success : score >= 45 ? STATUS.warning : STATUS.danger;\n const handleGenerateMatches = async () => {\n try {\n await fetchJobPostingMatches(job.id);\n } catch {\n showToast('Unable to generate candidate matches', 'error');\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardContent className=\"flex flex-col gap-4 p-6 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"min-w-0\">\n <div className=\"mb-1 flex flex-wrap items-center gap-2\">\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>{job.title}</GradientText>\n </h1>\n <Badge variant={job.status === 'active' ? 'default' : 'secondary'}>\n {formatEnumLabel(job.status)}\n </Badge>\n </div>\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <Briefcase className=\"h-3.5 w-3.5\" />\n {job.department}\n </span>\n <span className=\"flex items-center gap-1\">\n <MapPin className=\"h-3.5 w-3.5\" />\n {job.location}\n </span>\n <span>{formatEnumLabel(job.type)}</span>\n </div>\n <div className=\"mt-2 flex flex-wrap items-center gap-2 text-xs text-text-secondary\">\n <Badge variant=\"secondary\" className=\"text-[10px]\">\n {jobCandidates.length} applicants\n </Badge>\n {interviewCount > 0 && (\n <Badge variant=\"secondary\" className=\"text-[10px]\">\n {interviewCount} in interview\n </Badge>\n )}\n <span className=\"flex items-center gap-1\">\n <Calendar className=\"h-3 w-3\" />\n Posted {job.postedDate}\n </span>\n </div>\n <PagePurpose className=\"mt-3\">\n Everything about this role in one place — review applicants, requirements, and\n compensation to decide who to interview and keep the posting current.\n </PagePurpose>\n </div>\n\n <div className=\"flex shrink-0 flex-col items-start gap-3 lg:items-end\">\n <div className=\"text-2xl font-bold tabular-nums text-text-primary\">\n ${(job.salaryMin / 1000).toFixed(0)}k – ${(job.salaryMax / 1000).toFixed(0)}k\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={handleShare}>\n <Share2 className=\"mr-1 h-4 w-4\" />\n Share\n </Button>\n <Button variant=\"outline\" size=\"sm\" onClick={() => setEditOpen(true)}>\n <Pencil className=\"mr-1 h-4 w-4\" />\n Edit\n </Button>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n aria-label=\"Delete job posting\"\n onClick={() => setDeleteOpen(true)}\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n <Button size=\"sm\" onClick={viewApplications}>\n <Users className=\"mr-1 h-4 w-4\" />\n View Applications\n </Button>\n </div>\n </div>\n </CardContent>\n </GlassCard>\n\n {/* Two-column body */}\n <div className=\"grid gap-6 lg:grid-cols-3\">\n {/* Left: description / requirements / benefits */}\n <div className=\"space-y-6 lg:col-span-2\">\n <Card>\n <CardHeader>\n <CardTitle>Description</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm leading-relaxed text-text-secondary\">{job.description}</p>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Requirements</CardTitle>\n </CardHeader>\n <CardContent>\n {job.requirements.length === 0 ? (\n <p className=\"text-sm text-text-muted\">No requirements listed.</p>\n ) : (\n <ul className=\"space-y-2\">\n {job.requirements.map((req, idx) => (\n <li key={idx} className=\"flex items-start gap-2 text-sm\">\n <CheckCircle className=\"mt-0.5 h-4 w-4 flex-shrink-0 text-status-success-text\" />\n {req}\n </li>\n ))}\n </ul>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Benefits</CardTitle>\n </CardHeader>\n <CardContent>\n {job.benefits.length === 0 ? (\n <p className=\"text-sm text-text-muted\">No benefits listed.</p>\n ) : (\n <div className=\"flex flex-wrap gap-2\">\n {job.benefits.map((ben, idx) => (\n <Badge key={idx} variant=\"outline\" className=\"rounded-full px-3 py-1 text-xs\">\n {ben}\n </Badge>\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* BOFF-4195 — AI-assisted candidate matching (deterministic, explainable) */}\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between gap-3\">\n <div>\n <CardTitle>Suggested Matches</CardTitle>\n <p className=\"mt-1 text-xs text-text-muted\">\n Rule-based scoring — skill overlap (70%) + experience fit (30%) against this\n role&apos;s requirements. Not an AI-generated summary.\n </p>\n </div>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={handleGenerateMatches}\n disabled={jobPostingMatchesLoading}\n >\n <Sparkles className=\"mr-1 h-4 w-4\" />\n {matchesLoaded ? 'Refresh matches' : 'Generate matches'}\n </Button>\n </CardHeader>\n <CardContent>\n {jobPostingMatchesLoading ? (\n <div className=\"flex items-center justify-center py-6\">\n <Spinner size=\"md\" />\n </div>\n ) : !matchesLoaded ? (\n <p className=\"text-sm text-text-muted\">\n Click &quot;Generate matches&quot; to rank the candidate pool against this\n posting&apos;s requirements and experience level.\n </p>\n ) : matches.length === 0 ? (\n <p className=\"text-sm text-text-muted\">\n No candidates in the pool right now (excluding already-hired/rejected).\n </p>\n ) : (\n <ul className=\"space-y-4\">\n {matches.map((match) => (\n <li\n key={match.candidateId}\n className=\"rounded-[var(--radius-card)] border border-border-subtle bg-bg-surface p-3\"\n >\n <div className=\"flex items-center gap-2\">\n <InitialsAvatar\n name={`${match.candidate.firstName} ${match.candidate.lastName}`}\n size={28}\n />\n <span className=\"min-w-0 flex-1 truncate text-sm font-medium text-text-primary\">\n {match.candidate.firstName} {match.candidate.lastName}\n </span>\n <Badge\n variant={STAGE_BADGE[match.candidate.status]}\n className=\"shrink-0 text-[9px]\"\n >\n {formatEnumLabel(match.candidate.status)}\n </Badge>\n </div>\n\n <div className=\"mt-2\">\n <SkillBar\n label=\"Overall match\"\n value={match.score}\n levelLabel={`${match.score}%`}\n color={scoreTone(match.score)}\n height={6}\n />\n </div>\n <div className=\"mt-2 grid grid-cols-2 gap-2 text-xs text-text-secondary\">\n <span>Skills: {match.skillScore}%</span>\n <span>Experience: {match.experienceScore}%</span>\n </div>\n\n {(match.matchedSkills.length > 0 || match.missingSkills.length > 0) && (\n <div className=\"mt-2 flex flex-wrap gap-1\">\n {match.matchedSkills.map((skill) => (\n <Badge\n key={`m-${skill}`}\n variant=\"secondary\"\n className=\"gap-1 text-[10px]\"\n >\n <CheckCircle className=\"h-3 w-3 text-status-success-text\" />\n {skill}\n </Badge>\n ))}\n {match.missingSkills.map((skill) => (\n <Badge\n key={`x-${skill}`}\n variant=\"outline\"\n className=\"gap-1 text-[10px] text-text-muted\"\n >\n <XCircle className=\"h-3 w-3 text-status-error-text\" />\n {skill}\n </Badge>\n ))}\n </div>\n )}\n\n {/* Rationale is always visible — the ranking is never a black box. */}\n <p className=\"mt-2 text-xs leading-relaxed text-text-muted\">\n {match.rationale}\n </p>\n </li>\n ))}\n </ul>\n )}\n </CardContent>\n </Card>\n </div>\n\n {/* Right rail: candidates + funnel */}\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Candidates</CardTitle>\n </CardHeader>\n <CardContent>\n {jobCandidates.length === 0 ? (\n <p className=\"text-sm text-text-muted\">No applicants matched to this role yet.</p>\n ) : (\n <ul className=\"max-h-96 space-y-1 overflow-y-auto pr-1\">\n {jobCandidates.map((candidate) => (\n <li\n key={candidate.id}\n className=\"flex items-center gap-2 rounded-[var(--radius-input)] px-2 py-2 hover:bg-bg-sunken\"\n >\n <InitialsAvatar\n name={`${candidate.firstName} ${candidate.lastName}`}\n size={30}\n />\n <span className=\"min-w-0 flex-1 truncate text-sm font-medium text-text-primary\">\n {candidate.firstName} {candidate.lastName}\n </span>\n <Badge\n variant={STAGE_BADGE[candidate.status]}\n className=\"shrink-0 text-[9px]\"\n >\n {formatEnumLabel(candidate.status)}\n </Badge>\n <RatingStars value={candidate.rating} size={12} className=\"shrink-0\" />\n </li>\n ))}\n </ul>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Hiring funnel</CardTitle>\n </CardHeader>\n <CardContent>\n {jobCandidates.length === 0 ? (\n <p className=\"text-sm text-text-muted\">\n The funnel appears once candidates apply to this role.\n </p>\n ) : (\n <HBarFunnel\n stages={funnelStages}\n valueFormatter={(value) => String(value)}\n showConversion\n />\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n\n <FormModal\n open={editOpen}\n onOpenChange={setEditOpen}\n title=\"Edit Job Posting\"\n description=\"Update the role details, compensation, and status.\"\n fields={JOB_FIELDS}\n initialValues={editInitial}\n submitLabel=\"Save changes\"\n onSubmit={handleEditSubmit}\n />\n <ConfirmDialog\n open={deleteOpen}\n onOpenChange={setDeleteOpen}\n title=\"Delete job posting\"\n description={`Permanently remove “${job.title}”? This cannot be undone.`}\n confirmLabel=\"Delete\"\n destructive\n onConfirm={handleDelete}\n />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"HiringJobDetailsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/HiringJobDetailsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n Spinner,\n} from '@burdenoff/fe-libs/ui';\nimport { useState } from 'react';\nimport {\n Briefcase,\n Calendar,\n CheckCircle,\n MapPin,\n Pencil,\n Share2,\n Sparkles,\n Trash2,\n Users,\n XCircle,\n} from 'lucide-react';\nimport { useNavigate, useSearchParams } from 'react-router';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useCrewFoundryStore, type Candidate, type JobPosting } from '../store/crewfoundryStore';\nimport { FormModal, type FieldConfig, type FormValues } from '../components/forms/FormModal';\nimport { ConfirmDialog } from '../components/forms/ConfirmDialog';\nimport { getNumber, getTags, getText } from '../components/forms/formValues';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\nimport {\n HBarFunnel,\n RatingStars,\n SkillBar,\n STATUS,\n getInitials,\n type LabeledValue,\n} from '../components/viz';\n\nconst JOB_FIELDS: FieldConfig[] = [\n { name: 'title', label: 'Job Title', type: 'text', required: true, colSpan: 2 },\n { name: 'department', label: 'Department', type: 'text', required: true },\n { name: 'location', label: 'Location', type: 'text', required: true },\n {\n name: 'type',\n label: 'Employment Type',\n type: 'select',\n options: [\n { label: 'Full time', value: 'full_time' },\n { label: 'Part time', value: 'part_time' },\n { label: 'Contract', value: 'contract' },\n ],\n },\n {\n name: 'experienceLevel',\n label: 'Experience Level',\n type: 'select',\n options: [\n { label: 'Entry', value: 'entry' },\n { label: 'Mid', value: 'mid' },\n { label: 'Senior', value: 'senior' },\n { label: 'Lead', value: 'lead' },\n { label: 'Executive', value: 'executive' },\n ],\n },\n {\n name: 'status',\n label: 'Status',\n type: 'select',\n options: [\n { label: 'Draft', value: 'draft' },\n { label: 'Active', value: 'active' },\n { label: 'Paused', value: 'paused' },\n { label: 'Closed', value: 'closed' },\n { label: 'Filled', value: 'filled' },\n ],\n },\n { name: 'salaryMin', label: 'Salary Min ($)', type: 'number' },\n { name: 'salaryMax', label: 'Salary Max ($)', type: 'number' },\n { name: 'deadline', label: 'Application Deadline', type: 'date' },\n { name: 'description', label: 'Description', type: 'textarea', colSpan: 2 },\n { name: 'requirements', label: 'Requirements', type: 'tags' },\n { name: 'benefits', label: 'Benefits', type: 'tags' },\n];\n\nconst STAGE_BADGE: Record<\n Candidate['status'],\n 'default' | 'secondary' | 'destructive' | 'outline'\n> = {\n new: 'outline',\n screening: 'secondary',\n interview: 'secondary',\n assessment: 'secondary',\n offer: 'default',\n hired: 'default',\n rejected: 'destructive',\n};\n\nfunction InitialsAvatar({ name, size = 32 }: { name: string; size?: number }) {\n return (\n <span\n className=\"inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full border border-border-subtle bg-bg-sunken font-medium text-text-secondary\"\n style={{ width: size, height: size, fontSize: Math.round(size * 0.36) }}\n aria-hidden=\"true\"\n >\n {getInitials(name)}\n </span>\n );\n}\n\nexport function HiringJobDetailsPagePage() {\n const navigate = useNavigate();\n const [searchParams] = useSearchParams();\n const { showToast } = useGlobalUi();\n const {\n jobPostings,\n candidates,\n updateJobPosting,\n removeJobPosting,\n initialized,\n jobPostingMatches,\n jobPostingMatchesLoading,\n fetchJobPostingMatches,\n } = useCrewFoundryStore();\n const jobId = searchParams.get('id');\n const job = jobId ? jobPostings.find((j) => j.id === jobId) : jobPostings[0];\n const [editOpen, setEditOpen] = useState(false);\n const [deleteOpen, setDeleteOpen] = useState(false);\n\n if (!initialized && !job) {\n return (\n <div className=\"flex min-h-[60vh] items-center justify-center p-6\">\n <Spinner size=\"lg\" />\n </div>\n );\n }\n\n if (!job) {\n return (\n <div className=\"p-6\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No job posting found\"\n description=\"This job posting is no longer available.\"\n />\n </div>\n );\n }\n\n // Applicants for this role. CrewCandidate has no jobId link, so match on the\n // (normalized) position title — the only real linkage the schema exposes.\n const titleKey = job.title.trim().toLowerCase();\n const jobCandidates = candidates.filter((c) => c.position.trim().toLowerCase() === titleKey);\n const countBy = (status: Candidate['status']) =>\n jobCandidates.filter((c) => c.status === status).length;\n\n const interviewCount = countBy('interview');\n\n // Real-data hiring funnel: \"Applied\" = everyone who applied to this role, then\n // the count currently at each downstream stage (offer folds in hired).\n const funnelStages: LabeledValue[] = [\n { label: 'Applied', value: jobCandidates.length },\n { label: 'Screening', value: countBy('screening') },\n { label: 'Interview', value: interviewCount },\n { label: 'Assessment', value: countBy('assessment') },\n { label: 'Offer', value: countBy('offer') + countBy('hired') },\n ];\n\n const editInitial: FormValues = {\n title: job.title,\n department: job.department,\n location: job.location,\n type: job.type,\n experienceLevel: job.experienceLevel,\n status: job.status,\n salaryMin: job.salaryMin,\n salaryMax: job.salaryMax,\n deadline: job.deadline,\n description: job.description,\n requirements: job.requirements,\n benefits: job.benefits,\n };\n\n const handleEditSubmit = async (values: FormValues) => {\n const updates: Partial<JobPosting> = {\n title: getText(values, 'title'),\n department: getText(values, 'department'),\n location: getText(values, 'location'),\n type: values.type as JobPosting['type'],\n experienceLevel: values.experienceLevel as JobPosting['experienceLevel'],\n status: values.status as JobPosting['status'],\n salaryMin: getNumber(values, 'salaryMin'),\n salaryMax: getNumber(values, 'salaryMax'),\n deadline: getText(values, 'deadline'),\n description: getText(values, 'description'),\n requirements: getTags(values, 'requirements'),\n benefits: getTags(values, 'benefits'),\n };\n await updateJobPosting(job.id, updates);\n showToast('Job posting updated', 'success');\n };\n\n const handleDelete = async () => {\n await removeJobPosting(job.id);\n showToast('Job posting deleted', 'success');\n navigate('../hiring/jobs-page');\n };\n\n const handleShare = async () => {\n try {\n await navigator.clipboard.writeText(window.location.href);\n showToast('Job link copied to clipboard', 'success');\n } catch {\n showToast('Could not copy the job link', 'error');\n }\n };\n\n const viewApplications = () =>\n navigate(`../hiring/applications-page?position=${encodeURIComponent(job.title)}`);\n\n // BOFF-4195 — AI-assisted candidate matching (deterministic skill-overlap +\n // experience-fit scoring; see wspace-crewfoundry-svc matching.service.ts).\n const matches = jobPostingMatches[job.id];\n const matchesLoaded = matches !== undefined;\n const scoreTone = (score: number): string =>\n score >= 75 ? STATUS.success : score >= 45 ? STATUS.warning : STATUS.danger;\n const handleGenerateMatches = async () => {\n try {\n await fetchJobPostingMatches(job.id);\n } catch {\n showToast('Unable to generate candidate matches', 'error');\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardContent className=\"flex flex-col gap-4 p-6 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"min-w-0\">\n <div className=\"mb-1 flex flex-wrap items-center gap-2\">\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>{job.title}</GradientText>\n </h1>\n <Badge variant={job.status === 'active' ? 'default' : 'secondary'}>\n {formatEnumLabel(job.status)}\n </Badge>\n </div>\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <Briefcase className=\"h-3.5 w-3.5\" />\n {job.department}\n </span>\n <span className=\"flex items-center gap-1\">\n <MapPin className=\"h-3.5 w-3.5\" />\n {job.location}\n </span>\n <span>{formatEnumLabel(job.type)}</span>\n </div>\n <div className=\"mt-2 flex flex-wrap items-center gap-2 text-xs text-text-secondary\">\n <Badge variant=\"secondary\" className=\"text-[10px]\">\n {jobCandidates.length} applicants\n </Badge>\n {interviewCount > 0 && (\n <Badge variant=\"secondary\" className=\"text-[10px]\">\n {interviewCount} in interview\n </Badge>\n )}\n <span className=\"flex items-center gap-1\">\n <Calendar className=\"h-3 w-3\" />\n Posted {job.postedDate}\n </span>\n </div>\n <PagePurpose className=\"mt-3\">\n Everything about this role in one place — review applicants, requirements, and\n compensation to decide who to interview and keep the posting current.\n </PagePurpose>\n </div>\n\n <div className=\"flex shrink-0 flex-col items-start gap-3 lg:items-end\">\n <div className=\"text-2xl font-bold tabular-nums text-text-primary\">\n ${(job.salaryMin / 1000).toFixed(0)}k – ${(job.salaryMax / 1000).toFixed(0)}k\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={handleShare}>\n <Share2 className=\"mr-1 h-4 w-4\" />\n Share\n </Button>\n <Button variant=\"outline\" size=\"sm\" onClick={() => setEditOpen(true)}>\n <Pencil className=\"mr-1 h-4 w-4\" />\n Edit\n </Button>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n aria-label=\"Delete job posting\"\n onClick={() => setDeleteOpen(true)}\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n <Button size=\"sm\" onClick={viewApplications}>\n <Users className=\"mr-1 h-4 w-4\" />\n View Applications\n </Button>\n </div>\n </div>\n </CardContent>\n </GlassCard>\n\n {/* Two-column body */}\n <div className=\"grid gap-6 lg:grid-cols-3\">\n {/* Left: description / requirements / benefits */}\n <div className=\"space-y-6 lg:col-span-2\">\n <Card>\n <CardHeader>\n <CardTitle>Description</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm leading-relaxed text-text-secondary\">{job.description}</p>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Requirements</CardTitle>\n </CardHeader>\n <CardContent>\n {job.requirements.length === 0 ? (\n <p className=\"text-sm text-text-muted\">No requirements listed.</p>\n ) : (\n <ul className=\"space-y-2\">\n {job.requirements.map((req, idx) => (\n <li key={idx} className=\"flex items-start gap-2 text-sm\">\n <CheckCircle className=\"mt-0.5 h-4 w-4 flex-shrink-0 text-status-success-text\" />\n {req}\n </li>\n ))}\n </ul>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Benefits</CardTitle>\n </CardHeader>\n <CardContent>\n {job.benefits.length === 0 ? (\n <p className=\"text-sm text-text-muted\">No benefits listed.</p>\n ) : (\n <div className=\"flex flex-wrap gap-2\">\n {job.benefits.map((ben, idx) => (\n <Badge key={idx} variant=\"outline\" className=\"rounded-full px-3 py-1 text-xs\">\n {ben}\n </Badge>\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* BOFF-4195 — AI-assisted candidate matching (deterministic, explainable) */}\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between gap-3\">\n <div>\n <CardTitle>Suggested Matches</CardTitle>\n <p className=\"mt-1 text-xs text-text-muted\">\n Rule-based scoring — skill overlap (70%) + experience fit (30%) against this\n role&apos;s requirements. Not an AI-generated summary.\n </p>\n </div>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={handleGenerateMatches}\n disabled={jobPostingMatchesLoading}\n >\n <Sparkles className=\"mr-1 h-4 w-4\" />\n {matchesLoaded ? 'Refresh matches' : 'Generate matches'}\n </Button>\n </CardHeader>\n <CardContent>\n {jobPostingMatchesLoading ? (\n <div className=\"flex items-center justify-center py-6\">\n <Spinner size=\"md\" />\n </div>\n ) : !matchesLoaded ? (\n <p className=\"text-sm text-text-muted\">\n Click &quot;Generate matches&quot; to rank the candidate pool against this\n posting&apos;s requirements and experience level.\n </p>\n ) : matches.length === 0 ? (\n <p className=\"text-sm text-text-muted\">\n No candidates in the pool right now (excluding already-hired/rejected).\n </p>\n ) : (\n <ul className=\"space-y-4\">\n {matches.map((match) => (\n <li\n key={match.candidateId}\n className=\"rounded-[var(--radius-card)] border border-border-subtle bg-bg-surface p-3\"\n >\n <div className=\"flex items-center gap-2\">\n <InitialsAvatar\n name={`${match.candidate.firstName} ${match.candidate.lastName}`}\n size={28}\n />\n <span className=\"min-w-0 flex-1 truncate text-sm font-medium text-text-primary\">\n {match.candidate.firstName} {match.candidate.lastName}\n </span>\n <Badge\n variant={STAGE_BADGE[match.candidate.status]}\n className=\"shrink-0 text-[9px]\"\n >\n {formatEnumLabel(match.candidate.status)}\n </Badge>\n </div>\n\n <div className=\"mt-2\">\n <SkillBar\n label=\"Overall match\"\n value={match.score}\n levelLabel={`${match.score}%`}\n color={scoreTone(match.score)}\n height={6}\n />\n </div>\n <div className=\"mt-2 grid grid-cols-2 gap-2 text-xs text-text-secondary\">\n <span>Skills: {match.skillScore}%</span>\n <span>Experience: {match.experienceScore}%</span>\n </div>\n\n {(match.matchedSkills.length > 0 || match.missingSkills.length > 0) && (\n <div className=\"mt-2 flex flex-wrap gap-1\">\n {match.matchedSkills.map((skill) => (\n <Badge\n key={`m-${skill}`}\n variant=\"secondary\"\n className=\"gap-1 text-[10px]\"\n >\n <CheckCircle className=\"h-3 w-3 text-status-success-text\" />\n {skill}\n </Badge>\n ))}\n {match.missingSkills.map((skill) => (\n <Badge\n key={`x-${skill}`}\n variant=\"outline\"\n className=\"gap-1 text-[10px] text-text-muted\"\n >\n <XCircle className=\"h-3 w-3 text-status-error-text\" />\n {skill}\n </Badge>\n ))}\n </div>\n )}\n\n {/* Rationale is always visible — the ranking is never a black box. */}\n <p className=\"mt-2 text-xs leading-relaxed text-text-muted\">\n {match.rationale}\n </p>\n </li>\n ))}\n </ul>\n )}\n </CardContent>\n </Card>\n </div>\n\n {/* Right rail: candidates + funnel */}\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Candidates</CardTitle>\n </CardHeader>\n <CardContent>\n {jobCandidates.length === 0 ? (\n <p className=\"text-sm text-text-muted\">No applicants matched to this role yet.</p>\n ) : (\n <ul className=\"max-h-96 space-y-1 overflow-y-auto pr-1\">\n {jobCandidates.map((candidate) => (\n <li\n key={candidate.id}\n className=\"flex items-center gap-2 rounded-[var(--radius-input)] px-2 py-2 hover:bg-bg-sunken\"\n >\n <InitialsAvatar\n name={`${candidate.firstName} ${candidate.lastName}`}\n size={30}\n />\n <span className=\"min-w-0 flex-1 truncate text-sm font-medium text-text-primary\">\n {candidate.firstName} {candidate.lastName}\n </span>\n <Badge\n variant={STAGE_BADGE[candidate.status]}\n className=\"shrink-0 text-[9px]\"\n >\n {formatEnumLabel(candidate.status)}\n </Badge>\n <RatingStars value={candidate.rating} size={12} className=\"shrink-0\" />\n </li>\n ))}\n </ul>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Hiring funnel</CardTitle>\n </CardHeader>\n <CardContent>\n {jobCandidates.length === 0 ? (\n <p className=\"text-sm text-text-muted\">\n The funnel appears once candidates apply to this role.\n </p>\n ) : (\n <HBarFunnel\n stages={funnelStages}\n valueFormatter={(value) => String(value)}\n showConversion\n />\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n\n <FormModal\n open={editOpen}\n onOpenChange={setEditOpen}\n title=\"Edit Job Posting\"\n description=\"Update the role details, compensation, and status.\"\n fields={JOB_FIELDS}\n initialValues={editInitial}\n submitLabel=\"Save changes\"\n onSubmit={handleEditSubmit}\n />\n <ConfirmDialog\n open={deleteOpen}\n onOpenChange={setDeleteOpen}\n title=\"Delete job posting\"\n description={`Permanently remove “${job.title}”? This cannot be undone.`}\n confirmLabel=\"Delete\"\n destructive\n onConfirm={handleDelete}\n />\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"HiringJobsPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/HiringJobsPagePage.tsx"],"names":[],"mappings":"AA2DA,wBAAgB,kBAAkB,gCA+MjC"}
1
+ {"version":3,"file":"HiringJobsPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/HiringJobsPagePage.tsx"],"names":[],"mappings":"AA2DA,wBAAgB,kBAAkB,gCA0MjC"}