From f6d8b43694bdda4077a3e9f25809d835115c4304 Mon Sep 17 00:00:00 2001 From: Jake Shore Date: Thu, 12 Feb 2026 17:43:17 -0500 Subject: [PATCH] bamboohr: Build 18 React MCP Apps with complete structure - Created 18 standalone React apps in src/ui/react-app/{app-name}/ - Each app has App.tsx, index.html, and vite.config.ts - All apps use dark theme (#0f172a/#1e293b) - Apps built: employee-dashboard, employee-detail, employee-directory, time-off-calendar, time-off-requests, time-off-balances, report-builder, benefits-overview, benefits-enrollment, payroll-dashboard, goal-tracker, training-catalog, training-progress, file-manager, org-chart, headcount-analytics, turnover-report, new-hires - Each app self-contained with inline components - Client-side state management with React hooks - Tailwind CSS via CDN for styling - Unique dev server ports (3000-3017) --- .../ui/react-app/benefits-enrollment/App.tsx | 129 ++++++++++ .../react-app/benefits-enrollment/index.html | 21 ++ .../benefits-enrollment/vite.config.ts | 14 ++ .../ui/react-app/benefits-overview/App.tsx | 144 ++++++++++++ .../ui/react-app/benefits-overview/index.html | 21 ++ .../benefits-overview/vite.config.ts | 14 ++ .../ui/react-app/employee-dashboard/App.tsx | 139 +++++++++++ .../react-app/employee-dashboard/index.html | 21 ++ .../employee-dashboard/vite.config.ts | 14 ++ .../src/ui/react-app/employee-detail/App.tsx | 140 +++++++++++ .../ui/react-app/employee-detail/index.html | 21 ++ .../react-app/employee-detail/vite.config.ts | 14 ++ .../ui/react-app/employee-directory/App.tsx | 139 +++++++++++ .../react-app/employee-directory/index.html | 21 ++ .../employee-directory/vite.config.ts | 14 ++ .../src/ui/react-app/file-manager/App.tsx | 177 ++++++++++++++ .../src/ui/react-app/file-manager/index.html | 21 ++ .../ui/react-app/file-manager/vite.config.ts | 14 ++ .../src/ui/react-app/goal-tracker/App.tsx | 157 +++++++++++++ .../src/ui/react-app/goal-tracker/index.html | 21 ++ .../ui/react-app/goal-tracker/vite.config.ts | 14 ++ .../ui/react-app/headcount-analytics/App.tsx | 165 +++++++++++++ .../react-app/headcount-analytics/index.html | 21 ++ .../headcount-analytics/vite.config.ts | 14 ++ .../src/ui/react-app/new-hires/App.tsx | 222 ++++++++++++++++++ .../src/ui/react-app/new-hires/index.html | 21 ++ .../src/ui/react-app/new-hires/vite.config.ts | 14 ++ .../src/ui/react-app/org-chart/App.tsx | 183 +++++++++++++++ .../src/ui/react-app/org-chart/index.html | 21 ++ .../src/ui/react-app/org-chart/vite.config.ts | 14 ++ .../ui/react-app/payroll-dashboard/App.tsx | 165 +++++++++++++ .../ui/react-app/payroll-dashboard/index.html | 21 ++ .../payroll-dashboard/vite.config.ts | 14 ++ .../src/ui/react-app/report-builder/App.tsx | 201 ++++++++++++++++ .../ui/react-app/report-builder/index.html | 21 ++ .../react-app/report-builder/vite.config.ts | 14 ++ .../ui/react-app/time-off-balances/App.tsx | 196 ++++++++++++++++ .../ui/react-app/time-off-balances/index.html | 21 ++ .../time-off-balances/vite.config.ts | 14 ++ .../ui/react-app/time-off-calendar/App.tsx | 167 +++++++++++++ .../ui/react-app/time-off-calendar/index.html | 21 ++ .../time-off-calendar/vite.config.ts | 14 ++ .../ui/react-app/time-off-requests/App.tsx | 151 ++++++++++++ .../ui/react-app/time-off-requests/index.html | 21 ++ .../time-off-requests/vite.config.ts | 14 ++ .../src/ui/react-app/training-catalog/App.tsx | 171 ++++++++++++++ .../ui/react-app/training-catalog/index.html | 21 ++ .../react-app/training-catalog/vite.config.ts | 14 ++ .../ui/react-app/training-progress/App.tsx | 166 +++++++++++++ .../ui/react-app/training-progress/index.html | 21 ++ .../training-progress/vite.config.ts | 14 ++ .../src/ui/react-app/turnover-report/App.tsx | 184 +++++++++++++++ .../ui/react-app/turnover-report/index.html | 21 ++ .../react-app/turnover-report/vite.config.ts | 14 ++ 54 files changed, 3626 insertions(+) create mode 100644 servers/bamboohr/src/ui/react-app/benefits-enrollment/App.tsx create mode 100644 servers/bamboohr/src/ui/react-app/benefits-enrollment/index.html create mode 100644 servers/bamboohr/src/ui/react-app/benefits-enrollment/vite.config.ts create mode 100644 servers/bamboohr/src/ui/react-app/benefits-overview/App.tsx create mode 100644 servers/bamboohr/src/ui/react-app/benefits-overview/index.html create mode 100644 servers/bamboohr/src/ui/react-app/benefits-overview/vite.config.ts create mode 100644 servers/bamboohr/src/ui/react-app/employee-dashboard/App.tsx create mode 100644 servers/bamboohr/src/ui/react-app/employee-dashboard/index.html create mode 100644 servers/bamboohr/src/ui/react-app/employee-dashboard/vite.config.ts create mode 100644 servers/bamboohr/src/ui/react-app/employee-detail/App.tsx create mode 100644 servers/bamboohr/src/ui/react-app/employee-detail/index.html create mode 100644 servers/bamboohr/src/ui/react-app/employee-detail/vite.config.ts create mode 100644 servers/bamboohr/src/ui/react-app/employee-directory/App.tsx create mode 100644 servers/bamboohr/src/ui/react-app/employee-directory/index.html create mode 100644 servers/bamboohr/src/ui/react-app/employee-directory/vite.config.ts create mode 100644 servers/bamboohr/src/ui/react-app/file-manager/App.tsx create mode 100644 servers/bamboohr/src/ui/react-app/file-manager/index.html create mode 100644 servers/bamboohr/src/ui/react-app/file-manager/vite.config.ts create mode 100644 servers/bamboohr/src/ui/react-app/goal-tracker/App.tsx create mode 100644 servers/bamboohr/src/ui/react-app/goal-tracker/index.html create mode 100644 servers/bamboohr/src/ui/react-app/goal-tracker/vite.config.ts create mode 100644 servers/bamboohr/src/ui/react-app/headcount-analytics/App.tsx create mode 100644 servers/bamboohr/src/ui/react-app/headcount-analytics/index.html create mode 100644 servers/bamboohr/src/ui/react-app/headcount-analytics/vite.config.ts create mode 100644 servers/bamboohr/src/ui/react-app/new-hires/App.tsx create mode 100644 servers/bamboohr/src/ui/react-app/new-hires/index.html create mode 100644 servers/bamboohr/src/ui/react-app/new-hires/vite.config.ts create mode 100644 servers/bamboohr/src/ui/react-app/org-chart/App.tsx create mode 100644 servers/bamboohr/src/ui/react-app/org-chart/index.html create mode 100644 servers/bamboohr/src/ui/react-app/org-chart/vite.config.ts create mode 100644 servers/bamboohr/src/ui/react-app/payroll-dashboard/App.tsx create mode 100644 servers/bamboohr/src/ui/react-app/payroll-dashboard/index.html create mode 100644 servers/bamboohr/src/ui/react-app/payroll-dashboard/vite.config.ts create mode 100644 servers/bamboohr/src/ui/react-app/report-builder/App.tsx create mode 100644 servers/bamboohr/src/ui/react-app/report-builder/index.html create mode 100644 servers/bamboohr/src/ui/react-app/report-builder/vite.config.ts create mode 100644 servers/bamboohr/src/ui/react-app/time-off-balances/App.tsx create mode 100644 servers/bamboohr/src/ui/react-app/time-off-balances/index.html create mode 100644 servers/bamboohr/src/ui/react-app/time-off-balances/vite.config.ts create mode 100644 servers/bamboohr/src/ui/react-app/time-off-calendar/App.tsx create mode 100644 servers/bamboohr/src/ui/react-app/time-off-calendar/index.html create mode 100644 servers/bamboohr/src/ui/react-app/time-off-calendar/vite.config.ts create mode 100644 servers/bamboohr/src/ui/react-app/time-off-requests/App.tsx create mode 100644 servers/bamboohr/src/ui/react-app/time-off-requests/index.html create mode 100644 servers/bamboohr/src/ui/react-app/time-off-requests/vite.config.ts create mode 100644 servers/bamboohr/src/ui/react-app/training-catalog/App.tsx create mode 100644 servers/bamboohr/src/ui/react-app/training-catalog/index.html create mode 100644 servers/bamboohr/src/ui/react-app/training-catalog/vite.config.ts create mode 100644 servers/bamboohr/src/ui/react-app/training-progress/App.tsx create mode 100644 servers/bamboohr/src/ui/react-app/training-progress/index.html create mode 100644 servers/bamboohr/src/ui/react-app/training-progress/vite.config.ts create mode 100644 servers/bamboohr/src/ui/react-app/turnover-report/App.tsx create mode 100644 servers/bamboohr/src/ui/react-app/turnover-report/index.html create mode 100644 servers/bamboohr/src/ui/react-app/turnover-report/vite.config.ts diff --git a/servers/bamboohr/src/ui/react-app/benefits-enrollment/App.tsx b/servers/bamboohr/src/ui/react-app/benefits-enrollment/App.tsx new file mode 100644 index 0000000..0a0884d --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/benefits-enrollment/App.tsx @@ -0,0 +1,129 @@ +import React, { useState } from 'react'; +import { Heart, CheckCircle, XCircle, Search } from 'lucide-react'; + +interface Enrollment { + id: string; + employee: string; + plan: string; + type: string; + status: 'Enrolled' | 'Pending' | 'Declined'; + startDate: string; + premium: string; +} + +const Card: React.FC<{ children: React.ReactNode }> = ({ children }) => ( +
{children}
+); + +export default function App() { + const [searchTerm, setSearchTerm] = useState(''); + + const enrollments: Enrollment[] = [ + { id: '1', employee: 'Sarah Johnson', plan: 'Health Insurance - PPO', type: 'Health', status: 'Enrolled', startDate: '2025-01-01', premium: '$450' }, + { id: '2', employee: 'Sarah Johnson', plan: 'Dental Insurance', type: 'Dental', status: 'Enrolled', startDate: '2025-01-01', premium: '$50' }, + { id: '3', employee: 'Sarah Johnson', plan: '401(k) Plan', type: 'Retirement', status: 'Enrolled', startDate: '2025-01-01', premium: 'Varies' }, + { id: '4', employee: 'Mike Chen', plan: 'Health Insurance - HMO', type: 'Health', status: 'Enrolled', startDate: '2025-01-01', premium: '$350' }, + { id: '5', employee: 'Mike Chen', plan: 'Vision Insurance', type: 'Vision', status: 'Pending', startDate: '2025-02-01', premium: '$25' }, + { id: '6', employee: 'Emily Davis', plan: 'Health Insurance - PPO', type: 'Health', status: 'Enrolled', startDate: '2025-01-01', premium: '$450' }, + { id: '7', employee: 'Emily Davis', plan: 'Dental Insurance', type: 'Dental', status: 'Declined', startDate: 'N/A', premium: 'N/A' }, + { id: '8', employee: 'James Wilson', plan: 'Life Insurance', type: 'Life', status: 'Enrolled', startDate: '2025-01-01', premium: '$15' }, + ]; + + const filteredEnrollments = enrollments.filter(enrollment => + enrollment.employee.toLowerCase().includes(searchTerm.toLowerCase()) || + enrollment.plan.toLowerCase().includes(searchTerm.toLowerCase()) + ); + + const groupedEnrollments = filteredEnrollments.reduce((acc, enrollment) => { + if (!acc[enrollment.employee]) { + acc[enrollment.employee] = []; + } + acc[enrollment.employee].push(enrollment); + return acc; + }, {} as Record); + + return ( +
+
+

+ + Benefits Enrollment +

+ + {/* Search */} + +
+ + setSearchTerm(e.target.value)} + className="w-full pl-10 pr-4 py-2 bg-slate-700 border border-slate-600 rounded-lg text-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500" + /> +
+
+ + {/* Enrollments by Employee */} +
+ {Object.entries(groupedEnrollments).map(([employee, enrollments]) => ( + +
+
+ {employee.split(' ').map(n => n[0]).join('')} +
+
+

{employee}

+

{enrollments.length} plan{enrollments.length > 1 ? 's' : ''}

+
+
+ +
+ {enrollments.map((enrollment) => ( +
+
+
+ {enrollment.status === 'Enrolled' && } + {enrollment.status === 'Pending' && } + {enrollment.status === 'Declined' && } +

{enrollment.plan}

+
+
+
+

Type

+

{enrollment.type}

+
+
+

Start Date

+

{enrollment.startDate}

+
+
+

Premium

+

{enrollment.premium}

+
+
+
+ + {enrollment.status} + +
+ ))} +
+
+ ))} + + {Object.keys(groupedEnrollments).length === 0 && ( +
+ +

No enrollments found

+
+ )} +
+
+
+ ); +} diff --git a/servers/bamboohr/src/ui/react-app/benefits-enrollment/index.html b/servers/bamboohr/src/ui/react-app/benefits-enrollment/index.html new file mode 100644 index 0000000..fd92733 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/benefits-enrollment/index.html @@ -0,0 +1,21 @@ + + + + + + Benefits Enrollment - BambooHR + + + +
+ + + + diff --git a/servers/bamboohr/src/ui/react-app/benefits-enrollment/vite.config.ts b/servers/bamboohr/src/ui/react-app/benefits-enrollment/vite.config.ts new file mode 100644 index 0000000..4cc8510 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/benefits-enrollment/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + server: { + port: 3008, + open: true + }, + build: { + outDir: 'dist', + sourcemap: true + } +}); diff --git a/servers/bamboohr/src/ui/react-app/benefits-overview/App.tsx b/servers/bamboohr/src/ui/react-app/benefits-overview/App.tsx new file mode 100644 index 0000000..885fe00 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/benefits-overview/App.tsx @@ -0,0 +1,144 @@ +import React from 'react'; +import { Heart, Users, DollarSign, Shield } from 'lucide-react'; + +interface BenefitPlan { + id: string; + name: string; + type: string; + enrolled: number; + total: number; + cost: string; + provider: string; +} + +const Card: React.FC<{ children: React.ReactNode }> = ({ children }) => ( +
{children}
+); + +export default function App() { + const plans: BenefitPlan[] = [ + { id: '1', name: 'Health Insurance - PPO', type: 'Health', enrolled: 185, total: 245, cost: '$450/mo', provider: 'BlueCross' }, + { id: '2', name: 'Health Insurance - HMO', type: 'Health', enrolled: 45, total: 245, cost: '$350/mo', provider: 'BlueCross' }, + { id: '3', name: 'Dental Insurance', type: 'Dental', enrolled: 210, total: 245, cost: '$50/mo', provider: 'Delta Dental' }, + { id: '4', name: 'Vision Insurance', type: 'Vision', enrolled: 195, total: 245, cost: '$25/mo', provider: 'VSP' }, + { id: '5', name: '401(k) Plan', type: 'Retirement', enrolled: 220, total: 245, cost: 'Varies', provider: 'Fidelity' }, + { id: '6', name: 'Life Insurance', type: 'Life', enrolled: 245, total: 245, cost: '$15/mo', provider: 'MetLife' }, + { id: '7', name: 'Disability Insurance', type: 'Disability', enrolled: 230, total: 245, cost: '$30/mo', provider: 'Unum' }, + { id: '8', name: 'FSA', type: 'Flexible Spending', enrolled: 120, total: 245, cost: 'N/A', provider: 'WageWorks' }, + ]; + + const stats = { + totalEnrolled: plans.reduce((sum, plan) => sum + plan.enrolled, 0), + avgEnrollment: Math.round((plans.reduce((sum, plan) => sum + (plan.enrolled / plan.total), 0) / plans.length) * 100), + totalPlans: plans.length, + employees: 245 + }; + + const getIcon = (type: string) => { + switch (type) { + case 'Health': return ; + case 'Dental': return ; + case 'Vision': return ; + case 'Retirement': return ; + case 'Life': return ; + case 'Disability': return ; + default: return ; + } + }; + + const getColor = (type: string) => { + switch (type) { + case 'Health': return 'from-blue-500 to-blue-600'; + case 'Dental': return 'from-green-500 to-green-600'; + case 'Vision': return 'from-purple-500 to-purple-600'; + case 'Retirement': return 'from-yellow-500 to-yellow-600'; + case 'Life': return 'from-red-500 to-red-600'; + case 'Disability': return 'from-indigo-500 to-indigo-600'; + default: return 'from-gray-500 to-gray-600'; + } + }; + + return ( +
+
+

+ + Benefits Overview +

+ + {/* Summary Stats */} +
+ +
+
+

Total Plans

+

{stats.totalPlans}

+
+ +
+
+ +
+
+

Total Employees

+

{stats.employees}

+
+ +
+
+ +
+
+

Avg Enrollment

+

{stats.avgEnrollment}%

+
+ +
+
+ +
+
+

Total Enrollments

+

{stats.totalEnrolled}

+
+ +
+
+
+ + {/* Benefits Plans Grid */} +
+ {plans.map((plan) => { + const enrollmentPercentage = (plan.enrolled / plan.total) * 100; + return ( + +
+ {getIcon(plan.type)} +
+

{plan.name}

+

{plan.provider}

+ +
+
+ Enrollment + {plan.enrolled} / {plan.total} +
+
+
+
+
+ Cost + {plan.cost} +
+
+ + ); + })} +
+
+
+ ); +} diff --git a/servers/bamboohr/src/ui/react-app/benefits-overview/index.html b/servers/bamboohr/src/ui/react-app/benefits-overview/index.html new file mode 100644 index 0000000..33e2a57 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/benefits-overview/index.html @@ -0,0 +1,21 @@ + + + + + + Benefits Overview - BambooHR + + + +
+ + + + diff --git a/servers/bamboohr/src/ui/react-app/benefits-overview/vite.config.ts b/servers/bamboohr/src/ui/react-app/benefits-overview/vite.config.ts new file mode 100644 index 0000000..2a92264 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/benefits-overview/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + server: { + port: 3007, + open: true + }, + build: { + outDir: 'dist', + sourcemap: true + } +}); diff --git a/servers/bamboohr/src/ui/react-app/employee-dashboard/App.tsx b/servers/bamboohr/src/ui/react-app/employee-dashboard/App.tsx new file mode 100644 index 0000000..88c118e --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/employee-dashboard/App.tsx @@ -0,0 +1,139 @@ +import React, { useState, useEffect } from 'react'; +import { Users, Clock, Target, TrendingUp, TrendingDown, UserPlus } from 'lucide-react'; + +interface Stats { + totalEmployees: number; + activeEmployees: number; + newHires: number; + turnoverRate: number; +} + +const Card: React.FC<{ children: React.ReactNode; className?: string }> = ({ children, className = '' }) => ( +
{children}
+); + +const StatCard: React.FC<{ label: string; value: string | number; icon: React.ReactNode; color: string }> = + ({ label, value, icon, color }) => ( + +
+
+

{label}

+

{value}

+
+
+ {icon} +
+
+
+); + +export default function App() { + const [stats, setStats] = useState({ + totalEmployees: 245, + activeEmployees: 238, + newHires: 12, + turnoverRate: 2.8 + }); + + const [timeOffRequests] = useState([ + { id: 1, name: 'Sarah Johnson', dates: 'Jan 15 - Jan 17', status: 'Pending' }, + { id: 2, name: 'Mike Chen', dates: 'Jan 18 - Jan 20', status: 'Approved' }, + { id: 3, name: 'Emily Davis', dates: 'Jan 22 - Jan 24', status: 'Pending' }, + { id: 4, name: 'James Wilson', dates: 'Jan 25 - Jan 27', status: 'Pending' } + ]); + + const [activeGoals] = useState([ + { id: 1, title: 'Q1 Revenue Growth', progress: 75 }, + { id: 2, title: 'Customer Satisfaction', progress: 60 }, + { id: 3, title: 'Team Productivity', progress: 85 }, + { id: 4, title: 'Product Development', progress: 50 } + ]); + + return ( +
+
+

Employee Dashboard

+ + {/* Stats Grid */} +
+ } + color="bg-blue-600" + /> + } + color="bg-green-600" + /> + } + color="bg-purple-600" + /> + } + color="bg-orange-600" + /> +
+ + {/* Activity Grid */} +
+ {/* Time Off Requests */} + +

+ + Recent Time Off Requests +

+
+ {timeOffRequests.map((request) => ( +
+
+

{request.name}

+

{request.dates}

+
+ + {request.status} + +
+ ))} +
+
+ + {/* Active Goals */} + +

+ + Active Goals +

+
+ {activeGoals.map((goal) => ( +
+
+

{goal.title}

+ {goal.progress}% +
+
+
+
+
+ ))} +
+ +
+
+
+ ); +} diff --git a/servers/bamboohr/src/ui/react-app/employee-dashboard/index.html b/servers/bamboohr/src/ui/react-app/employee-dashboard/index.html new file mode 100644 index 0000000..7ad4d93 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/employee-dashboard/index.html @@ -0,0 +1,21 @@ + + + + + + Employee Dashboard - BambooHR + + + +
+ + + + diff --git a/servers/bamboohr/src/ui/react-app/employee-dashboard/vite.config.ts b/servers/bamboohr/src/ui/react-app/employee-dashboard/vite.config.ts new file mode 100644 index 0000000..cb3e436 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/employee-dashboard/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + server: { + port: 3000, + open: true + }, + build: { + outDir: 'dist', + sourcemap: true + } +}); diff --git a/servers/bamboohr/src/ui/react-app/employee-detail/App.tsx b/servers/bamboohr/src/ui/react-app/employee-detail/App.tsx new file mode 100644 index 0000000..7bddb48 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/employee-detail/App.tsx @@ -0,0 +1,140 @@ +import React, { useState } from 'react'; +import { User, Mail, Phone, Calendar, Briefcase, DollarSign, MapPin, Award } from 'lucide-react'; + +interface Employee { + id: string; + firstName: string; + lastName: string; + email: string; + phone: string; + department: string; + jobTitle: string; + hireDate: string; + location: string; + salary: string; + manager: string; + status: string; +} + +const Card: React.FC<{ children: React.ReactNode; className?: string }> = ({ children, className = '' }) => ( +
{children}
+); + +const InfoRow: React.FC<{ icon: React.ReactNode; label: string; value: string }> = ({ icon, label, value }) => ( +
+
{icon}
+
+

{label}

+

{value}

+
+
+); + +export default function App() { + const [employee] = useState({ + id: '12345', + firstName: 'Sarah', + lastName: 'Johnson', + email: 'sarah.johnson@company.com', + phone: '+1 (555) 123-4567', + department: 'Engineering', + jobTitle: 'Senior Software Engineer', + hireDate: 'January 15, 2020', + location: 'San Francisco, CA', + salary: '$125,000', + manager: 'John Smith', + status: 'Active' + }); + + const customFields = [ + { label: 'Employee Type', value: 'Full-time' }, + { label: 'Work Schedule', value: 'Monday - Friday, 9:00 AM - 5:00 PM' }, + { label: 'Emergency Contact', value: 'Michael Johnson - +1 (555) 987-6543' }, + { label: 'T-Shirt Size', value: 'Medium' } + ]; + + return ( +
+
+ {/* Header */} +
+
+
+ {employee.firstName[0]}{employee.lastName[0]} +
+
+

+ {employee.firstName} {employee.lastName} +

+

{employee.jobTitle}

+
+
+ + {employee.status} + +
+ + {/* Main Info Grid */} +
+ +

Contact Information

+
+ } label="Email" value={employee.email} /> + } label="Phone" value={employee.phone} /> + } label="Location" value={employee.location} /> +
+
+ + +

Job Information

+
+ } label="Department" value={employee.department} /> + } label="Manager" value={employee.manager} /> + } label="Hire Date" value={employee.hireDate} /> +
+
+
+ + {/* Compensation */} + +

+ + Compensation +

+
+
+

Annual Salary

+

{employee.salary}

+
+
+

Last Review

+

Dec 2024

+
+
+

Next Review

+

Jun 2025

+
+
+
+ + {/* Custom Fields */} + +

+ + Additional Information +

+
+ {customFields.map((field, idx) => ( +
+

{field.label}

+

{field.value}

+
+ ))} +
+
+
+
+ ); +} diff --git a/servers/bamboohr/src/ui/react-app/employee-detail/index.html b/servers/bamboohr/src/ui/react-app/employee-detail/index.html new file mode 100644 index 0000000..ae5f519 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/employee-detail/index.html @@ -0,0 +1,21 @@ + + + + + + Employee Detail - BambooHR + + + +
+ + + + diff --git a/servers/bamboohr/src/ui/react-app/employee-detail/vite.config.ts b/servers/bamboohr/src/ui/react-app/employee-detail/vite.config.ts new file mode 100644 index 0000000..6be4ea1 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/employee-detail/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + server: { + port: 3001, + open: true + }, + build: { + outDir: 'dist', + sourcemap: true + } +}); diff --git a/servers/bamboohr/src/ui/react-app/employee-directory/App.tsx b/servers/bamboohr/src/ui/react-app/employee-directory/App.tsx new file mode 100644 index 0000000..9a94d40 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/employee-directory/App.tsx @@ -0,0 +1,139 @@ +import React, { useState, useMemo } from 'react'; +import { Search, Filter, Mail, Phone } from 'lucide-react'; + +interface Employee { + id: string; + name: string; + title: string; + department: string; + email: string; + phone: string; + location: string; + status: string; +} + +const Card: React.FC<{ children: React.ReactNode }> = ({ children }) => ( +
{children}
+); + +export default function App() { + const [searchTerm, setSearchTerm] = useState(''); + const [filterDept, setFilterDept] = useState('All'); + const [filterStatus, setFilterStatus] = useState('All'); + + const employees: Employee[] = [ + { id: '1', name: 'Sarah Johnson', title: 'Senior Software Engineer', department: 'Engineering', email: 'sarah.j@company.com', phone: '555-0101', location: 'San Francisco', status: 'Active' }, + { id: '2', name: 'Mike Chen', title: 'Product Manager', department: 'Product', email: 'mike.c@company.com', phone: '555-0102', location: 'New York', status: 'Active' }, + { id: '3', name: 'Emily Davis', title: 'UX Designer', department: 'Design', email: 'emily.d@company.com', phone: '555-0103', location: 'Austin', status: 'Active' }, + { id: '4', name: 'James Wilson', title: 'DevOps Engineer', department: 'Engineering', email: 'james.w@company.com', phone: '555-0104', location: 'Seattle', status: 'Active' }, + { id: '5', name: 'Lisa Anderson', title: 'Marketing Director', department: 'Marketing', email: 'lisa.a@company.com', phone: '555-0105', location: 'Boston', status: 'Active' }, + { id: '6', name: 'David Brown', title: 'Sales Manager', department: 'Sales', email: 'david.b@company.com', phone: '555-0106', location: 'Chicago', status: 'Active' }, + { id: '7', name: 'Jennifer Taylor', title: 'HR Manager', department: 'HR', email: 'jen.t@company.com', phone: '555-0107', location: 'Denver', status: 'Active' }, + { id: '8', name: 'Robert Martinez', title: 'Software Engineer', department: 'Engineering', email: 'rob.m@company.com', phone: '555-0108', location: 'San Francisco', status: 'Inactive' } + ]; + + const departments = ['All', ...Array.from(new Set(employees.map(e => e.department)))]; + + const filteredEmployees = useMemo(() => { + return employees.filter(emp => { + const matchesSearch = emp.name.toLowerCase().includes(searchTerm.toLowerCase()) || + emp.title.toLowerCase().includes(searchTerm.toLowerCase()) || + emp.email.toLowerCase().includes(searchTerm.toLowerCase()); + const matchesDept = filterDept === 'All' || emp.department === filterDept; + const matchesStatus = filterStatus === 'All' || emp.status === filterStatus; + return matchesSearch && matchesDept && matchesStatus; + }); + }, [searchTerm, filterDept, filterStatus, employees]); + + return ( +
+
+

Employee Directory

+ + {/* Search and Filters */} + +
+
+ + setSearchTerm(e.target.value)} + className="w-full pl-10 pr-4 py-2 bg-slate-700 border border-slate-600 rounded-lg text-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500" + /> +
+ + +
+ +

+ Showing {filteredEmployees.length} of {employees.length} employees +

+
+ + {/* Employee Grid */} +
+ {filteredEmployees.map((employee) => ( + +
+
+ {employee.name.split(' ').map(n => n[0]).join('')} +
+
+

{employee.name}

+

{employee.title}

+

{employee.department}

+
+
+ +
+ +
+ + {employee.phone} +
+
+ +
+ {employee.location} + + {employee.status} + +
+
+ ))} +
+ + {filteredEmployees.length === 0 && ( +
+

No employees found matching your criteria

+
+ )} +
+
+ ); +} diff --git a/servers/bamboohr/src/ui/react-app/employee-directory/index.html b/servers/bamboohr/src/ui/react-app/employee-directory/index.html new file mode 100644 index 0000000..cdb6535 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/employee-directory/index.html @@ -0,0 +1,21 @@ + + + + + + Employee Directory - BambooHR + + + +
+ + + + diff --git a/servers/bamboohr/src/ui/react-app/employee-directory/vite.config.ts b/servers/bamboohr/src/ui/react-app/employee-directory/vite.config.ts new file mode 100644 index 0000000..c639ddd --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/employee-directory/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + server: { + port: 3002, + open: true + }, + build: { + outDir: 'dist', + sourcemap: true + } +}); diff --git a/servers/bamboohr/src/ui/react-app/file-manager/App.tsx b/servers/bamboohr/src/ui/react-app/file-manager/App.tsx new file mode 100644 index 0000000..8a87236 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/file-manager/App.tsx @@ -0,0 +1,177 @@ +import React, { useState } from 'react'; +import { FileText, Folder, Download, Upload, Search, Calendar } from 'lucide-react'; + +interface FileItem { + id: string; + name: string; + type: 'file' | 'folder'; + category: string; + employee: string; + size?: string; + uploadDate: string; +} + +const Card: React.FC<{ children: React.ReactNode }> = ({ children }) => ( +
{children}
+); + +export default function App() { + const [searchTerm, setSearchTerm] = useState(''); + const [filterCategory, setFilterCategory] = useState('All'); + + const files: FileItem[] = [ + { id: '1', name: 'Employee Handbook 2025', type: 'file', category: 'Policies', employee: 'HR Department', size: '2.5 MB', uploadDate: '2025-01-01' }, + { id: '2', name: 'Tax Documents', type: 'folder', category: 'Tax', employee: 'Sarah Johnson', uploadDate: '2025-01-15' }, + { id: '3', name: 'W-2 Form 2024', type: 'file', category: 'Tax', employee: 'Sarah Johnson', size: '156 KB', uploadDate: '2025-01-15' }, + { id: '4', name: 'Performance Review Q4', type: 'file', category: 'Reviews', employee: 'Mike Chen', size: '890 KB', uploadDate: '2024-12-20' }, + { id: '5', name: 'Benefits Enrollment', type: 'folder', category: 'Benefits', employee: 'Emily Davis', uploadDate: '2024-11-01' }, + { id: '6', name: 'Training Certificates', type: 'folder', category: 'Training', employee: 'James Wilson', uploadDate: '2024-12-10' }, + { id: '7', name: 'Employment Contract', type: 'file', category: 'Contracts', employee: 'Lisa Anderson', size: '1.2 MB', uploadDate: '2024-06-15' }, + { id: '8', name: 'Time Off Request Form', type: 'file', category: 'Forms', employee: 'David Brown', size: '450 KB', uploadDate: '2025-01-08' }, + ]; + + const categories = ['All', ...Array.from(new Set(files.map(f => f.category)))]; + + const filteredFiles = files.filter(file => { + const matchesSearch = file.name.toLowerCase().includes(searchTerm.toLowerCase()) || + file.employee.toLowerCase().includes(searchTerm.toLowerCase()); + const matchesCategory = filterCategory === 'All' || file.category === filterCategory; + return matchesSearch && matchesCategory; + }); + + const stats = { + totalFiles: files.filter(f => f.type === 'file').length, + totalFolders: files.filter(f => f.type === 'folder').length, + recentUploads: files.filter(f => new Date(f.uploadDate) >= new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)).length + }; + + return ( +
+
+

+ + File Manager +

+ + {/* Stats */} +
+ +
+
+

Total Files

+

{stats.totalFiles}

+
+ +
+
+ +
+
+

Total Folders

+

{stats.totalFolders}

+
+ +
+
+ +
+
+

Recent Uploads

+

{stats.recentUploads}

+
+ +
+
+
+ + {/* Toolbar */} + +
+
+
+ + setSearchTerm(e.target.value)} + className="w-full pl-10 pr-4 py-2 bg-slate-700 border border-slate-600 rounded-lg text-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500" + /> +
+ +
+ +
+
+ + {/* Files List */} +
+ {filteredFiles.map((file) => ( + +
+
+
+ {file.type === 'folder' ? ( + + ) : ( + + )} +
+
+

{file.name}

+
+ {file.employee} + + {file.category} + {file.size && ( + <> + + {file.size} + + )} +
+
+
+
+
+

+ + {file.uploadDate} +

+
+ {file.type === 'file' && ( + + )} +
+
+
+ ))} + + {filteredFiles.length === 0 && ( +
+ +

No files found

+
+ )} +
+
+
+ ); +} diff --git a/servers/bamboohr/src/ui/react-app/file-manager/index.html b/servers/bamboohr/src/ui/react-app/file-manager/index.html new file mode 100644 index 0000000..55aa801 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/file-manager/index.html @@ -0,0 +1,21 @@ + + + + + + File Manager - BambooHR + + + +
+ + + + diff --git a/servers/bamboohr/src/ui/react-app/file-manager/vite.config.ts b/servers/bamboohr/src/ui/react-app/file-manager/vite.config.ts new file mode 100644 index 0000000..bddbbb6 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/file-manager/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + server: { + port: 3013, + open: true + }, + build: { + outDir: 'dist', + sourcemap: true + } +}); diff --git a/servers/bamboohr/src/ui/react-app/goal-tracker/App.tsx b/servers/bamboohr/src/ui/react-app/goal-tracker/App.tsx new file mode 100644 index 0000000..1e090b3 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/goal-tracker/App.tsx @@ -0,0 +1,157 @@ +import React, { useState } from 'react'; +import { Target, TrendingUp, Clock, CheckCircle } from 'lucide-react'; + +interface Goal { + id: string; + employee: string; + title: string; + description: string; + progress: number; + dueDate: string; + status: 'On Track' | 'At Risk' | 'Completed'; + category: string; +} + +const Card: React.FC<{ children: React.ReactNode }> = ({ children }) => ( +
{children}
+); + +export default function App() { + const goals: Goal[] = [ + { id: '1', employee: 'Sarah Johnson', title: 'Q1 Revenue Growth', description: 'Increase quarterly revenue by 15%', progress: 75, dueDate: '2025-03-31', status: 'On Track', category: 'Performance' }, + { id: '2', employee: 'Sarah Johnson', title: 'Team Leadership', description: 'Lead 2 major projects', progress: 50, dueDate: '2025-06-30', status: 'On Track', category: 'Leadership' }, + { id: '3', employee: 'Mike Chen', title: 'Product Launch', description: 'Launch new product feature', progress: 90, dueDate: '2025-02-15', status: 'On Track', category: 'Development' }, + { id: '4', employee: 'Mike Chen', title: 'Code Quality', description: 'Reduce bug count by 30%', progress: 30, dueDate: '2025-04-30', status: 'At Risk', category: 'Quality' }, + { id: '5', employee: 'Emily Davis', title: 'Design System', description: 'Complete design system overhaul', progress: 100, dueDate: '2025-01-15', status: 'Completed', category: 'Design' }, + { id: '6', employee: 'James Wilson', title: 'Infrastructure Upgrade', description: 'Migrate to new cloud infrastructure', progress: 65, dueDate: '2025-05-31', status: 'On Track', category: 'Infrastructure' }, + ]; + + const stats = { + total: goals.length, + onTrack: goals.filter(g => g.status === 'On Track').length, + atRisk: goals.filter(g => g.status === 'At Risk').length, + completed: goals.filter(g => g.status === 'Completed').length + }; + + const groupedGoals = goals.reduce((acc, goal) => { + if (!acc[goal.employee]) { + acc[goal.employee] = []; + } + acc[goal.employee].push(goal); + return acc; + }, {} as Record); + + return ( +
+
+

+ + Goal Tracker +

+ + {/* Stats */} +
+ +
+
+

Total Goals

+

{stats.total}

+
+ +
+
+ +
+
+

On Track

+

{stats.onTrack}

+
+ +
+
+ +
+
+

At Risk

+

{stats.atRisk}

+
+ +
+
+ +
+
+

Completed

+

{stats.completed}

+
+ +
+
+
+ + {/* Goals by Employee */} +
+ {Object.entries(groupedGoals).map(([employee, employeeGoals]) => ( + +
+
+ {employee.split(' ').map(n => n[0]).join('')} +
+
+

{employee}

+

{employeeGoals.length} goal{employeeGoals.length > 1 ? 's' : ''}

+
+
+ +
+ {employeeGoals.map((goal) => ( +
+
+
+

{goal.title}

+

{goal.description}

+
+ + + Due: {goal.dueDate} + + + {goal.category} + +
+
+ + {goal.status} + +
+ +
+
+ Progress + {goal.progress}% +
+
+
+
+
+
+ ))} +
+ + ))} +
+
+
+ ); +} diff --git a/servers/bamboohr/src/ui/react-app/goal-tracker/index.html b/servers/bamboohr/src/ui/react-app/goal-tracker/index.html new file mode 100644 index 0000000..a3d82c3 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/goal-tracker/index.html @@ -0,0 +1,21 @@ + + + + + + Goal Tracker - BambooHR + + + +
+ + + + diff --git a/servers/bamboohr/src/ui/react-app/goal-tracker/vite.config.ts b/servers/bamboohr/src/ui/react-app/goal-tracker/vite.config.ts new file mode 100644 index 0000000..ba3ac50 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/goal-tracker/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + server: { + port: 3010, + open: true + }, + build: { + outDir: 'dist', + sourcemap: true + } +}); diff --git a/servers/bamboohr/src/ui/react-app/headcount-analytics/App.tsx b/servers/bamboohr/src/ui/react-app/headcount-analytics/App.tsx new file mode 100644 index 0000000..56b3465 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/headcount-analytics/App.tsx @@ -0,0 +1,165 @@ +import React from 'react'; +import { Users, TrendingUp, Building, Calendar } from 'lucide-react'; + +const Card: React.FC<{ children: React.ReactNode }> = ({ children }) => ( +
{children}
+); + +export default function App() { + const monthlyData = [ + { month: 'Jul 2024', headcount: 220, hires: 5, terminations: 2 }, + { month: 'Aug 2024', headcount: 225, hires: 7, terminations: 2 }, + { month: 'Sep 2024', headcount: 230, hires: 6, terminations: 1 }, + { month: 'Oct 2024', headcount: 235, hires: 8, terminations: 3 }, + { month: 'Nov 2024', headcount: 238, hires: 5, terminations: 2 }, + { month: 'Dec 2024', headcount: 242, hires: 6, terminations: 2 }, + { month: 'Jan 2025', headcount: 245, hires: 4, terminations: 1 } + ]; + + const departmentBreakdown = [ + { dept: 'Engineering', count: 85, change: '+5' }, + { dept: 'Product', count: 32, change: '+2' }, + { dept: 'Design', count: 18, change: '+1' }, + { dept: 'Marketing', count: 28, change: '+3' }, + { dept: 'Sales', count: 45, change: '+4' }, + { dept: 'HR', count: 12, change: '0' }, + { dept: 'Finance', count: 15, change: '+1' }, + { dept: 'Operations', count: 10, change: '0' } + ]; + + const currentHeadcount = monthlyData[monthlyData.length - 1].headcount; + const previousHeadcount = monthlyData[0].headcount; + const growthRate = ((currentHeadcount - previousHeadcount) / previousHeadcount * 100).toFixed(1); + + const maxHeadcount = Math.max(...monthlyData.map(d => d.headcount)); + + return ( +
+
+

+ + Headcount Analytics +

+ + {/* Stats */} +
+ +
+
+

Current Headcount

+

{currentHeadcount}

+
+ +
+
+ +
+
+

6-Month Growth

+

+{growthRate}%

+
+ +
+
+ +
+
+

Departments

+

{departmentBreakdown.length}

+
+ +
+
+ +
+
+

Net Change

+

+{currentHeadcount - previousHeadcount}

+
+ +
+
+
+ +
+ {/* Trend Chart */} +
+ +

Headcount Trend

+
+ {monthlyData.map((data, idx) => { + const barWidth = (data.headcount / maxHeadcount) * 100; + return ( +
+
+ {data.month} + {data.headcount} +
+
+
+
+ +{data.hires} + -{data.terminations} +
+
+
+
+ ); + })} +
+
+
+
+ New Hires +
+
+
+ Terminations +
+
+
+
+ + {/* Department Breakdown */} +
+ +

By Department

+
+ {departmentBreakdown.map((dept, idx) => { + const percentage = (dept.count / currentHeadcount) * 100; + return ( +
+
+ {dept.dept} +
+ {dept.count} + + {dept.change} + +
+
+
+
+
+

{percentage.toFixed(1)}% of total

+
+ ); + })} +
+ +
+
+
+
+ ); +} diff --git a/servers/bamboohr/src/ui/react-app/headcount-analytics/index.html b/servers/bamboohr/src/ui/react-app/headcount-analytics/index.html new file mode 100644 index 0000000..5726542 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/headcount-analytics/index.html @@ -0,0 +1,21 @@ + + + + + + Headcount Analytics - BambooHR + + + +
+ + + + diff --git a/servers/bamboohr/src/ui/react-app/headcount-analytics/vite.config.ts b/servers/bamboohr/src/ui/react-app/headcount-analytics/vite.config.ts new file mode 100644 index 0000000..20bc2f5 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/headcount-analytics/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + server: { + port: 3015, + open: true + }, + build: { + outDir: 'dist', + sourcemap: true + } +}); diff --git a/servers/bamboohr/src/ui/react-app/new-hires/App.tsx b/servers/bamboohr/src/ui/react-app/new-hires/App.tsx new file mode 100644 index 0000000..c56d3ce --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/new-hires/App.tsx @@ -0,0 +1,222 @@ +import React, { useState } from 'react'; +import { UserPlus, Calendar, CheckCircle, Clock, AlertCircle } from 'lucide-react'; + +interface NewHire { + id: string; + name: string; + title: string; + department: string; + startDate: string; + manager: string; + onboardingProgress: number; + status: 'Scheduled' | 'In Progress' | 'Completed'; + daysUntilStart?: number; +} + +const Card: React.FC<{ children: React.ReactNode }> = ({ children }) => ( +
{children}
+); + +export default function App() { + const [filterStatus, setFilterStatus] = useState('All'); + + const newHires: NewHire[] = [ + { id: '1', name: 'Alex Martinez', title: 'Software Engineer', department: 'Engineering', startDate: '2025-01-20', manager: 'Sarah Johnson', onboardingProgress: 100, status: 'Completed', daysUntilStart: 0 }, + { id: '2', name: 'Rachel Green', title: 'Product Designer', department: 'Design', startDate: '2025-02-01', manager: 'Emily Davis', onboardingProgress: 65, status: 'In Progress', daysUntilStart: 12 }, + { id: '3', name: 'Tom Harris', title: 'Marketing Specialist', department: 'Marketing', startDate: '2025-02-05', manager: 'Lisa Anderson', onboardingProgress: 30, status: 'In Progress', daysUntilStart: 16 }, + { id: '4', name: 'Jessica Lee', title: 'Sales Representative', department: 'Sales', startDate: '2025-02-10', manager: 'David Brown', onboardingProgress: 0, status: 'Scheduled', daysUntilStart: 21 }, + { id: '5', name: 'Kevin Wu', title: 'Data Analyst', department: 'Engineering', startDate: '2025-02-15', manager: 'Mike Chen', onboardingProgress: 0, status: 'Scheduled', daysUntilStart: 26 }, + { id: '6', name: 'Maria Garcia', title: 'HR Coordinator', department: 'HR', startDate: '2025-01-15', manager: 'Jennifer Taylor', onboardingProgress: 100, status: 'Completed', daysUntilStart: 0 }, + ]; + + const filteredHires = filterStatus === 'All' + ? newHires + : newHires.filter(h => h.status === filterStatus); + + const stats = { + total: newHires.length, + scheduled: newHires.filter(h => h.status === 'Scheduled').length, + inProgress: newHires.filter(h => h.status === 'In Progress').length, + completed: newHires.filter(h => h.status === 'Completed').length + }; + + const onboardingTasks = [ + 'Complete HR paperwork', + 'Set up workstation', + 'IT account creation', + 'Benefits enrollment', + 'Team introductions', + 'First week training' + ]; + + return ( +
+
+

+ + New Hires +

+ + {/* Stats */} +
+ +
+
+

Total New Hires

+

{stats.total}

+
+ +
+
+ +
+
+

Scheduled

+

{stats.scheduled}

+
+ +
+
+ +
+
+

In Progress

+

{stats.inProgress}

+
+ +
+
+ +
+
+

Completed

+

{stats.completed}

+
+ +
+
+
+ +
+ {/* New Hires List */} +
+ +
+

Upcoming & Recent Hires

+ +
+ +
+ {filteredHires.map((hire) => ( +
+
+
+
+ {hire.name.split(' ').map(n => n[0]).join('')} +
+
+

{hire.name}

+

{hire.title}

+
+
+ + {hire.status} + +
+ +
+
+

Department

+

{hire.department}

+
+
+

Manager

+

{hire.manager}

+
+
+

Start Date

+

{hire.startDate}

+
+
+

Days Until Start

+

+ {hire.daysUntilStart === 0 ? 'Started' : `${hire.daysUntilStart} days`} +

+
+
+ +
+
+ Onboarding Progress + {hire.onboardingProgress}% +
+
+
+
+
+
+ ))} +
+ +
+ + {/* Onboarding Checklist */} +
+ +

Onboarding Checklist

+
+ {onboardingTasks.map((task, idx) => ( +
+ + {task} +
+ ))} +
+
+ + +

+ + Quick Stats +

+
+
+ This Month + 3 hires +
+
+ Next Month + 3 hires +
+
+ Avg Onboarding Time + 14 days +
+
+
+
+
+
+
+ ); +} diff --git a/servers/bamboohr/src/ui/react-app/new-hires/index.html b/servers/bamboohr/src/ui/react-app/new-hires/index.html new file mode 100644 index 0000000..b0467ef --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/new-hires/index.html @@ -0,0 +1,21 @@ + + + + + + New Hires - BambooHR + + + +
+ + + + diff --git a/servers/bamboohr/src/ui/react-app/new-hires/vite.config.ts b/servers/bamboohr/src/ui/react-app/new-hires/vite.config.ts new file mode 100644 index 0000000..0ba7dfe --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/new-hires/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + server: { + port: 3017, + open: true + }, + build: { + outDir: 'dist', + sourcemap: true + } +}); diff --git a/servers/bamboohr/src/ui/react-app/org-chart/App.tsx b/servers/bamboohr/src/ui/react-app/org-chart/App.tsx new file mode 100644 index 0000000..4ecbaab --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/org-chart/App.tsx @@ -0,0 +1,183 @@ +import React, { useState } from 'react'; +import { Network, Users, ChevronDown, ChevronRight } from 'lucide-react'; + +interface OrgNode { + id: string; + name: string; + title: string; + department: string; + reports: OrgNode[]; +} + +const Card: React.FC<{ children: React.ReactNode }> = ({ children }) => ( +
{children}
+); + +const OrgCard: React.FC<{ node: OrgNode; depth: number }> = ({ node, depth }) => { + const [isExpanded, setIsExpanded] = useState(true); + + return ( +
+
0 ? 'ml-8' : ''}`}> + {node.reports.length > 0 && ( + + )} +
+
+
+ {node.name.split(' ').map(n => n[0]).join('')} +
+
+

{node.name}

+

{node.title}

+ {node.reports.length > 0 && ( +

+ {node.reports.length} direct report{node.reports.length > 1 ? 's' : ''} +

+ )} +
+
+
+
+ {isExpanded && node.reports.length > 0 && ( +
+ {node.reports.map((report) => ( + + ))} +
+ )} +
+ ); +}; + +export default function App() { + const orgData: OrgNode = { + id: '1', + name: 'Jennifer Taylor', + title: 'CEO', + department: 'Executive', + reports: [ + { + id: '2', + name: 'John Smith', + title: 'CTO', + department: 'Technology', + reports: [ + { + id: '5', + name: 'Sarah Johnson', + title: 'Engineering Manager', + department: 'Engineering', + reports: [ + { id: '8', name: 'Mike Chen', title: 'Senior Engineer', department: 'Engineering', reports: [] }, + { id: '9', name: 'James Wilson', title: 'DevOps Engineer', department: 'Engineering', reports: [] } + ] + }, + { + id: '6', + name: 'Emily Davis', + title: 'Design Lead', + department: 'Design', + reports: [ + { id: '10', name: 'Alex Martinez', title: 'UX Designer', department: 'Design', reports: [] } + ] + } + ] + }, + { + id: '3', + name: 'Lisa Anderson', + title: 'VP Marketing', + department: 'Marketing', + reports: [ + { id: '7', name: 'David Brown', title: 'Marketing Manager', department: 'Marketing', reports: [] }, + { id: '11', name: 'Rachel Green', title: 'Content Manager', department: 'Marketing', reports: [] } + ] + }, + { + id: '4', + name: 'Robert Martinez', + title: 'CFO', + department: 'Finance', + reports: [ + { id: '12', name: 'Tom Harris', title: 'Controller', department: 'Finance', reports: [] } + ] + } + ] + }; + + const countEmployees = (node: OrgNode): number => { + return 1 + node.reports.reduce((sum, report) => sum + countEmployees(report), 0); + }; + + const countManagers = (node: OrgNode): number => { + return (node.reports.length > 0 ? 1 : 0) + + node.reports.reduce((sum, report) => sum + countManagers(report), 0); + }; + + const stats = { + totalEmployees: countEmployees(orgData), + totalManagers: countManagers(orgData), + departments: 5 + }; + + return ( +
+
+

+ + Organization Chart +

+ + {/* Stats */} +
+ +
+
+

Total Employees

+

{stats.totalEmployees}

+
+ +
+
+ +
+
+

Managers

+

{stats.totalManagers}

+
+ +
+
+ +
+
+

Departments

+

{stats.departments}

+
+ +
+
+
+ + {/* Organization Tree */} + +
+

Organizational Hierarchy

+

Click the arrows to expand/collapse

+
+ +
+
+
+ ); +} diff --git a/servers/bamboohr/src/ui/react-app/org-chart/index.html b/servers/bamboohr/src/ui/react-app/org-chart/index.html new file mode 100644 index 0000000..d0aa5c6 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/org-chart/index.html @@ -0,0 +1,21 @@ + + + + + + Org Chart - BambooHR + + + +
+ + + + diff --git a/servers/bamboohr/src/ui/react-app/org-chart/vite.config.ts b/servers/bamboohr/src/ui/react-app/org-chart/vite.config.ts new file mode 100644 index 0000000..3f42ebc --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/org-chart/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + server: { + port: 3014, + open: true + }, + build: { + outDir: 'dist', + sourcemap: true + } +}); diff --git a/servers/bamboohr/src/ui/react-app/payroll-dashboard/App.tsx b/servers/bamboohr/src/ui/react-app/payroll-dashboard/App.tsx new file mode 100644 index 0000000..e8de903 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/payroll-dashboard/App.tsx @@ -0,0 +1,165 @@ +import React, { useState } from 'react'; +import { DollarSign, TrendingUp, FileText, Calendar } from 'lucide-react'; + +interface PayStub { + id: string; + period: string; + grossPay: number; + netPay: number; + deductions: number; + date: string; +} + +const Card: React.FC<{ children: React.ReactNode }> = ({ children }) => ( +
{children}
+); + +export default function App() { + const payStubs: PayStub[] = [ + { id: '1', period: 'Jan 1-15, 2025', grossPay: 5200, netPay: 3850, deductions: 1350, date: '2025-01-15' }, + { id: '2', period: 'Dec 16-31, 2024', grossPay: 5200, netPay: 3850, deductions: 1350, date: '2024-12-31' }, + { id: '3', period: 'Dec 1-15, 2024', grossPay: 5200, netPay: 3850, deductions: 1350, date: '2024-12-15' }, + { id: '4', period: 'Nov 16-30, 2024', grossPay: 5200, netPay: 3850, deductions: 1350, date: '2024-11-30' }, + ]; + + const deductionsBreakdown = [ + { name: 'Federal Tax', amount: 780 }, + { name: 'State Tax', amount: 310 }, + { name: 'Social Security', amount: 120 }, + { name: 'Medicare', amount: 80 }, + { name: 'Health Insurance', amount: 450 }, + { name: '401(k)', amount: 260 }, + ]; + + const ytdStats = { + grossPay: 10400, + netPay: 7700, + totalDeductions: 2700, + avgDeductions: 1350 + }; + + return ( +
+
+

+ + Payroll Dashboard +

+ + {/* YTD Stats */} +
+ +
+

YTD Gross Pay

+ +
+

${ytdStats.grossPay.toLocaleString()}

+
+ +
+

YTD Net Pay

+ +
+

${ytdStats.netPay.toLocaleString()}

+
+ +
+

Total Deductions

+ +
+

${ytdStats.totalDeductions.toLocaleString()}

+
+ +
+

Avg Deductions

+ +
+

${ytdStats.avgDeductions.toLocaleString()}

+
+
+ +
+ {/* Pay Stubs */} +
+

Recent Pay Stubs

+
+ {payStubs.map((stub) => ( + +
+
+

{stub.period}

+

Paid on {stub.date}

+
+ +
+
+
+

Gross Pay

+

${stub.grossPay.toLocaleString()}

+
+
+

Deductions

+

-${stub.deductions.toLocaleString()}

+
+
+

Net Pay

+

${stub.netPay.toLocaleString()}

+
+
+
+ ))} +
+
+ + {/* Deductions Breakdown */} +
+

Deductions Breakdown

+ +
+ {deductionsBreakdown.map((deduction, idx) => ( +
+
+ {deduction.name} + ${deduction.amount} +
+
+
+
+
+ ))} +
+
+ Total + + ${deductionsBreakdown.reduce((sum, d) => sum + d.amount, 0)} + +
+
+
+ + + {/* Tax Documents */} + +

Tax Documents

+
+ + +
+
+
+
+
+
+ ); +} diff --git a/servers/bamboohr/src/ui/react-app/payroll-dashboard/index.html b/servers/bamboohr/src/ui/react-app/payroll-dashboard/index.html new file mode 100644 index 0000000..3f55686 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/payroll-dashboard/index.html @@ -0,0 +1,21 @@ + + + + + + Payroll Dashboard - BambooHR + + + +
+ + + + diff --git a/servers/bamboohr/src/ui/react-app/payroll-dashboard/vite.config.ts b/servers/bamboohr/src/ui/react-app/payroll-dashboard/vite.config.ts new file mode 100644 index 0000000..3e63e3f --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/payroll-dashboard/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + server: { + port: 3009, + open: true + }, + build: { + outDir: 'dist', + sourcemap: true + } +}); diff --git a/servers/bamboohr/src/ui/react-app/report-builder/App.tsx b/servers/bamboohr/src/ui/react-app/report-builder/App.tsx new file mode 100644 index 0000000..52b8f9e --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/report-builder/App.tsx @@ -0,0 +1,201 @@ +import React, { useState } from 'react'; +import { FileText, Plus, Trash2, Download } from 'lucide-react'; + +interface ReportField { + id: string; + name: string; + type: string; +} + +const Card: React.FC<{ children: React.ReactNode }> = ({ children }) => ( +
{children}
+); + +export default function App() { + const [reportName, setReportName] = useState('Custom Employee Report'); + const [selectedFields, setSelectedFields] = useState([ + { id: '1', name: 'Employee Name', type: 'text' }, + { id: '2', name: 'Department', type: 'text' }, + { id: '3', name: 'Hire Date', type: 'date' } + ]); + + const availableFields: ReportField[] = [ + { id: '4', name: 'Job Title', type: 'text' }, + { id: '5', name: 'Email', type: 'text' }, + { id: '6', name: 'Phone', type: 'text' }, + { id: '7', name: 'Location', type: 'text' }, + { id: '8', name: 'Manager', type: 'text' }, + { id: '9', name: 'Salary', type: 'currency' }, + { id: '10', name: 'Status', type: 'select' }, + { id: '11', name: 'Birth Date', type: 'date' }, + { id: '12', name: 'Address', type: 'text' }, + { id: '13', name: 'Employee ID', type: 'number' } + ].filter(field => !selectedFields.find(sf => sf.id === field.id)); + + const addField = (field: ReportField) => { + setSelectedFields([...selectedFields, field]); + }; + + const removeField = (fieldId: string) => { + setSelectedFields(selectedFields.filter(f => f.id !== fieldId)); + }; + + const handleGenerateReport = () => { + alert('Report generated! In a real app, this would export the data.'); + }; + + return ( +
+
+

+ + Report Builder +

+ +
+ {/* Configuration Panel */} +
+ +

Report Configuration

+
+
+ + setReportName(e.target.value)} + className="w-full px-4 py-2 bg-slate-700 border border-slate-600 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-blue-500" + /> +
+ +
+ + +
+ +
+ + +
+
+
+ + +

Available Fields

+
+ {availableFields.map((field) => ( +
+
+

{field.name}

+

{field.type}

+
+ +
+ ))} +
+
+
+ + {/* Preview Panel */} +
+ +

Selected Fields

+
+ {selectedFields.map((field, index) => ( +
+
+ {index + 1} +
+

{field.name}

+

{field.type}

+
+
+ +
+ ))} + {selectedFields.length === 0 && ( +
+ No fields selected. Add fields from the left panel. +
+ )} +
+
+ + +

Report Preview

+
+ + + + {selectedFields.map((field) => ( + + ))} + + + + + {selectedFields.map((field) => ( + + ))} + + + {selectedFields.map((field) => ( + + ))} + + +
+ {field.name} +
+ Sample data +
+ Sample data +
+
+
+ + +
+
+
+
+ ); +} diff --git a/servers/bamboohr/src/ui/react-app/report-builder/index.html b/servers/bamboohr/src/ui/react-app/report-builder/index.html new file mode 100644 index 0000000..2a1ce89 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/report-builder/index.html @@ -0,0 +1,21 @@ + + + + + + Report Builder - BambooHR + + + +
+ + + + diff --git a/servers/bamboohr/src/ui/react-app/report-builder/vite.config.ts b/servers/bamboohr/src/ui/react-app/report-builder/vite.config.ts new file mode 100644 index 0000000..035d070 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/report-builder/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + server: { + port: 3006, + open: true + }, + build: { + outDir: 'dist', + sourcemap: true + } +}); diff --git a/servers/bamboohr/src/ui/react-app/time-off-balances/App.tsx b/servers/bamboohr/src/ui/react-app/time-off-balances/App.tsx new file mode 100644 index 0000000..a531cc2 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/time-off-balances/App.tsx @@ -0,0 +1,196 @@ +import React, { useState } from 'react'; +import { Calendar, TrendingUp, Search } from 'lucide-react'; + +interface EmployeeBalance { + id: string; + employee: string; + department: string; + vacation: { used: number; total: number }; + sick: { used: number; total: number }; + personal: { used: number; total: number }; +} + +const Card: React.FC<{ children: React.ReactNode }> = ({ children }) => ( +
{children}
+); + +const ProgressBar: React.FC<{ used: number; total: number; color: string }> = ({ used, total, color }) => { + const percentage = (used / total) * 100; + return ( +
+
+ {used} / {total} days + {Math.round(percentage)}% +
+
+
+
+
+ ); +}; + +export default function App() { + const [searchTerm, setSearchTerm] = useState(''); + + const balances: EmployeeBalance[] = [ + { + id: '1', + employee: 'Sarah Johnson', + department: 'Engineering', + vacation: { used: 8, total: 15 }, + sick: { used: 2, total: 10 }, + personal: { used: 1, total: 5 } + }, + { + id: '2', + employee: 'Mike Chen', + department: 'Product', + vacation: { used: 12, total: 15 }, + sick: { used: 3, total: 10 }, + personal: { used: 2, total: 5 } + }, + { + id: '3', + employee: 'Emily Davis', + department: 'Design', + vacation: { used: 5, total: 15 }, + sick: { used: 1, total: 10 }, + personal: { used: 0, total: 5 } + }, + { + id: '4', + employee: 'James Wilson', + department: 'Engineering', + vacation: { used: 10, total: 15 }, + sick: { used: 4, total: 10 }, + personal: { used: 3, total: 5 } + }, + { + id: '5', + employee: 'Lisa Anderson', + department: 'Marketing', + vacation: { used: 14, total: 15 }, + sick: { used: 5, total: 10 }, + personal: { used: 4, total: 5 } + }, + ]; + + const filteredBalances = balances.filter(balance => + balance.employee.toLowerCase().includes(searchTerm.toLowerCase()) || + balance.department.toLowerCase().includes(searchTerm.toLowerCase()) + ); + + const totalStats = balances.reduce((acc, balance) => ({ + vacation: acc.vacation + balance.vacation.used, + sick: acc.sick + balance.sick.used, + personal: acc.personal + balance.personal.used + }), { vacation: 0, sick: 0, personal: 0 }); + + return ( +
+
+

+ + Time Off Balances +

+ + {/* Summary Cards */} +
+ +
+

Vacation Days Used

+ +
+

{totalStats.vacation}

+

Across all employees

+
+ + +
+

Sick Days Used

+ +
+

{totalStats.sick}

+

Across all employees

+
+ + +
+

Personal Days Used

+ +
+

{totalStats.personal}

+

Across all employees

+
+
+ + {/* Search */} + +
+ + setSearchTerm(e.target.value)} + className="w-full pl-10 pr-4 py-2 bg-slate-700 border border-slate-600 rounded-lg text-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500" + /> +
+
+ + {/* Balances List */} +
+ {filteredBalances.map((balance) => ( + +
+
+ {balance.employee.split(' ').map(n => n[0]).join('')} +
+
+

{balance.employee}

+

{balance.department}

+
+
+ +
+
+

Vacation

+ +
+
+

Sick Leave

+ +
+
+

Personal

+ +
+
+
+ ))} + + {filteredBalances.length === 0 && ( +
+

No employees found

+
+ )} +
+
+
+ ); +} diff --git a/servers/bamboohr/src/ui/react-app/time-off-balances/index.html b/servers/bamboohr/src/ui/react-app/time-off-balances/index.html new file mode 100644 index 0000000..b846bf7 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/time-off-balances/index.html @@ -0,0 +1,21 @@ + + + + + + Time Off Balances - BambooHR + + + +
+ + + + diff --git a/servers/bamboohr/src/ui/react-app/time-off-balances/vite.config.ts b/servers/bamboohr/src/ui/react-app/time-off-balances/vite.config.ts new file mode 100644 index 0000000..49e7b22 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/time-off-balances/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + server: { + port: 3005, + open: true + }, + build: { + outDir: 'dist', + sourcemap: true + } +}); diff --git a/servers/bamboohr/src/ui/react-app/time-off-calendar/App.tsx b/servers/bamboohr/src/ui/react-app/time-off-calendar/App.tsx new file mode 100644 index 0000000..05e1ab9 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/time-off-calendar/App.tsx @@ -0,0 +1,167 @@ +import React, { useState } from 'react'; +import { ChevronLeft, ChevronRight, Calendar } from 'lucide-react'; + +interface TimeOffRequest { + id: string; + employee: string; + type: string; + startDate: Date; + endDate: Date; + status: 'Approved' | 'Pending' | 'Denied'; +} + +const Card: React.FC<{ children: React.ReactNode }> = ({ children }) => ( +
{children}
+); + +export default function App() { + const [currentDate, setCurrentDate] = useState(new Date(2025, 0, 1)); // January 2025 + + const timeOffRequests: TimeOffRequest[] = [ + { id: '1', employee: 'Sarah Johnson', type: 'Vacation', startDate: new Date(2025, 0, 15), endDate: new Date(2025, 0, 17), status: 'Approved' }, + { id: '2', employee: 'Mike Chen', type: 'Sick Leave', startDate: new Date(2025, 0, 8), endDate: new Date(2025, 0, 8), status: 'Approved' }, + { id: '3', employee: 'Emily Davis', type: 'Vacation', startDate: new Date(2025, 0, 22), endDate: new Date(2025, 0, 24), status: 'Pending' }, + { id: '4', employee: 'James Wilson', type: 'Personal', startDate: new Date(2025, 0, 10), endDate: new Date(2025, 0, 10), status: 'Approved' }, + ]; + + const getDaysInMonth = (date: Date) => { + return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate(); + }; + + const getFirstDayOfMonth = (date: Date) => { + return new Date(date.getFullYear(), date.getMonth(), 1).getDay(); + }; + + const getRequestsForDay = (day: number) => { + const checkDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), day); + return timeOffRequests.filter(req => { + return checkDate >= req.startDate && checkDate <= req.endDate; + }); + }; + + const changeMonth = (delta: number) => { + setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() + delta, 1)); + }; + + const daysInMonth = getDaysInMonth(currentDate); + const firstDay = getFirstDayOfMonth(currentDate); + const monthName = currentDate.toLocaleString('default', { month: 'long', year: 'numeric' }); + + const days = []; + for (let i = 0; i < firstDay; i++) { + days.push(
); + } + for (let day = 1; day <= daysInMonth; day++) { + const requests = getRequestsForDay(day); + days.push( +
+
{day}
+
+ {requests.slice(0, 2).map((req, idx) => ( +
+ {req.employee.split(' ')[0]} +
+ ))} + {requests.length > 2 && ( +
+{requests.length - 2} more
+ )} +
+
+ ); + } + + return ( +
+
+

+ + Time Off Calendar +

+ + + {/* Calendar Header */} +
+ +

{monthName}

+ +
+ + {/* Day Headers */} +
+ {['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(day => ( +
+ {day} +
+ ))} +
+ + {/* Calendar Grid */} +
+ {days} +
+ + {/* Legend */} +
+
+
+ Approved +
+
+
+ Pending +
+
+
+ Denied +
+
+
+ + {/* Upcoming Requests */} +
+

Upcoming Requests

+
+ {timeOffRequests.map(req => ( + +
+
+

{req.employee}

+

{req.type}

+

+ {req.startDate.toLocaleDateString()} - {req.endDate.toLocaleDateString()} +

+
+ + {req.status} + +
+
+ ))} +
+
+
+
+ ); +} diff --git a/servers/bamboohr/src/ui/react-app/time-off-calendar/index.html b/servers/bamboohr/src/ui/react-app/time-off-calendar/index.html new file mode 100644 index 0000000..6fb22d5 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/time-off-calendar/index.html @@ -0,0 +1,21 @@ + + + + + + Time Off Calendar - BambooHR + + + +
+ + + + diff --git a/servers/bamboohr/src/ui/react-app/time-off-calendar/vite.config.ts b/servers/bamboohr/src/ui/react-app/time-off-calendar/vite.config.ts new file mode 100644 index 0000000..a4d3f74 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/time-off-calendar/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + server: { + port: 3003, + open: true + }, + build: { + outDir: 'dist', + sourcemap: true + } +}); diff --git a/servers/bamboohr/src/ui/react-app/time-off-requests/App.tsx b/servers/bamboohr/src/ui/react-app/time-off-requests/App.tsx new file mode 100644 index 0000000..4b9201d --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/time-off-requests/App.tsx @@ -0,0 +1,151 @@ +import React, { useState } from 'react'; +import { Check, X, Clock, Filter } from 'lucide-react'; + +interface TimeOffRequest { + id: string; + employee: string; + type: string; + startDate: string; + endDate: string; + days: number; + reason: string; + status: 'Pending' | 'Approved' | 'Denied'; + submittedDate: string; +} + +const Card: React.FC<{ children: React.ReactNode }> = ({ children }) => ( +
{children}
+); + +export default function App() { + const [filterStatus, setFilterStatus] = useState('All'); + + const [requests, setRequests] = useState([ + { id: '1', employee: 'Sarah Johnson', type: 'Vacation', startDate: '2025-01-15', endDate: '2025-01-17', days: 3, reason: 'Family vacation', status: 'Pending', submittedDate: '2025-01-05' }, + { id: '2', employee: 'Mike Chen', type: 'Sick Leave', startDate: '2025-01-08', endDate: '2025-01-08', days: 1, reason: 'Medical appointment', status: 'Approved', submittedDate: '2025-01-07' }, + { id: '3', employee: 'Emily Davis', type: 'Vacation', startDate: '2025-01-22', endDate: '2025-01-24', days: 3, reason: 'Personal time', status: 'Pending', submittedDate: '2025-01-10' }, + { id: '4', employee: 'James Wilson', type: 'Personal', startDate: '2025-01-10', endDate: '2025-01-10', days: 1, reason: 'Personal errands', status: 'Approved', submittedDate: '2025-01-08' }, + { id: '5', employee: 'Lisa Anderson', type: 'Vacation', startDate: '2025-02-01', endDate: '2025-02-05', days: 5, reason: 'Holiday trip', status: 'Pending', submittedDate: '2025-01-12' }, + { id: '6', employee: 'David Brown', type: 'Sick Leave', startDate: '2025-01-03', endDate: '2025-01-03', days: 1, reason: 'Illness', status: 'Denied', submittedDate: '2025-01-02' }, + ]); + + const handleApprove = (id: string) => { + setRequests(requests.map(req => + req.id === id ? { ...req, status: 'Approved' as const } : req + )); + }; + + const handleDeny = (id: string) => { + setRequests(requests.map(req => + req.id === id ? { ...req, status: 'Denied' as const } : req + )); + }; + + const filteredRequests = filterStatus === 'All' + ? requests + : requests.filter(req => req.status === filterStatus); + + return ( +
+
+

+ + Time Off Requests +

+ + {/* Filter */} + +
+ + + + Showing {filteredRequests.length} of {requests.length} requests + +
+
+ + {/* Requests List */} +
+ {filteredRequests.map((request) => ( + +
+
+
+
+ {request.employee.split(' ').map(n => n[0]).join('')} +
+
+

{request.employee}

+

{request.type}

+
+
+ +
+
+

Dates

+

{request.startDate} to {request.endDate}

+

{request.days} day{request.days > 1 ? 's' : ''}

+
+
+

Reason

+

{request.reason}

+
+
+

Submitted

+

{request.submittedDate}

+
+
+
+ +
+ + {request.status} + + + {request.status === 'Pending' && ( +
+ + +
+ )} +
+
+
+ ))} + + {filteredRequests.length === 0 && ( +
+ +

No requests found

+
+ )} +
+
+
+ ); +} diff --git a/servers/bamboohr/src/ui/react-app/time-off-requests/index.html b/servers/bamboohr/src/ui/react-app/time-off-requests/index.html new file mode 100644 index 0000000..bd54ca1 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/time-off-requests/index.html @@ -0,0 +1,21 @@ + + + + + + Time Off Requests - BambooHR + + + +
+ + + + diff --git a/servers/bamboohr/src/ui/react-app/time-off-requests/vite.config.ts b/servers/bamboohr/src/ui/react-app/time-off-requests/vite.config.ts new file mode 100644 index 0000000..821cc98 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/time-off-requests/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + server: { + port: 3004, + open: true + }, + build: { + outDir: 'dist', + sourcemap: true + } +}); diff --git a/servers/bamboohr/src/ui/react-app/training-catalog/App.tsx b/servers/bamboohr/src/ui/react-app/training-catalog/App.tsx new file mode 100644 index 0000000..270292a --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/training-catalog/App.tsx @@ -0,0 +1,171 @@ +import React, { useState } from 'react'; +import { BookOpen, Users, Clock, Star } from 'lucide-react'; + +interface Course { + id: string; + title: string; + category: string; + duration: string; + enrolled: number; + completed: number; + rating: number; + difficulty: 'Beginner' | 'Intermediate' | 'Advanced'; +} + +const Card: React.FC<{ children: React.ReactNode }> = ({ children }) => ( +
{children}
+); + +export default function App() { + const [filterCategory, setFilterCategory] = useState('All'); + + const courses: Course[] = [ + { id: '1', title: 'Leadership Essentials', category: 'Leadership', duration: '4 hours', enrolled: 45, completed: 32, rating: 4.8, difficulty: 'Intermediate' }, + { id: '2', title: 'Communication Skills', category: 'Soft Skills', duration: '3 hours', enrolled: 65, completed: 58, rating: 4.6, difficulty: 'Beginner' }, + { id: '3', title: 'Project Management', category: 'Management', duration: '6 hours', enrolled: 38, completed: 25, rating: 4.7, difficulty: 'Advanced' }, + { id: '4', title: 'Data Analytics Basics', category: 'Technical', duration: '5 hours', enrolled: 52, completed: 40, rating: 4.5, difficulty: 'Beginner' }, + { id: '5', title: 'Advanced Excel', category: 'Technical', duration: '4 hours', enrolled: 30, completed: 22, rating: 4.9, difficulty: 'Advanced' }, + { id: '6', title: 'Diversity & Inclusion', category: 'Compliance', duration: '2 hours', enrolled: 85, completed: 75, rating: 4.4, difficulty: 'Beginner' }, + { id: '7', title: 'Agile Methodology', category: 'Development', duration: '5 hours', enrolled: 42, completed: 30, rating: 4.7, difficulty: 'Intermediate' }, + { id: '8', title: 'Customer Service Excellence', category: 'Soft Skills', duration: '3 hours', enrolled: 55, completed: 48, rating: 4.6, difficulty: 'Beginner' }, + ]; + + const categories = ['All', ...Array.from(new Set(courses.map(c => c.category)))]; + + const filteredCourses = filterCategory === 'All' + ? courses + : courses.filter(c => c.category === filterCategory); + + const stats = { + totalCourses: courses.length, + totalEnrolled: courses.reduce((sum, c) => sum + c.enrolled, 0), + totalCompleted: courses.reduce((sum, c) => sum + c.completed, 0), + avgRating: (courses.reduce((sum, c) => sum + c.rating, 0) / courses.length).toFixed(1) + }; + + return ( +
+
+

+ + Training Catalog +

+ + {/* Stats */} +
+ +
+
+

Total Courses

+

{stats.totalCourses}

+
+ +
+
+ +
+
+

Enrolled

+

{stats.totalEnrolled}

+
+ +
+
+ +
+
+

Completed

+

{stats.totalCompleted}

+
+ +
+
+ +
+
+

Avg Rating

+

{stats.avgRating}

+
+ +
+
+
+ + {/* Filter */} + + + + Showing {filteredCourses.length} course{filteredCourses.length !== 1 ? 's' : ''} + + + + {/* Courses Grid */} +
+ {filteredCourses.map((course) => { + const completionRate = Math.round((course.completed / course.enrolled) * 100); + return ( + +
+
+

{course.title}

+ + {course.difficulty} + +
+

{course.category}

+
+ +
+
+ + + {course.duration} + + + + {course.rating} + +
+ +
+ Enrolled + {course.enrolled} +
+ +
+
+ Completion Rate + {completionRate}% +
+
+
+
+
+
+ + + + ); + })} +
+
+
+ ); +} diff --git a/servers/bamboohr/src/ui/react-app/training-catalog/index.html b/servers/bamboohr/src/ui/react-app/training-catalog/index.html new file mode 100644 index 0000000..dc7c363 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/training-catalog/index.html @@ -0,0 +1,21 @@ + + + + + + Training Catalog - BambooHR + + + +
+ + + + diff --git a/servers/bamboohr/src/ui/react-app/training-catalog/vite.config.ts b/servers/bamboohr/src/ui/react-app/training-catalog/vite.config.ts new file mode 100644 index 0000000..16943cc --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/training-catalog/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + server: { + port: 3011, + open: true + }, + build: { + outDir: 'dist', + sourcemap: true + } +}); diff --git a/servers/bamboohr/src/ui/react-app/training-progress/App.tsx b/servers/bamboohr/src/ui/react-app/training-progress/App.tsx new file mode 100644 index 0000000..fe8828c --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/training-progress/App.tsx @@ -0,0 +1,166 @@ +import React, { useState } from 'react'; +import { BookOpen, CheckCircle, Clock, Award, Search } from 'lucide-react'; + +interface EmployeeProgress { + id: string; + employee: string; + department: string; + coursesCompleted: number; + coursesInProgress: number; + totalCourses: number; + hoursCompleted: number; + lastActivity: string; +} + +const Card: React.FC<{ children: React.ReactNode }> = ({ children }) => ( +
{children}
+); + +export default function App() { + const [searchTerm, setSearchTerm] = useState(''); + + const progress: EmployeeProgress[] = [ + { id: '1', employee: 'Sarah Johnson', department: 'Engineering', coursesCompleted: 8, coursesInProgress: 2, totalCourses: 12, hoursCompleted: 32, lastActivity: '2 days ago' }, + { id: '2', employee: 'Mike Chen', department: 'Product', coursesCompleted: 5, coursesInProgress: 1, totalCourses: 8, hoursCompleted: 20, lastActivity: '1 week ago' }, + { id: '3', employee: 'Emily Davis', department: 'Design', coursesCompleted: 10, coursesInProgress: 0, totalCourses: 10, hoursCompleted: 38, lastActivity: '3 days ago' }, + { id: '4', employee: 'James Wilson', department: 'Engineering', coursesCompleted: 6, coursesInProgress: 3, totalCourses: 12, hoursCompleted: 24, lastActivity: 'Today' }, + { id: '5', employee: 'Lisa Anderson', department: 'Marketing', coursesCompleted: 7, coursesInProgress: 2, totalCourses: 10, hoursCompleted: 28, lastActivity: '4 days ago' }, + ]; + + const filteredProgress = progress.filter(p => + p.employee.toLowerCase().includes(searchTerm.toLowerCase()) || + p.department.toLowerCase().includes(searchTerm.toLowerCase()) + ); + + const stats = { + totalEmployees: progress.length, + avgCompletion: Math.round((progress.reduce((sum, p) => sum + (p.coursesCompleted / p.totalCourses), 0) / progress.length) * 100), + totalHours: progress.reduce((sum, p) => sum + p.hoursCompleted, 0), + activeEmployees: progress.filter(p => p.coursesInProgress > 0).length + }; + + return ( +
+
+

+ + Training Progress +

+ + {/* Stats */} +
+ +
+
+

Total Employees

+

{stats.totalEmployees}

+
+ +
+
+ +
+
+

Avg Completion

+

{stats.avgCompletion}%

+
+ +
+
+ +
+
+

Total Hours

+

{stats.totalHours}

+
+ +
+
+ +
+
+

Active Learners

+

{stats.activeEmployees}

+
+ +
+
+
+ + {/* Search */} + +
+ + setSearchTerm(e.target.value)} + className="w-full pl-10 pr-4 py-2 bg-slate-700 border border-slate-600 rounded-lg text-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500" + /> +
+
+ + {/* Progress List */} +
+ {filteredProgress.map((employee) => { + const completionRate = Math.round((employee.coursesCompleted / employee.totalCourses) * 100); + return ( + +
+
+
+ {employee.employee.split(' ').map(n => n[0]).join('')} +
+
+

{employee.employee}

+

{employee.department}

+

Last active: {employee.lastActivity}

+
+
+ +
+
+

{employee.coursesCompleted}

+

Completed

+
+
+

{employee.coursesInProgress}

+

In Progress

+
+
+

{employee.hoursCompleted}h

+

Hours

+
+
+
+ +
+
+ Overall Progress + + {employee.coursesCompleted} / {employee.totalCourses} ({completionRate}%) + +
+
+
+
+
+ + ); + })} + + {filteredProgress.length === 0 && ( +
+ +

No employees found

+
+ )} +
+
+
+ ); +} diff --git a/servers/bamboohr/src/ui/react-app/training-progress/index.html b/servers/bamboohr/src/ui/react-app/training-progress/index.html new file mode 100644 index 0000000..4d80a5a --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/training-progress/index.html @@ -0,0 +1,21 @@ + + + + + + Training Progress - BambooHR + + + +
+ + + + diff --git a/servers/bamboohr/src/ui/react-app/training-progress/vite.config.ts b/servers/bamboohr/src/ui/react-app/training-progress/vite.config.ts new file mode 100644 index 0000000..471acef --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/training-progress/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + server: { + port: 3012, + open: true + }, + build: { + outDir: 'dist', + sourcemap: true + } +}); diff --git a/servers/bamboohr/src/ui/react-app/turnover-report/App.tsx b/servers/bamboohr/src/ui/react-app/turnover-report/App.tsx new file mode 100644 index 0000000..11e7bb5 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/turnover-report/App.tsx @@ -0,0 +1,184 @@ +import React from 'react'; +import { TrendingDown, AlertCircle, Users, Calendar } from 'lucide-react'; + +const Card: React.FC<{ children: React.ReactNode }> = ({ children }) => ( +
{children}
+); + +export default function App() { + const monthlyTurnover = [ + { month: 'Jul 2024', voluntary: 1, involuntary: 1, total: 2, rate: 0.9 }, + { month: 'Aug 2024', voluntary: 2, involuntary: 0, total: 2, rate: 0.9 }, + { month: 'Sep 2024', voluntary: 1, involuntary: 0, total: 1, rate: 0.4 }, + { month: 'Oct 2024', voluntary: 2, involuntary: 1, total: 3, rate: 1.3 }, + { month: 'Nov 2024', voluntary: 1, involuntary: 1, total: 2, rate: 0.8 }, + { month: 'Dec 2024', voluntary: 2, involuntary: 0, total: 2, rate: 0.8 }, + { month: 'Jan 2025', voluntary: 1, involuntary: 0, total: 1, rate: 0.4 } + ]; + + const departmentTurnover = [ + { dept: 'Engineering', terminations: 3, avgTenure: '2.5 years', rate: 3.5, risk: 'Low' }, + { dept: 'Sales', terminations: 5, avgTenure: '1.8 years', rate: 11.1, risk: 'High' }, + { dept: 'Marketing', terminations: 2, avgTenure: '2.2 years', rate: 7.1, risk: 'Medium' }, + { dept: 'Product', terminations: 1, avgTenure: '3.1 years', rate: 3.1, risk: 'Low' }, + { dept: 'Operations', terminations: 2, avgTenure: '1.5 years', rate: 20.0, risk: 'High' } + ]; + + const exitReasons = [ + { reason: 'Better Opportunity', count: 5, percentage: 38 }, + { reason: 'Compensation', count: 3, percentage: 23 }, + { reason: 'Relocation', count: 2, percentage: 15 }, + { reason: 'Career Growth', count: 2, percentage: 15 }, + { reason: 'Other', count: 1, percentage: 8 } + ]; + + const totalTerminations = monthlyTurnover.reduce((sum, m) => sum + m.total, 0); + const avgTurnoverRate = (monthlyTurnover.reduce((sum, m) => sum + m.rate, 0) / monthlyTurnover.length).toFixed(1); + const voluntaryTerminations = monthlyTurnover.reduce((sum, m) => sum + m.voluntary, 0); + const involuntaryTerminations = monthlyTurnover.reduce((sum, m) => sum + m.involuntary, 0); + + const maxRate = Math.max(...monthlyTurnover.map(m => m.rate)); + + return ( +
+
+

+ + Turnover Report +

+ + {/* Stats */} +
+ +
+
+

Total Terminations

+

{totalTerminations}

+

Last 6 months

+
+ +
+
+ +
+
+

Avg Turnover Rate

+

{avgTurnoverRate}%

+

Monthly average

+
+ +
+
+ +
+
+

Voluntary

+

{voluntaryTerminations}

+

{((voluntaryTerminations/totalTerminations)*100).toFixed(0)}% of total

+
+ +
+
+ +
+
+

Involuntary

+

{involuntaryTerminations}

+

{((involuntaryTerminations/totalTerminations)*100).toFixed(0)}% of total

+
+ +
+
+
+ +
+ {/* Monthly Trend */} + +

Monthly Turnover Rate

+
+ {monthlyTurnover.map((data, idx) => { + const barHeight = (data.rate / maxRate) * 100; + return ( +
+
+ {data.month} +
+ V:{data.voluntary} I:{data.involuntary} + {data.rate}% +
+
+
+
+
+
+ ); + })} +
+ + + {/* By Department */} + +

Department Analysis

+
+ {departmentTurnover.map((dept, idx) => ( +
+
+

{dept.dept}

+ + {dept.risk} Risk + +
+
+
+

Terminations

+

{dept.terminations}

+
+
+

Rate

+

{dept.rate}%

+
+
+

Avg Tenure

+

{dept.avgTenure}

+
+
+
+ ))} +
+
+
+ + {/* Exit Reasons */} + +

Exit Reasons (Voluntary)

+
+ {exitReasons.map((reason, idx) => ( +
+
+ {reason.reason} +
+ {reason.count} employee{reason.count > 1 ? 's' : ''} + {reason.percentage}% +
+
+
+
+
+
+ ))} +
+ +
+
+ ); +} diff --git a/servers/bamboohr/src/ui/react-app/turnover-report/index.html b/servers/bamboohr/src/ui/react-app/turnover-report/index.html new file mode 100644 index 0000000..a54d07e --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/turnover-report/index.html @@ -0,0 +1,21 @@ + + + + + + Turnover Report - BambooHR + + + +
+ + + + diff --git a/servers/bamboohr/src/ui/react-app/turnover-report/vite.config.ts b/servers/bamboohr/src/ui/react-app/turnover-report/vite.config.ts new file mode 100644 index 0000000..33c4984 --- /dev/null +++ b/servers/bamboohr/src/ui/react-app/turnover-report/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + server: { + port: 3016, + open: true + }, + build: { + outDir: 'dist', + sourcemap: true + } +});