@burdenoff/microfe-crewfoundry 2026.625.1 → 2026.625.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/crewfoundry/pages/AdminAdminPagePage.js +37 -29
- package/dist/crewfoundry/pages/AdminAdminPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsAnalyticsPagePage.js +50 -42
- package/dist/crewfoundry/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsEmployeeAnalyticsPagePage.js +39 -31
- package/dist/crewfoundry/pages/AnalyticsEmployeeAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js +52 -44
- package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.js +46 -38
- package/dist/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js +28 -15
- package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsCallsPagePage.js +30 -22
- package/dist/crewfoundry/pages/CommunicationsCallsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsDirectoryPagePage.js +87 -69
- package/dist/crewfoundry/pages/CommunicationsDirectoryPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsFilesPagePage.js +99 -80
- package/dist/crewfoundry/pages/CommunicationsFilesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.js +86 -67
- package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsTeamsPagePage.js +31 -15
- package/dist/crewfoundry/pages/CommunicationsTeamsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsVideoCallsPagePage.js +37 -29
- package/dist/crewfoundry/pages/CommunicationsVideoCallsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js +29 -21
- package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyCompanyDashboardPagePage.js +25 -17
- package/dist/crewfoundry/pages/CompanyCompanyDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyCompanyProfilePagePage.js +42 -35
- package/dist/crewfoundry/pages/CompanyCompanyProfilePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js +80 -62
- package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardAnalyticsPagePage.js +41 -33
- package/dist/crewfoundry/pages/DashboardAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardDashboardPagePage.js +142 -108
- package/dist/crewfoundry/pages/DashboardDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardEnhancedDashboardPagePage.js +177 -136
- package/dist/crewfoundry/pages/DashboardEnhancedDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardExecutiveDashboardPage.js +44 -36
- package/dist/crewfoundry/pages/DashboardExecutiveDashboardPage.js.map +1 -1
- package/dist/crewfoundry/pages/HelpApiDocumentationPagePage.js +49 -38
- package/dist/crewfoundry/pages/HelpApiDocumentationPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringApplicationsPagePage.js +26 -18
- package/dist/crewfoundry/pages/HiringApplicationsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringCandidatesPagePage.js +112 -93
- package/dist/crewfoundry/pages/HiringCandidatesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringCreateJobPagePage.js +53 -46
- package/dist/crewfoundry/pages/HiringCreateJobPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringInterviewsPagePage.js +31 -23
- package/dist/crewfoundry/pages/HiringInterviewsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js +129 -90
- package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringJobsPagePage.js +96 -85
- package/dist/crewfoundry/pages/HiringJobsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrDepartmentsPagePage.js +26 -18
- package/dist/crewfoundry/pages/HrDepartmentsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrEmployeesPagePage.js +47 -35
- package/dist/crewfoundry/pages/HrEmployeesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js +33 -25
- package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrPayrollPagePage.js +36 -28
- package/dist/crewfoundry/pages/HrPayrollPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js +27 -19
- package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningAssessmentsPagePage.js +27 -19
- package/dist/crewfoundry/pages/LearningAssessmentsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCertificatesPagePage.js +93 -75
- package/dist/crewfoundry/pages/LearningCertificatesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js +111 -70
- package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js +65 -53
- package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCoursesPagePage.js +35 -19
- package/dist/crewfoundry/pages/LearningCoursesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningAnalyticsPagePage.js +33 -25
- package/dist/crewfoundry/pages/LearningLearningAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPagePage.js +26 -18
- package/dist/crewfoundry/pages/LearningLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js +80 -55
- package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js +33 -17
- package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningMyLearningPagePage.js +31 -23
- package/dist/crewfoundry/pages/LearningMyLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceBookingManagementPagePage.js +29 -21
- package/dist/crewfoundry/pages/MarketplaceBookingManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceEarningsPagePage.js +35 -27
- package/dist/crewfoundry/pages/MarketplaceEarningsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.js +147 -112
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js +31 -27
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js +31 -23
- package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js +104 -85
- package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMessagingPagePage.js +75 -56
- package/dist/crewfoundry/pages/MarketplaceMessagingPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.js +78 -60
- package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.js +133 -92
- package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js +93 -52
- package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsEscrowPagePage.js +28 -20
- package/dist/crewfoundry/pages/PaymentsEscrowPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js +84 -65
- package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.js +25 -17
- package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsPaymentMethodsPagePage.js +58 -40
- package/dist/crewfoundry/pages/PaymentsPaymentMethodsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsSecurityPagePage.js +41 -33
- package/dist/crewfoundry/pages/PaymentsSecurityPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsCreateProjectPagePage.js +36 -29
- package/dist/crewfoundry/pages/ProjectsCreateProjectPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsProjectDetailsPagePage.js +103 -72
- package/dist/crewfoundry/pages/ProjectsProjectDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js +43 -27
- package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamAnalyticsPagePage.js +28 -20
- package/dist/crewfoundry/pages/TeamsTeamAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamDetailPagePage.js +103 -62
- package/dist/crewfoundry/pages/TeamsTeamDetailPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamDirectoryPagePage.js +83 -64
- package/dist/crewfoundry/pages/TeamsTeamDirectoryPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamSettingsPagePage.js +185 -144
- package/dist/crewfoundry/pages/TeamsTeamSettingsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamsPagePage.js +40 -24
- package/dist/crewfoundry/pages/TeamsTeamsPagePage.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnalyticsFinancialAnalyticsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n Separator,\n} from '@burdenoff/fe-libs/ui';\nimport { ArrowUp, DollarSign, FileText, Receipt, TrendingUp, Wallet } from 'lucide-react';\nimport { useMemo } from 'react';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\nexport function AnalyticsFinancialAnalyticsPagePage() {\n const { employees, departments, payrollRecords, invoices, projects } = useCrewFoundryStore();\n\n const monthlyPayrollTrend = useMemo(() => {\n const byPeriod = payrollRecords.reduce<\n Record<string, { gross: number; net: number; deductions: number }>\n >((acc, record) => {\n const month = new Date(record.payDate).toLocaleString('en-US', { month: 'short' });\n const current = acc[month] || { gross: 0, net: 0, deductions: 0 };\n current.gross += record.grossPay;\n current.net += record.netPay;\n current.deductions += record.deductions;\n acc[month] = current;\n return acc;\n }, {});\n return Object.entries(byPeriod)\n .map(([month, value]) => ({ month, ...value }))\n .slice(-6);\n }, [payrollRecords]);\n\n const expenseCategories = useMemo(\n () => [\n {\n category: 'Payroll',\n amount: payrollRecords.reduce((sum, record) => sum + record.grossPay, 0),\n budget: departments.reduce((sum, department) => sum + department.budget * 0.7, 0),\n icon: Wallet,\n },\n {\n category: 'Benefits',\n amount: payrollRecords.reduce((sum, record) => sum + record.deductions * 0.4, 0),\n budget: departments.reduce((sum, department) => sum + department.budget * 0.15, 0),\n icon: Receipt,\n },\n {\n category: 'Recruitment',\n amount: invoices.reduce(\n (sum, invoice) => sum + (invoice.status === 'paid' ? invoice.amount * 0.15 : 0),\n 0\n ),\n budget: departments.reduce((sum, department) => sum + department.budget * 0.1, 0),\n icon: FileText,\n },\n {\n category: 'Training',\n amount: projects.reduce((sum, project) => sum + project.spent * 0.08, 0),\n budget: departments.reduce((sum, department) => sum + department.budget * 0.08, 0),\n icon: TrendingUp,\n },\n ],\n [departments, invoices, payrollRecords, projects]\n );\n\n const totalPayroll = employees.reduce((sum, e) => sum + e.salary, 0);\n const monthlyPayroll = Math.round(totalPayroll / 12);\n const totalInvoiced = invoices.reduce((sum, inv) => sum + inv.amount, 0);\n const paidInvoices = invoices\n .filter((i) => i.status === 'paid')\n .reduce((s, i) => s + i.amount, 0);\n const totalProjectBudget = projects.reduce((s, p) => s + p.budget, 0);\n const totalProjectSpent = projects.reduce((s, p) => s + p.spent, 0);\n\n const maxPayroll = Math.max(1, ...monthlyPayrollTrend.map((m) => m.gross));\n\n const deptCost = departments.map((d) => {\n const deptEmps = employees.filter((e) => e.department === d.name);\n const totalSal = deptEmps.reduce((s, e) => s + e.salary, 0);\n return { name: d.name, salary: totalSal, budget: d.budget, headcount: deptEmps.length };\n });\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Financial Analytics</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n Workforce financial metrics and cost analysis\n </p>\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Monthly Payroll</CardTitle>\n <DollarSign className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${(monthlyPayroll / 1000).toFixed(0)}K\n </div>\n <div className=\"flex items-center text-xs text-status-warning-text\">\n <ArrowUp className=\"h-3 w-3 mr-1\" />\n +2.1% from last month\n </div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Annual Salary Cost</CardTitle>\n <Wallet className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${(totalPayroll / 1000000).toFixed(1)}M\n </div>\n <p className=\"text-xs text-text-secondary\">{employees.length} employees</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Total Invoiced</CardTitle>\n <FileText className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${(totalInvoiced / 1000).toFixed(1)}K\n </div>\n <p className=\"text-xs text-text-secondary\">\n ${(paidInvoices / 1000).toFixed(1)}K collected\n </p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Project Budget</CardTitle>\n <TrendingUp className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${(totalProjectBudget / 1000000).toFixed(1)}M\n </div>\n <p className=\"text-xs text-text-secondary\">\n {Math.round((totalProjectSpent / totalProjectBudget) * 100)}% utilized\n </p>\n </CardContent>\n </GlassCard>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Payroll Trend</CardTitle>\n <CardDescription>Monthly gross payroll over the last 6 months</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {monthlyPayrollTrend.map((m) => (\n <div key={m.month} className=\"flex items-center gap-3\">\n <span className=\"w-10 text-sm text-text-secondary\">{m.month}</span>\n <div className=\"flex-1 h-5 bg-bg-sunken rounded overflow-hidden flex\">\n <div\n className=\"h-5 bg-action-primary-bg\"\n style={{ width: `${Math.round((m.net / maxPayroll) * 100)}%` }}\n />\n <div\n className=\"h-5 bg-action-primary-bg/30\"\n style={{ width: `${Math.round((m.deductions / maxPayroll) * 100)}%` }}\n />\n </div>\n <span className=\"text-sm font-medium w-20 text-right\">\n ${(m.gross / 1000000).toFixed(2)}M\n </span>\n </div>\n ))}\n <div className=\"flex items-center gap-4 mt-2 text-xs text-text-secondary\">\n <div className=\"flex items-center gap-1\">\n <div className=\"h-3 w-3 bg-action-primary-bg rounded\" />\n <span>Net Pay</span>\n </div>\n <div className=\"flex items-center gap-1\">\n <div className=\"h-3 w-3 bg-action-primary-bg/30 rounded\" />\n <span>Deductions</span>\n </div>\n </div>\n </CardContent>\n </Card>\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle>Budget vs Actual</CardTitle>\n <CardDescription>Monthly expense tracking by category</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {expenseCategories.map((cat) => {\n const pct = Math.round((cat.amount / cat.budget) * 100);\n return (\n <div key={cat.category} className=\"space-y-1\">\n <div className=\"flex justify-between text-sm\">\n <span className=\"font-medium\">{cat.category}</span>\n <span>\n ${(cat.amount / 1000).toFixed(0)}K / ${(cat.budget / 1000).toFixed(0)}K\n </span>\n </div>\n <div className=\"h-3 bg-bg-sunken rounded overflow-hidden\">\n <div\n className={`h-3 rounded ${pct > 95 ? 'bg-status-error-text' : pct > 80 ? 'bg-status-warning-text' : 'bg-status-success-text'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n <p className=\"text-xs text-text-secondary text-right\">{pct}% utilized</p>\n </div>\n );\n })}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Outstanding Invoices</CardTitle>\n <CardDescription>Invoices requiring attention</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {invoices.map((inv) => (\n <div key={inv.id} className=\"flex items-center justify-between rounded border p-3\">\n <div>\n <p className=\"text-sm font-medium\">{inv.number}</p>\n <p className=\"text-xs text-text-secondary\">{inv.clientName}</p>\n </div>\n <div className=\"text-right\">\n <p className=\"text-sm font-medium\">${inv.amount.toLocaleString()}</p>\n <Badge\n variant={\n inv.status === 'paid'\n ? 'default'\n : inv.status === 'overdue'\n ? 'destructive'\n : 'secondary'\n }\n className=\"text-[10px]\"\n >\n {inv.status}\n </Badge>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Department Cost Analysis</CardTitle>\n <CardDescription>Total salary cost and budget allocation by department</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-5 gap-2 text-xs font-medium text-text-secondary border-b pb-2 mb-2\">\n <span>Department</span>\n <span className=\"text-right\">Headcount</span>\n <span className=\"text-right\">Total Salary</span>\n <span className=\"text-right\">Dept Budget</span>\n <span className=\"text-right\">Salary/Budget %</span>\n </div>\n {deptCost\n .sort((a, b) => b.salary - a.salary)\n .map((d) => (\n <div\n key={d.name}\n className=\"grid grid-cols-5 gap-2 py-2 border-b last:border-0 text-sm\"\n >\n <span className=\"font-medium\">{d.name}</span>\n <span className=\"text-right\">{d.headcount}</span>\n <span className=\"text-right\">${(d.salary / 1000).toFixed(0)}K</span>\n <span className=\"text-right\">${(d.budget / 1000000).toFixed(1)}M</span>\n <span className=\"text-right\">\n <Badge\n variant={d.salary / d.budget > 0.5 ? 'destructive' : 'default'}\n className=\"text-[10px]\"\n >\n {Math.round((d.salary / d.budget) * 100)}%\n </Badge>\n </span>\n </div>\n ))}\n <Separator className=\"my-2\" />\n <div className=\"grid grid-cols-5 gap-2 text-sm font-bold\">\n <span>Total</span>\n <span className=\"text-right\">{employees.length}</span>\n <span className=\"text-right\">${(totalPayroll / 1000000).toFixed(1)}M</span>\n <span className=\"text-right\">\n ${(departments.reduce((s, d) => s + d.budget, 0) / 1000000).toFixed(1)}M\n </span>\n <span />\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AnalyticsFinancialAnalyticsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n PagePurpose,\n Separator,\n} from '@burdenoff/fe-libs/ui';\nimport { ArrowUp, DollarSign, FileText, Receipt, TrendingUp, Wallet } from 'lucide-react';\nimport { useMemo } from 'react';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\nexport function AnalyticsFinancialAnalyticsPagePage() {\n const { employees, departments, payrollRecords, invoices, projects } = useCrewFoundryStore();\n\n const monthlyPayrollTrend = useMemo(() => {\n const byPeriod = payrollRecords.reduce<\n Record<string, { gross: number; net: number; deductions: number }>\n >((acc, record) => {\n const month = new Date(record.payDate).toLocaleString('en-US', { month: 'short' });\n const current = acc[month] || { gross: 0, net: 0, deductions: 0 };\n current.gross += record.grossPay;\n current.net += record.netPay;\n current.deductions += record.deductions;\n acc[month] = current;\n return acc;\n }, {});\n return Object.entries(byPeriod)\n .map(([month, value]) => ({ month, ...value }))\n .slice(-6);\n }, [payrollRecords]);\n\n const expenseCategories = useMemo(\n () => [\n {\n category: 'Payroll',\n amount: payrollRecords.reduce((sum, record) => sum + record.grossPay, 0),\n budget: departments.reduce((sum, department) => sum + department.budget * 0.7, 0),\n icon: Wallet,\n },\n {\n category: 'Benefits',\n amount: payrollRecords.reduce((sum, record) => sum + record.deductions * 0.4, 0),\n budget: departments.reduce((sum, department) => sum + department.budget * 0.15, 0),\n icon: Receipt,\n },\n {\n category: 'Recruitment',\n amount: invoices.reduce(\n (sum, invoice) => sum + (invoice.status === 'paid' ? invoice.amount * 0.15 : 0),\n 0\n ),\n budget: departments.reduce((sum, department) => sum + department.budget * 0.1, 0),\n icon: FileText,\n },\n {\n category: 'Training',\n amount: projects.reduce((sum, project) => sum + project.spent * 0.08, 0),\n budget: departments.reduce((sum, department) => sum + department.budget * 0.08, 0),\n icon: TrendingUp,\n },\n ],\n [departments, invoices, payrollRecords, projects]\n );\n\n const totalPayroll = employees.reduce((sum, e) => sum + e.salary, 0);\n const monthlyPayroll = Math.round(totalPayroll / 12);\n const totalInvoiced = invoices.reduce((sum, inv) => sum + inv.amount, 0);\n const paidInvoices = invoices\n .filter((i) => i.status === 'paid')\n .reduce((s, i) => s + i.amount, 0);\n const totalProjectBudget = projects.reduce((s, p) => s + p.budget, 0);\n const totalProjectSpent = projects.reduce((s, p) => s + p.spent, 0);\n\n const maxPayroll = Math.max(1, ...monthlyPayrollTrend.map((m) => m.gross));\n\n const deptCost = departments.map((d) => {\n const deptEmps = employees.filter((e) => e.department === d.name);\n const totalSal = deptEmps.reduce((s, e) => s + e.salary, 0);\n return { name: d.name, salary: totalSal, budget: d.budget, headcount: deptEmps.length };\n });\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Financial Analytics</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n Workforce financial metrics and cost analysis\n </p>\n <PagePurpose className=\"mt-1\">Understand the money side of your workforce — payroll, project budgets, and spend trends — so you can plan headcount and control costs with confidence.</PagePurpose>\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Monthly Payroll</CardTitle>\n <DollarSign className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${(monthlyPayroll / 1000).toFixed(0)}K\n </div>\n <div className=\"flex items-center text-xs text-status-warning-text\">\n <ArrowUp className=\"h-3 w-3 mr-1\" />\n +2.1% from last month\n </div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Annual Salary Cost</CardTitle>\n <Wallet className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${(totalPayroll / 1000000).toFixed(1)}M\n </div>\n <p className=\"text-xs text-text-secondary\">{employees.length} employees</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Total Invoiced</CardTitle>\n <FileText className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${(totalInvoiced / 1000).toFixed(1)}K\n </div>\n <p className=\"text-xs text-text-secondary\">\n ${(paidInvoices / 1000).toFixed(1)}K collected\n </p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Project Budget</CardTitle>\n <TrendingUp className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${(totalProjectBudget / 1000000).toFixed(1)}M\n </div>\n <p className=\"text-xs text-text-secondary\">\n {Math.round((totalProjectSpent / totalProjectBudget) * 100)}% utilized\n </p>\n </CardContent>\n </GlassCard>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Payroll Trend</CardTitle>\n <CardDescription>Monthly gross payroll over the last 6 months</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {monthlyPayrollTrend.map((m) => (\n <div key={m.month} className=\"flex items-center gap-3\">\n <span className=\"w-10 text-sm text-text-secondary\">{m.month}</span>\n <div className=\"flex-1 h-5 bg-bg-sunken rounded overflow-hidden flex\">\n <div\n className=\"h-5 bg-action-primary-bg\"\n style={{ width: `${Math.round((m.net / maxPayroll) * 100)}%` }}\n />\n <div\n className=\"h-5 bg-action-primary-bg/30\"\n style={{ width: `${Math.round((m.deductions / maxPayroll) * 100)}%` }}\n />\n </div>\n <span className=\"text-sm font-medium w-20 text-right\">\n ${(m.gross / 1000000).toFixed(2)}M\n </span>\n </div>\n ))}\n <div className=\"flex items-center gap-4 mt-2 text-xs text-text-secondary\">\n <div className=\"flex items-center gap-1\">\n <div className=\"h-3 w-3 bg-action-primary-bg rounded\" />\n <span>Net Pay</span>\n </div>\n <div className=\"flex items-center gap-1\">\n <div className=\"h-3 w-3 bg-action-primary-bg/30 rounded\" />\n <span>Deductions</span>\n </div>\n </div>\n </CardContent>\n </Card>\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle>Budget vs Actual</CardTitle>\n <CardDescription>Monthly expense tracking by category</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {expenseCategories.map((cat) => {\n const pct = Math.round((cat.amount / cat.budget) * 100);\n return (\n <div key={cat.category} className=\"space-y-1\">\n <div className=\"flex justify-between text-sm\">\n <span className=\"font-medium\">{cat.category}</span>\n <span>\n ${(cat.amount / 1000).toFixed(0)}K / ${(cat.budget / 1000).toFixed(0)}K\n </span>\n </div>\n <div className=\"h-3 bg-bg-sunken rounded overflow-hidden\">\n <div\n className={`h-3 rounded ${pct > 95 ? 'bg-status-error-text' : pct > 80 ? 'bg-status-warning-text' : 'bg-status-success-text'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n <p className=\"text-xs text-text-secondary text-right\">{pct}% utilized</p>\n </div>\n );\n })}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Outstanding Invoices</CardTitle>\n <CardDescription>Invoices requiring attention</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {invoices.map((inv) => (\n <div key={inv.id} className=\"flex items-center justify-between rounded border p-3\">\n <div>\n <p className=\"text-sm font-medium\">{inv.number}</p>\n <p className=\"text-xs text-text-secondary\">{inv.clientName}</p>\n </div>\n <div className=\"text-right\">\n <p className=\"text-sm font-medium\">${inv.amount.toLocaleString()}</p>\n <Badge\n variant={\n inv.status === 'paid'\n ? 'default'\n : inv.status === 'overdue'\n ? 'destructive'\n : 'secondary'\n }\n className=\"text-[10px]\"\n >\n {inv.status}\n </Badge>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Department Cost Analysis</CardTitle>\n <CardDescription>Total salary cost and budget allocation by department</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-5 gap-2 text-xs font-medium text-text-secondary border-b pb-2 mb-2\">\n <span>Department</span>\n <span className=\"text-right\">Headcount</span>\n <span className=\"text-right\">Total Salary</span>\n <span className=\"text-right\">Dept Budget</span>\n <span className=\"text-right\">Salary/Budget %</span>\n </div>\n {deptCost\n .sort((a, b) => b.salary - a.salary)\n .map((d) => (\n <div\n key={d.name}\n className=\"grid grid-cols-5 gap-2 py-2 border-b last:border-0 text-sm\"\n >\n <span className=\"font-medium\">{d.name}</span>\n <span className=\"text-right\">{d.headcount}</span>\n <span className=\"text-right\">${(d.salary / 1000).toFixed(0)}K</span>\n <span className=\"text-right\">${(d.budget / 1000000).toFixed(1)}M</span>\n <span className=\"text-right\">\n <Badge\n variant={d.salary / d.budget > 0.5 ? 'destructive' : 'default'}\n className=\"text-[10px]\"\n >\n {Math.round((d.salary / d.budget) * 100)}%\n </Badge>\n </span>\n </div>\n ))}\n <Separator className=\"my-2\" />\n <div className=\"grid grid-cols-5 gap-2 text-sm font-bold\">\n <span>Total</span>\n <span className=\"text-right\">{employees.length}</span>\n <span className=\"text-right\">${(totalPayroll / 1000000).toFixed(1)}M</span>\n <span className=\"text-right\">\n ${(departments.reduce((s, d) => s + d.budget, 0) / 1000000).toFixed(1)}M\n </span>\n <span />\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -2,10 +2,10 @@ import { useCrewFoundryStore as e } from "../store/crewfoundryStore.js";
|
|
|
2
2
|
import { useMemo as t } from "react";
|
|
3
3
|
import { Briefcase as n, Clock as r, Target as i, TrendingUp as a, Users as o } from "lucide-react";
|
|
4
4
|
import { jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
5
|
-
import { Badge as l, Card as u, CardContent as d, CardDescription as f, CardHeader as p, CardTitle as m, GlassCard as h, GradientText as g } from "@burdenoff/fe-libs/ui";
|
|
5
|
+
import { Badge as l, Card as u, CardContent as d, CardDescription as f, CardHeader as p, CardTitle as m, GlassCard as h, GradientText as g, PagePurpose as _ } from "@burdenoff/fe-libs/ui";
|
|
6
6
|
//#region src/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.tsx
|
|
7
|
-
function
|
|
8
|
-
let { jobPostings:
|
|
7
|
+
function v() {
|
|
8
|
+
let { jobPostings: v, candidates: y, interviews: b } = e(), x = t(() => [
|
|
9
9
|
"Sep",
|
|
10
10
|
"Oct",
|
|
11
11
|
"Nov",
|
|
@@ -13,7 +13,7 @@ function _() {
|
|
|
13
13
|
"Jan",
|
|
14
14
|
"Feb"
|
|
15
15
|
].map((e, t) => {
|
|
16
|
-
let n =
|
|
16
|
+
let n = b.filter((t) => new Date(t.date).toLocaleString("en-US", { month: "short" }) === e).length, r = y.filter((t) => new Date(t.appliedDate).toLocaleString("en-US", { month: "short" }) === e), i = r.filter((e) => e.status === "offer").length, a = r.filter((e) => e.status === "hired").length, o = r.length + v.reduce((e, t) => e + t.applicants, 0) / 6;
|
|
17
17
|
return {
|
|
18
18
|
month: e,
|
|
19
19
|
applications: Math.round(o - t * 3),
|
|
@@ -22,11 +22,11 @@ function _() {
|
|
|
22
22
|
hires: a
|
|
23
23
|
};
|
|
24
24
|
}), [
|
|
25
|
-
v,
|
|
26
25
|
y,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
b,
|
|
27
|
+
v
|
|
28
|
+
]), S = t(() => {
|
|
29
|
+
let e = y.reduce((e, t) => {
|
|
30
30
|
let n = t.source || "Unknown", r = e[n] || {
|
|
31
31
|
applications: 0,
|
|
32
32
|
hires: 0
|
|
@@ -38,63 +38,71 @@ function _() {
|
|
|
38
38
|
applications: t.applications,
|
|
39
39
|
hires: t.hires,
|
|
40
40
|
conversionRate: Number((t.hires / Math.max(t.applications, 1) * 100).toFixed(1)),
|
|
41
|
-
costPerHire: Math.round(
|
|
41
|
+
costPerHire: Math.round(v.reduce((e, t) => e + t.salaryMin * .02, 0) / Math.max(t.hires, 1))
|
|
42
42
|
}));
|
|
43
|
-
}, [
|
|
43
|
+
}, [y, v]), C = v.filter((e) => e.status === "active"), w = v.reduce((e, t) => e + t.applicants, 0), T = b.filter((e) => e.status === "scheduled").length, E = b.filter((e) => e.status === "completed").length, D = y.filter((e) => e.status === "offer").length, O = y.filter((e) => e.status === "hired").length, k = Math.max(1, ...x.map((e) => e.applications)), A = [
|
|
44
44
|
{
|
|
45
45
|
status: "New",
|
|
46
|
-
count:
|
|
46
|
+
count: y.filter((e) => e.status === "new").length,
|
|
47
47
|
color: "bg-text-secondary"
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
status: "Screening",
|
|
51
|
-
count:
|
|
51
|
+
count: y.filter((e) => e.status === "screening").length,
|
|
52
52
|
color: "bg-status-info-text"
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
55
|
status: "Interview",
|
|
56
|
-
count:
|
|
56
|
+
count: y.filter((e) => e.status === "interview").length,
|
|
57
57
|
color: "bg-status-info-text"
|
|
58
58
|
},
|
|
59
59
|
{
|
|
60
60
|
status: "Assessment",
|
|
61
|
-
count:
|
|
61
|
+
count: y.filter((e) => e.status === "assessment").length,
|
|
62
62
|
color: "bg-action-primary-bg"
|
|
63
63
|
},
|
|
64
64
|
{
|
|
65
65
|
status: "Offer",
|
|
66
|
-
count:
|
|
66
|
+
count: D,
|
|
67
67
|
color: "bg-status-warning-text"
|
|
68
68
|
},
|
|
69
69
|
{
|
|
70
70
|
status: "Hired",
|
|
71
|
-
count:
|
|
71
|
+
count: O,
|
|
72
72
|
color: "bg-status-success-text"
|
|
73
73
|
},
|
|
74
74
|
{
|
|
75
75
|
status: "Rejected",
|
|
76
|
-
count:
|
|
76
|
+
count: y.filter((e) => e.status === "rejected").length,
|
|
77
77
|
color: "bg-status-error-text"
|
|
78
78
|
}
|
|
79
|
-
],
|
|
79
|
+
], j = A.reduce((e, t) => e + t.count, 0);
|
|
80
80
|
return /* @__PURE__ */ c("div", {
|
|
81
81
|
className: "space-y-6 p-6",
|
|
82
82
|
children: [
|
|
83
83
|
/* @__PURE__ */ s("div", {
|
|
84
84
|
className: "flex items-center justify-between",
|
|
85
|
-
children: /* @__PURE__ */ c("div", { children: [
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
85
|
+
children: /* @__PURE__ */ c("div", { children: [
|
|
86
|
+
/* @__PURE__ */ s("h1", {
|
|
87
|
+
className: "text-2xl font-semibold",
|
|
88
|
+
children: /* @__PURE__ */ s(g, { children: "Hiring Analytics" })
|
|
89
|
+
}),
|
|
90
|
+
/* @__PURE__ */ s("p", {
|
|
91
|
+
className: "text-sm text-text-secondary",
|
|
92
|
+
children: "Recruitment performance and pipeline analysis"
|
|
93
|
+
}),
|
|
94
|
+
/* @__PURE__ */ s(_, {
|
|
95
|
+
className: "mt-1",
|
|
96
|
+
children: "Measure how your hiring funnel performs — applications, time-to-hire, and source quality — to make recruiting faster and more effective."
|
|
97
|
+
})
|
|
98
|
+
] })
|
|
92
99
|
}),
|
|
93
100
|
/* @__PURE__ */ c("div", {
|
|
94
101
|
className: "grid gap-4 md:grid-cols-2 lg:grid-cols-4",
|
|
95
102
|
children: [
|
|
96
103
|
/* @__PURE__ */ c(h, {
|
|
97
|
-
treatment: "
|
|
104
|
+
treatment: "glass",
|
|
105
|
+
glow: !0,
|
|
98
106
|
className: "border-border-seam",
|
|
99
107
|
children: [/* @__PURE__ */ c(p, {
|
|
100
108
|
className: "flex flex-row items-center justify-between space-y-0 pb-2",
|
|
@@ -104,10 +112,10 @@ function _() {
|
|
|
104
112
|
}), /* @__PURE__ */ s(n, { className: "h-4 w-4 text-text-secondary" })]
|
|
105
113
|
}), /* @__PURE__ */ c(d, { children: [/* @__PURE__ */ s("div", {
|
|
106
114
|
className: "text-2xl font-bold tabular-nums",
|
|
107
|
-
children:
|
|
115
|
+
children: C.length
|
|
108
116
|
}), /* @__PURE__ */ c("p", {
|
|
109
117
|
className: "text-xs text-text-secondary",
|
|
110
|
-
children: [
|
|
118
|
+
children: [w, " total applicants"]
|
|
111
119
|
})] })]
|
|
112
120
|
}),
|
|
113
121
|
/* @__PURE__ */ c(h, {
|
|
@@ -142,9 +150,9 @@ function _() {
|
|
|
142
150
|
}), /* @__PURE__ */ c("p", {
|
|
143
151
|
className: "text-xs text-text-secondary",
|
|
144
152
|
children: [
|
|
145
|
-
w,
|
|
146
|
-
" scheduled, ",
|
|
147
153
|
T,
|
|
154
|
+
" scheduled, ",
|
|
155
|
+
E,
|
|
148
156
|
" completed"
|
|
149
157
|
]
|
|
150
158
|
})] })]
|
|
@@ -163,7 +171,7 @@ function _() {
|
|
|
163
171
|
children: "87%"
|
|
164
172
|
}), /* @__PURE__ */ c("p", {
|
|
165
173
|
className: "text-xs text-text-secondary",
|
|
166
|
-
children: [
|
|
174
|
+
children: [O, " hired this quarter"]
|
|
167
175
|
})] })]
|
|
168
176
|
})
|
|
169
177
|
]
|
|
@@ -174,7 +182,7 @@ function _() {
|
|
|
174
182
|
className: "col-span-4",
|
|
175
183
|
children: [/* @__PURE__ */ c(p, { children: [/* @__PURE__ */ s(m, { children: "Monthly Hiring Activity" }), /* @__PURE__ */ s(f, { children: "Applications, interviews, and hires over time" })] }), /* @__PURE__ */ s(d, {
|
|
176
184
|
className: "space-y-3",
|
|
177
|
-
children:
|
|
185
|
+
children: x.map((e) => /* @__PURE__ */ c("div", {
|
|
178
186
|
className: "flex items-center gap-3",
|
|
179
187
|
children: [
|
|
180
188
|
/* @__PURE__ */ s("span", {
|
|
@@ -185,7 +193,7 @@ function _() {
|
|
|
185
193
|
className: "flex-1 h-5 bg-bg-sunken rounded overflow-hidden",
|
|
186
194
|
children: /* @__PURE__ */ s("div", {
|
|
187
195
|
className: "h-5 bg-action-primary-bg rounded",
|
|
188
|
-
style: { width: `${Math.round(e.applications / (
|
|
196
|
+
style: { width: `${Math.round(e.applications / (k + 20) * 100)}%` }
|
|
189
197
|
})
|
|
190
198
|
}),
|
|
191
199
|
/* @__PURE__ */ c("div", {
|
|
@@ -215,7 +223,7 @@ function _() {
|
|
|
215
223
|
className: "col-span-3",
|
|
216
224
|
children: [/* @__PURE__ */ c(p, { children: [/* @__PURE__ */ s(m, { children: "Pipeline Status" }), /* @__PURE__ */ s(f, { children: "Current candidate distribution" })] }), /* @__PURE__ */ s(d, {
|
|
217
225
|
className: "space-y-3",
|
|
218
|
-
children:
|
|
226
|
+
children: A.map((e) => /* @__PURE__ */ c("div", {
|
|
219
227
|
className: "flex items-center gap-3",
|
|
220
228
|
children: [
|
|
221
229
|
/* @__PURE__ */ s("div", { className: `h-3 w-3 rounded-full ${e.color}` }),
|
|
@@ -227,7 +235,7 @@ function _() {
|
|
|
227
235
|
className: "flex-1 h-3 bg-bg-sunken rounded overflow-hidden",
|
|
228
236
|
children: /* @__PURE__ */ s("div", {
|
|
229
237
|
className: `h-3 rounded ${e.color}`,
|
|
230
|
-
style: { width: `${
|
|
238
|
+
style: { width: `${j > 0 ? Math.max(Math.round(e.count / j * 100), 3) : 3}%` }
|
|
231
239
|
})
|
|
232
240
|
}),
|
|
233
241
|
/* @__PURE__ */ s("span", {
|
|
@@ -260,7 +268,7 @@ function _() {
|
|
|
260
268
|
children: "Cost/Hire"
|
|
261
269
|
})
|
|
262
270
|
]
|
|
263
|
-
}),
|
|
271
|
+
}), S.sort((e, t) => t.conversionRate - e.conversionRate).map((e) => /* @__PURE__ */ c("div", {
|
|
264
272
|
className: "grid grid-cols-5 gap-2 py-2 border-b last:border-0 text-sm",
|
|
265
273
|
children: [
|
|
266
274
|
/* @__PURE__ */ s("span", {
|
|
@@ -308,7 +316,7 @@ function _() {
|
|
|
308
316
|
children: "Status"
|
|
309
317
|
})
|
|
310
318
|
]
|
|
311
|
-
}),
|
|
319
|
+
}), v.map((e) => /* @__PURE__ */ c("div", {
|
|
312
320
|
className: "grid grid-cols-6 gap-2 py-2 border-b last:border-0 text-sm",
|
|
313
321
|
children: [
|
|
314
322
|
/* @__PURE__ */ s("span", {
|
|
@@ -352,6 +360,6 @@ function _() {
|
|
|
352
360
|
});
|
|
353
361
|
}
|
|
354
362
|
//#endregion
|
|
355
|
-
export {
|
|
363
|
+
export { v as AnalyticsHiringAnalyticsPagePage };
|
|
356
364
|
|
|
357
365
|
//# sourceMappingURL=AnalyticsHiringAnalyticsPagePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnalyticsHiringAnalyticsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n} from '@burdenoff/fe-libs/ui';\nimport { Briefcase, Clock, Target, TrendingUp, Users } from 'lucide-react';\nimport { useMemo } from 'react';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\nexport function AnalyticsHiringAnalyticsPagePage() {\n const { jobPostings, candidates, interviews } = useCrewFoundryStore();\n\n const hiringMetrics = useMemo(() => {\n const months = ['Sep', 'Oct', 'Nov', 'Dec', 'Jan', 'Feb'];\n return months.map((month, index) => {\n const monthInterviews = interviews.filter(\n (interview) =>\n new Date(interview.date).toLocaleString('en-US', { month: 'short' }) === month\n ).length;\n const monthCandidates = candidates.filter(\n (candidate) =>\n new Date(candidate.appliedDate).toLocaleString('en-US', { month: 'short' }) === month\n );\n const offers = monthCandidates.filter((candidate) => candidate.status === 'offer').length;\n const hires = monthCandidates.filter((candidate) => candidate.status === 'hired').length;\n const applications =\n monthCandidates.length +\n jobPostings.reduce((sum, posting) => sum + posting.applicants, 0) / 6;\n return {\n month,\n applications: Math.round(applications - index * 3),\n interviews: monthInterviews,\n offers,\n hires,\n };\n });\n }, [candidates, interviews, jobPostings]);\n\n const sourceEffectiveness = useMemo(() => {\n const grouped = candidates.reduce<Record<string, { applications: number; hires: number }>>(\n (acc, candidate) => {\n const source = candidate.source || 'Unknown';\n const value = acc[source] || { applications: 0, hires: 0 };\n value.applications += 1;\n if (candidate.status === 'hired') {\n value.hires += 1;\n }\n acc[source] = value;\n return acc;\n },\n {}\n );\n\n return Object.entries(grouped).map(([source, value]) => ({\n source,\n applications: value.applications,\n hires: value.hires,\n conversionRate: Number(((value.hires / Math.max(value.applications, 1)) * 100).toFixed(1)),\n costPerHire: Math.round(\n jobPostings.reduce((sum, posting) => sum + posting.salaryMin * 0.02, 0) /\n Math.max(value.hires, 1)\n ),\n }));\n }, [candidates, jobPostings]);\n\n const activeJobs = jobPostings.filter((j) => j.status === 'active');\n const totalApplicants = jobPostings.reduce((s, j) => s + j.applicants, 0);\n const scheduledInterviews = interviews.filter((i) => i.status === 'scheduled').length;\n const completedInterviews = interviews.filter((i) => i.status === 'completed').length;\n const offeredCandidates = candidates.filter((c) => c.status === 'offer').length;\n const hiredCandidates = candidates.filter((c) => c.status === 'hired').length;\n\n const maxApplications = Math.max(1, ...hiringMetrics.map((m) => m.applications));\n\n const statusPipeline = [\n {\n status: 'New',\n count: candidates.filter((c) => c.status === 'new').length,\n color: 'bg-text-secondary',\n },\n {\n status: 'Screening',\n count: candidates.filter((c) => c.status === 'screening').length,\n color: 'bg-status-info-text',\n },\n {\n status: 'Interview',\n count: candidates.filter((c) => c.status === 'interview').length,\n color: 'bg-status-info-text',\n },\n {\n status: 'Assessment',\n count: candidates.filter((c) => c.status === 'assessment').length,\n color: 'bg-action-primary-bg',\n },\n { status: 'Offer', count: offeredCandidates, color: 'bg-status-warning-text' },\n { status: 'Hired', count: hiredCandidates, color: 'bg-status-success-text' },\n {\n status: 'Rejected',\n count: candidates.filter((c) => c.status === 'rejected').length,\n color: 'bg-status-error-text',\n },\n ];\n const totalPipeline = statusPipeline.reduce((s, p) => s + p.count, 0);\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Hiring Analytics</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n Recruitment performance and pipeline analysis\n </p>\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Active Postings</CardTitle>\n <Briefcase className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{activeJobs.length}</div>\n <p className=\"text-xs text-text-secondary\">{totalApplicants} total applicants</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Time to Fill</CardTitle>\n <Clock className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">28 days</div>\n <div className=\"flex items-center text-xs text-status-success-text\">\n <TrendingUp className=\"h-3 w-3 mr-1\" />\n -3 days from last quarter\n </div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Interview to Offer</CardTitle>\n <Target className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">32%</div>\n <p className=\"text-xs text-text-secondary\">\n {scheduledInterviews} scheduled, {completedInterviews} completed\n </p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Offer Acceptance</CardTitle>\n <Users className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">87%</div>\n <p className=\"text-xs text-text-secondary\">{hiredCandidates} hired this quarter</p>\n </CardContent>\n </GlassCard>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-7\">\n <Card className=\"col-span-4\">\n <CardHeader>\n <CardTitle>Monthly Hiring Activity</CardTitle>\n <CardDescription>Applications, interviews, and hires over time</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {hiringMetrics.map((m) => (\n <div key={m.month} className=\"flex items-center gap-3\">\n <span className=\"w-10 text-sm text-text-secondary\">{m.month}</span>\n <div className=\"flex-1 h-5 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-5 bg-action-primary-bg rounded\"\n style={{\n width: `${Math.round((m.applications / (maxApplications + 20)) * 100)}%`,\n }}\n />\n </div>\n <div className=\"flex gap-1 w-48 justify-end\">\n <Badge variant=\"outline\" className=\"text-[10px]\">\n {m.applications} apps\n </Badge>\n <Badge variant=\"secondary\" className=\"text-[10px]\">\n {m.interviews} int\n </Badge>\n <Badge variant=\"default\" className=\"text-[10px]\">\n {m.hires} hired\n </Badge>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card className=\"col-span-3\">\n <CardHeader>\n <CardTitle>Pipeline Status</CardTitle>\n <CardDescription>Current candidate distribution</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {statusPipeline.map((stage) => (\n <div key={stage.status} className=\"flex items-center gap-3\">\n <div className={`h-3 w-3 rounded-full ${stage.color}`} />\n <span className=\"w-20 text-sm\">{stage.status}</span>\n <div className=\"flex-1 h-3 bg-bg-sunken rounded overflow-hidden\">\n <div\n className={`h-3 rounded ${stage.color}`}\n style={{\n width: `${totalPipeline > 0 ? Math.max(Math.round((stage.count / totalPipeline) * 100), 3) : 3}%`,\n }}\n />\n </div>\n <span className=\"text-sm font-medium w-6 text-right\">{stage.count}</span>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Source Effectiveness</CardTitle>\n <CardDescription>Recruitment channel performance comparison</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-5 gap-2 text-xs font-medium text-text-secondary border-b pb-2 mb-2\">\n <span>Source</span>\n <span className=\"text-right\">Applications</span>\n <span className=\"text-right\">Hires</span>\n <span className=\"text-right\">Conversion</span>\n <span className=\"text-right\">Cost/Hire</span>\n </div>\n {sourceEffectiveness\n .sort((a, b) => b.conversionRate - a.conversionRate)\n .map((src) => (\n <div\n key={src.source}\n className=\"grid grid-cols-5 gap-2 py-2 border-b last:border-0 text-sm\"\n >\n <span className=\"font-medium\">{src.source}</span>\n <span className=\"text-right\">{src.applications}</span>\n <span className=\"text-right\">{src.hires}</span>\n <span className=\"text-right\">\n <Badge\n variant={src.conversionRate >= 5 ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {src.conversionRate}%\n </Badge>\n </span>\n <span className=\"text-right\">${src.costPerHire.toLocaleString()}</span>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Open Positions Detail</CardTitle>\n <CardDescription>Active job postings and applicant counts</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-6 gap-2 text-xs font-medium text-text-secondary border-b pb-2 mb-2\">\n <span>Position</span>\n <span>Department</span>\n <span>Level</span>\n <span className=\"text-right\">Applicants</span>\n <span className=\"text-right\">Salary Range</span>\n <span className=\"text-right\">Status</span>\n </div>\n {jobPostings.map((job) => (\n <div\n key={job.id}\n className=\"grid grid-cols-6 gap-2 py-2 border-b last:border-0 text-sm\"\n >\n <span className=\"font-medium truncate\">{job.title}</span>\n <span className=\"text-text-secondary\">{job.department}</span>\n <Badge variant=\"outline\" className=\"w-fit text-[10px]\">\n {job.experienceLevel}\n </Badge>\n <span className=\"text-right\">{job.applicants}</span>\n <span className=\"text-right text-text-secondary\">\n ${(job.salaryMin / 1000).toFixed(0)}K-${(job.salaryMax / 1000).toFixed(0)}K\n </span>\n <div className=\"text-right\">\n <Badge\n variant={job.status === 'active' ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {job.status}\n </Badge>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AnalyticsHiringAnalyticsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { Briefcase, Clock, Target, TrendingUp, Users } from 'lucide-react';\nimport { useMemo } from 'react';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\nexport function AnalyticsHiringAnalyticsPagePage() {\n const { jobPostings, candidates, interviews } = useCrewFoundryStore();\n\n const hiringMetrics = useMemo(() => {\n const months = ['Sep', 'Oct', 'Nov', 'Dec', 'Jan', 'Feb'];\n return months.map((month, index) => {\n const monthInterviews = interviews.filter(\n (interview) =>\n new Date(interview.date).toLocaleString('en-US', { month: 'short' }) === month\n ).length;\n const monthCandidates = candidates.filter(\n (candidate) =>\n new Date(candidate.appliedDate).toLocaleString('en-US', { month: 'short' }) === month\n );\n const offers = monthCandidates.filter((candidate) => candidate.status === 'offer').length;\n const hires = monthCandidates.filter((candidate) => candidate.status === 'hired').length;\n const applications =\n monthCandidates.length +\n jobPostings.reduce((sum, posting) => sum + posting.applicants, 0) / 6;\n return {\n month,\n applications: Math.round(applications - index * 3),\n interviews: monthInterviews,\n offers,\n hires,\n };\n });\n }, [candidates, interviews, jobPostings]);\n\n const sourceEffectiveness = useMemo(() => {\n const grouped = candidates.reduce<Record<string, { applications: number; hires: number }>>(\n (acc, candidate) => {\n const source = candidate.source || 'Unknown';\n const value = acc[source] || { applications: 0, hires: 0 };\n value.applications += 1;\n if (candidate.status === 'hired') {\n value.hires += 1;\n }\n acc[source] = value;\n return acc;\n },\n {}\n );\n\n return Object.entries(grouped).map(([source, value]) => ({\n source,\n applications: value.applications,\n hires: value.hires,\n conversionRate: Number(((value.hires / Math.max(value.applications, 1)) * 100).toFixed(1)),\n costPerHire: Math.round(\n jobPostings.reduce((sum, posting) => sum + posting.salaryMin * 0.02, 0) /\n Math.max(value.hires, 1)\n ),\n }));\n }, [candidates, jobPostings]);\n\n const activeJobs = jobPostings.filter((j) => j.status === 'active');\n const totalApplicants = jobPostings.reduce((s, j) => s + j.applicants, 0);\n const scheduledInterviews = interviews.filter((i) => i.status === 'scheduled').length;\n const completedInterviews = interviews.filter((i) => i.status === 'completed').length;\n const offeredCandidates = candidates.filter((c) => c.status === 'offer').length;\n const hiredCandidates = candidates.filter((c) => c.status === 'hired').length;\n\n const maxApplications = Math.max(1, ...hiringMetrics.map((m) => m.applications));\n\n const statusPipeline = [\n {\n status: 'New',\n count: candidates.filter((c) => c.status === 'new').length,\n color: 'bg-text-secondary',\n },\n {\n status: 'Screening',\n count: candidates.filter((c) => c.status === 'screening').length,\n color: 'bg-status-info-text',\n },\n {\n status: 'Interview',\n count: candidates.filter((c) => c.status === 'interview').length,\n color: 'bg-status-info-text',\n },\n {\n status: 'Assessment',\n count: candidates.filter((c) => c.status === 'assessment').length,\n color: 'bg-action-primary-bg',\n },\n { status: 'Offer', count: offeredCandidates, color: 'bg-status-warning-text' },\n { status: 'Hired', count: hiredCandidates, color: 'bg-status-success-text' },\n {\n status: 'Rejected',\n count: candidates.filter((c) => c.status === 'rejected').length,\n color: 'bg-status-error-text',\n },\n ];\n const totalPipeline = statusPipeline.reduce((s, p) => s + p.count, 0);\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Hiring Analytics</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n Recruitment performance and pipeline analysis\n </p>\n <PagePurpose className=\"mt-1\">Measure how your hiring funnel performs — applications, time-to-hire, and source quality — to make recruiting faster and more effective.</PagePurpose>\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Active Postings</CardTitle>\n <Briefcase className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{activeJobs.length}</div>\n <p className=\"text-xs text-text-secondary\">{totalApplicants} total applicants</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Time to Fill</CardTitle>\n <Clock className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">28 days</div>\n <div className=\"flex items-center text-xs text-status-success-text\">\n <TrendingUp className=\"h-3 w-3 mr-1\" />\n -3 days from last quarter\n </div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Interview to Offer</CardTitle>\n <Target className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">32%</div>\n <p className=\"text-xs text-text-secondary\">\n {scheduledInterviews} scheduled, {completedInterviews} completed\n </p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Offer Acceptance</CardTitle>\n <Users className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">87%</div>\n <p className=\"text-xs text-text-secondary\">{hiredCandidates} hired this quarter</p>\n </CardContent>\n </GlassCard>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-7\">\n <Card className=\"col-span-4\">\n <CardHeader>\n <CardTitle>Monthly Hiring Activity</CardTitle>\n <CardDescription>Applications, interviews, and hires over time</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {hiringMetrics.map((m) => (\n <div key={m.month} className=\"flex items-center gap-3\">\n <span className=\"w-10 text-sm text-text-secondary\">{m.month}</span>\n <div className=\"flex-1 h-5 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-5 bg-action-primary-bg rounded\"\n style={{\n width: `${Math.round((m.applications / (maxApplications + 20)) * 100)}%`,\n }}\n />\n </div>\n <div className=\"flex gap-1 w-48 justify-end\">\n <Badge variant=\"outline\" className=\"text-[10px]\">\n {m.applications} apps\n </Badge>\n <Badge variant=\"secondary\" className=\"text-[10px]\">\n {m.interviews} int\n </Badge>\n <Badge variant=\"default\" className=\"text-[10px]\">\n {m.hires} hired\n </Badge>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card className=\"col-span-3\">\n <CardHeader>\n <CardTitle>Pipeline Status</CardTitle>\n <CardDescription>Current candidate distribution</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {statusPipeline.map((stage) => (\n <div key={stage.status} className=\"flex items-center gap-3\">\n <div className={`h-3 w-3 rounded-full ${stage.color}`} />\n <span className=\"w-20 text-sm\">{stage.status}</span>\n <div className=\"flex-1 h-3 bg-bg-sunken rounded overflow-hidden\">\n <div\n className={`h-3 rounded ${stage.color}`}\n style={{\n width: `${totalPipeline > 0 ? Math.max(Math.round((stage.count / totalPipeline) * 100), 3) : 3}%`,\n }}\n />\n </div>\n <span className=\"text-sm font-medium w-6 text-right\">{stage.count}</span>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Source Effectiveness</CardTitle>\n <CardDescription>Recruitment channel performance comparison</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-5 gap-2 text-xs font-medium text-text-secondary border-b pb-2 mb-2\">\n <span>Source</span>\n <span className=\"text-right\">Applications</span>\n <span className=\"text-right\">Hires</span>\n <span className=\"text-right\">Conversion</span>\n <span className=\"text-right\">Cost/Hire</span>\n </div>\n {sourceEffectiveness\n .sort((a, b) => b.conversionRate - a.conversionRate)\n .map((src) => (\n <div\n key={src.source}\n className=\"grid grid-cols-5 gap-2 py-2 border-b last:border-0 text-sm\"\n >\n <span className=\"font-medium\">{src.source}</span>\n <span className=\"text-right\">{src.applications}</span>\n <span className=\"text-right\">{src.hires}</span>\n <span className=\"text-right\">\n <Badge\n variant={src.conversionRate >= 5 ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {src.conversionRate}%\n </Badge>\n </span>\n <span className=\"text-right\">${src.costPerHire.toLocaleString()}</span>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Open Positions Detail</CardTitle>\n <CardDescription>Active job postings and applicant counts</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-6 gap-2 text-xs font-medium text-text-secondary border-b pb-2 mb-2\">\n <span>Position</span>\n <span>Department</span>\n <span>Level</span>\n <span className=\"text-right\">Applicants</span>\n <span className=\"text-right\">Salary Range</span>\n <span className=\"text-right\">Status</span>\n </div>\n {jobPostings.map((job) => (\n <div\n key={job.id}\n className=\"grid grid-cols-6 gap-2 py-2 border-b last:border-0 text-sm\"\n >\n <span className=\"font-medium truncate\">{job.title}</span>\n <span className=\"text-text-secondary\">{job.department}</span>\n <Badge variant=\"outline\" className=\"w-fit text-[10px]\">\n {job.experienceLevel}\n </Badge>\n <span className=\"text-right\">{job.applicants}</span>\n <span className=\"text-right text-text-secondary\">\n ${(job.salaryMin / 1000).toFixed(0)}K-${(job.salaryMax / 1000).toFixed(0)}K\n </span>\n <div className=\"text-right\">\n <Badge\n variant={job.status === 'active' ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {job.status}\n </Badge>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,33 +1,41 @@
|
|
|
1
1
|
import { useCrewFoundryStore as e } from "../store/crewfoundryStore.js";
|
|
2
2
|
import { Eye as t, Pin as n, Plus as r } from "lucide-react";
|
|
3
3
|
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
4
|
-
import { Badge as o, Button as s, Card as c, CardContent as l, CardHeader as u, CardTitle as d,
|
|
4
|
+
import { Badge as o, Button as s, Card as c, CardContent as l, CardHeader as u, CardTitle as d, GlassCard as f, GradientText as p, IllustratedEmptyState as m, PagePurpose as h } from "@burdenoff/fe-libs/ui";
|
|
5
5
|
//#region src/crewfoundry/pages/CommunicationsAnnouncementsPagePage.tsx
|
|
6
|
-
function
|
|
7
|
-
let { announcements:
|
|
6
|
+
function g() {
|
|
7
|
+
let { announcements: g } = e(), _ = g.filter((e) => e.isPinned), v = g.filter((e) => !e.isPinned);
|
|
8
8
|
return /* @__PURE__ */ a("div", {
|
|
9
9
|
className: "space-y-6 p-6",
|
|
10
10
|
children: [
|
|
11
11
|
/* @__PURE__ */ a("div", {
|
|
12
12
|
className: "flex items-center justify-between",
|
|
13
|
-
children: [/* @__PURE__ */ a("div", { children: [
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
children: [/* @__PURE__ */ a("div", { children: [
|
|
14
|
+
/* @__PURE__ */ i("h1", {
|
|
15
|
+
className: "text-2xl font-semibold",
|
|
16
|
+
children: /* @__PURE__ */ i(p, { children: "Announcements" })
|
|
17
|
+
}),
|
|
18
|
+
/* @__PURE__ */ a("p", {
|
|
19
|
+
className: "text-sm text-text-secondary",
|
|
20
|
+
children: [g.length, " company announcements"]
|
|
21
|
+
}),
|
|
22
|
+
/* @__PURE__ */ i(h, {
|
|
23
|
+
className: "mt-1",
|
|
24
|
+
children: "Broadcast company-wide news and updates so everyone stays informed — post announcements here when you have something the whole team needs to see."
|
|
25
|
+
})
|
|
26
|
+
] }), /* @__PURE__ */ a(s, {
|
|
20
27
|
size: "sm",
|
|
21
28
|
children: [/* @__PURE__ */ i(r, { className: "h-4 w-4 mr-1" }), "New Announcement"]
|
|
22
29
|
})]
|
|
23
30
|
}),
|
|
24
|
-
|
|
31
|
+
_.length > 0 && /* @__PURE__ */ a("div", {
|
|
25
32
|
className: "space-y-3",
|
|
26
33
|
children: [/* @__PURE__ */ a("h2", {
|
|
27
34
|
className: "text-sm font-medium flex items-center gap-1",
|
|
28
35
|
children: [/* @__PURE__ */ i(n, { className: "h-4 w-4" }), "Pinned"]
|
|
29
|
-
}),
|
|
30
|
-
|
|
36
|
+
}), _.map((e) => /* @__PURE__ */ a(f, {
|
|
37
|
+
treatment: "glass",
|
|
38
|
+
glow: !0,
|
|
31
39
|
children: [/* @__PURE__ */ i(u, { children: /* @__PURE__ */ a("div", {
|
|
32
40
|
className: "flex items-center justify-between",
|
|
33
41
|
children: [/* @__PURE__ */ i(d, {
|
|
@@ -64,9 +72,14 @@ function p() {
|
|
|
64
72
|
})] })]
|
|
65
73
|
}, e.id))]
|
|
66
74
|
}),
|
|
75
|
+
g.length === 0 && /* @__PURE__ */ i(m, {
|
|
76
|
+
illustration: "empty-notifications",
|
|
77
|
+
title: "No announcements yet",
|
|
78
|
+
description: "Post an announcement to share news with the whole team."
|
|
79
|
+
}),
|
|
67
80
|
/* @__PURE__ */ i("div", {
|
|
68
81
|
className: "space-y-3",
|
|
69
|
-
children:
|
|
82
|
+
children: v.map((e) => /* @__PURE__ */ a(c, { children: [/* @__PURE__ */ i(u, { children: /* @__PURE__ */ a("div", {
|
|
70
83
|
className: "flex items-center justify-between",
|
|
71
84
|
children: [/* @__PURE__ */ i(d, {
|
|
72
85
|
className: "text-base",
|
|
@@ -116,6 +129,6 @@ function p() {
|
|
|
116
129
|
});
|
|
117
130
|
}
|
|
118
131
|
//#endregion
|
|
119
|
-
export {
|
|
132
|
+
export { g as CommunicationsAnnouncementsPagePage };
|
|
120
133
|
|
|
121
134
|
//# sourceMappingURL=CommunicationsAnnouncementsPagePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommunicationsAnnouncementsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/CommunicationsAnnouncementsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n GradientText,\n} from '@burdenoff/fe-libs/ui';\nimport { Eye,
|
|
1
|
+
{"version":3,"file":"CommunicationsAnnouncementsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/CommunicationsAnnouncementsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { Eye, Pin, Plus } from 'lucide-react';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\nexport function CommunicationsAnnouncementsPagePage() {\n const { announcements } = useCrewFoundryStore();\n const pinned = announcements.filter((a) => a.isPinned);\n const regular = announcements.filter((a) => !a.isPinned);\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Announcements</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {announcements.length} company announcements\n </p>\n <PagePurpose className=\"mt-1\">Broadcast company-wide news and updates so everyone stays informed — post announcements here when you have something the whole team needs to see.</PagePurpose>\n </div>\n <Button size=\"sm\">\n <Plus className=\"h-4 w-4 mr-1\" />\n New Announcement\n </Button>\n </div>\n {pinned.length > 0 && (\n <div className=\"space-y-3\">\n <h2 className=\"text-sm font-medium flex items-center gap-1\">\n <Pin className=\"h-4 w-4\" />\n Pinned\n </h2>\n {pinned.map((ann) => (\n <GlassCard key={ann.id} treatment=\"glass\" glow>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">{ann.title}</CardTitle>\n <div className=\"flex gap-1\">\n <Badge\n variant={\n ann.priority === 'urgent'\n ? 'destructive'\n : ann.priority === 'high'\n ? 'default'\n : 'secondary'\n }\n className=\"text-[10px]\"\n >\n {ann.priority}\n </Badge>\n <Badge variant=\"outline\" className=\"text-[10px]\">\n {ann.category}\n </Badge>\n </div>\n </div>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm text-text-secondary\">{ann.content}</p>\n <div className=\"flex items-center justify-between mt-3 text-xs text-text-secondary\">\n <span>\n {ann.author} · {ann.authorRole}\n </span>\n <div className=\"flex items-center gap-3\">\n <span className=\"flex items-center gap-1\">\n <Eye className=\"h-3 w-3\" />\n {ann.viewCount}\n </span>\n <span>{ann.createdAt}</span>\n </div>\n </div>\n </CardContent>\n </GlassCard>\n ))}\n </div>\n )}\n {announcements.length === 0 && (\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title=\"No announcements yet\"\n description=\"Post an announcement to share news with the whole team.\"\n />\n )}\n <div className=\"space-y-3\">\n {regular.map((ann) => (\n <Card key={ann.id}>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">{ann.title}</CardTitle>\n <div className=\"flex gap-1\">\n <Badge\n variant={\n ann.priority === 'urgent'\n ? 'destructive'\n : ann.priority === 'high'\n ? 'default'\n : 'secondary'\n }\n className=\"text-[10px]\"\n >\n {ann.priority}\n </Badge>\n <Badge variant=\"outline\" className=\"text-[10px]\">\n {ann.category}\n </Badge>\n </div>\n </div>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm text-text-secondary\">{ann.content}</p>\n <div className=\"flex items-center justify-between mt-3 text-xs text-text-secondary\">\n <span>\n {ann.author} · {ann.authorRole}\n </span>\n <div className=\"flex items-center gap-3\">\n <span className=\"flex items-center gap-1\">\n <Eye className=\"h-3 w-3\" />\n {ann.viewCount}\n </span>\n <span>{ann.createdAt}</span>\n </div>\n </div>\n <div className=\"flex gap-1 mt-2\">\n {ann.tags.map((t) => (\n <Badge key={t} variant=\"outline\" className=\"text-[9px]\">\n {t}\n </Badge>\n ))}\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|