Generated by Spark: split app.tsx into 5 parts

This commit is contained in:
2026-01-23 02:22:24 +00:00
committed by GitHub
parent 8d10199879
commit e64f7d8f02
5 changed files with 707 additions and 35 deletions

View File

@@ -9,25 +9,9 @@ import {
ShieldCheck,
ChartBar,
Buildings,
CheckCircle,
XCircle,
ClockCounterClockwise,
Plus,
MagnifyingGlass,
Funnel,
Download,
ArrowUp,
ArrowDown,
Warning,
MapTrifold,
UploadSimple,
FileCsv,
Envelope,
X,
CalendarBlank,
Notepad,
Bell,
Camera,
ChartLine,
CurrencyCircleDollar,
QrCode,
@@ -35,24 +19,23 @@ import {
UserPlus,
Gear,
FileText,
CaretDown,
CaretRight,
Question
Question,
UploadSimple,
ClockCounterClockwise,
CalendarBlank,
Envelope,
X
} from '@phosphor-icons/react'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { Input } from '@/components/ui/input'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'
import { Label } from '@/components/ui/label'
import { Textarea } from '@/components/ui/textarea'
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
import { Separator } from '@/components/ui/separator'
import { ScrollArea } from '@/components/ui/scroll-area'
import { toast } from 'sonner'
import { cn } from '@/lib/utils'
import { NavItem, NavGroup } from '@/components/navigation'
import { DashboardView } from '@/components/dashboard-view'
import { RoadmapView } from '@/components/roadmap-view'
import { ReportsView } from '@/components/ReportsView'
import { CurrencyManagement } from '@/components/CurrencyManagement'
import { EmailTemplateManager } from '@/components/EmailTemplateManager'
@@ -71,7 +54,6 @@ import { CustomReportBuilder } from '@/components/CustomReportBuilder'
import { HolidayPayManager } from '@/components/HolidayPayManager'
import { PermanentPlacementInvoice } from '@/components/PermanentPlacementInvoice'
import { CreditNoteGenerator } from '@/components/CreditNoteGenerator'
import { ShiftPremiumCalculator } from '@/components/ShiftPremiumCalculator'
import { ContractValidator } from '@/components/ContractValidator'
import { DetailedTimesheetEntry } from '@/components/DetailedTimesheetEntry'
import { ShiftPatternManager } from '@/components/ShiftPatternManager'
@@ -81,6 +63,7 @@ import { ExpenseDetailDialog } from '@/components/ExpenseDetailDialog'
import { ComplianceDetailDialog } from '@/components/ComplianceDetailDialog'
import { AdvancedSearch, type FilterField } from '@/components/AdvancedSearch'
import { QueryLanguageGuide } from '@/components/QueryLanguageGuide'
import { TimesheetsView, BillingView, PayrollView, ExpensesView, ComplianceView } from '@/components/views'
import type {
Timesheet,
Invoice,
@@ -909,13 +892,7 @@ function App() {
)
}
interface NavItemProps {
icon: React.ReactNode
label: string
active?: boolean
onClick?: () => void
badge?: number
}
export default App
function NavItem({ icon, label, active, onClick, badge }: NavItemProps) {
return (
@@ -3083,4 +3060,3 @@ function RoadmapView() {
)
}
export default App

View File

@@ -0,0 +1,236 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import {
Clock,
Receipt,
CurrencyDollar,
ClockCounterClockwise,
CheckCircle,
Warning,
Notepad,
Download,
ArrowUp,
ArrowDown
} from '@phosphor-icons/react'
import { cn } from '@/lib/utils'
import type { DashboardMetrics } from '@/lib/types'
interface DashboardViewProps {
metrics: DashboardMetrics
}
export function DashboardView({ metrics }: DashboardViewProps) {
return (
<div className="space-y-6">
<div>
<h2 className="text-3xl font-semibold tracking-tight">Dashboard</h2>
<p className="text-muted-foreground mt-1">Real-time overview of your workforce operations</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<MetricCard
title="Pending Approvals"
value={metrics.pendingApprovals}
icon={<ClockCounterClockwise size={20} className="text-warning" />}
trend={{ value: 12, direction: 'up' }}
variant="warning"
/>
<MetricCard
title="Pending Expenses"
value={metrics.pendingExpenses}
icon={<Notepad size={20} className="text-info" />}
variant="default"
/>
<MetricCard
title="Overdue Invoices"
value={metrics.overdueInvoices}
icon={<Receipt size={20} className="text-destructive" />}
trend={{ value: 5, direction: 'down' }}
variant="error"
/>
<MetricCard
title="Compliance Alerts"
value={metrics.complianceAlerts}
icon={<Warning size={20} className="text-warning" />}
variant="warning"
/>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
<Card className="lg:col-span-1">
<CardHeader>
<CardTitle className="text-lg">Monthly Revenue</CardTitle>
<CardDescription>Total invoiced this month</CardDescription>
</CardHeader>
<CardContent>
<div className="text-3xl font-semibold font-mono">
£{metrics.monthlyRevenue.toLocaleString()}
</div>
<div className="flex items-center gap-1 mt-2 text-sm text-success">
<ArrowUp size={16} weight="bold" />
<span>12.5% from last month</span>
</div>
</CardContent>
</Card>
<Card className="lg:col-span-1">
<CardHeader>
<CardTitle className="text-lg">Monthly Payroll</CardTitle>
<CardDescription>Total payroll costs</CardDescription>
</CardHeader>
<CardContent>
<div className="text-3xl font-semibold font-mono">
£{metrics.monthlyPayroll.toLocaleString()}
</div>
<div className="flex items-center gap-1 mt-2 text-sm text-muted-foreground">
<ArrowUp size={16} weight="bold" />
<span>8.3% from last month</span>
</div>
</CardContent>
</Card>
<Card className="lg:col-span-1">
<CardHeader>
<CardTitle className="text-lg">Gross Margin</CardTitle>
<CardDescription>Revenue minus payroll</CardDescription>
</CardHeader>
<CardContent>
<div className="text-3xl font-semibold font-mono">
{metrics.grossMargin.toFixed(1)}%
</div>
<div className="flex items-center gap-1 mt-2 text-sm text-success">
<ArrowUp size={16} weight="bold" />
<span>3.2% from last month</span>
</div>
</CardContent>
</Card>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
<Card>
<CardHeader>
<CardTitle className="text-lg">Recent Activity</CardTitle>
<CardDescription>Latest timesheet and billing events</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<ActivityItem
icon={<CheckCircle size={18} className="text-success" />}
title="Timesheet approved"
description="John Smith - Week ending 15 Jan 2025"
time="5 minutes ago"
/>
<ActivityItem
icon={<Receipt size={18} className="text-info" />}
title="Invoice generated"
description="INV-00234 - Acme Corp - £2,450"
time="12 minutes ago"
/>
<ActivityItem
icon={<CheckCircle size={18} className="text-success" />}
title="Payroll completed"
description="Weekly run - 45 workers - £28,900"
time="1 hour ago"
/>
<ActivityItem
icon={<Warning size={18} className="text-warning" />}
title="Document expiring soon"
description="DBS check for Sarah Johnson - 14 days"
time="2 hours ago"
/>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="text-lg">Quick Actions</CardTitle>
<CardDescription>Common tasks and workflows</CardDescription>
</CardHeader>
<CardContent className="space-y-2">
<Button className="w-full justify-start" variant="outline">
<Clock size={18} className="mr-2" />
Create Timesheet
</Button>
<Button className="w-full justify-start" variant="outline">
<Receipt size={18} className="mr-2" />
Generate Invoice
</Button>
<Button className="w-full justify-start" variant="outline">
<CurrencyDollar size={18} className="mr-2" />
Run Payroll
</Button>
<Button className="w-full justify-start" variant="outline">
<Download size={18} className="mr-2" />
Export Reports
</Button>
</CardContent>
</Card>
</div>
</div>
)
}
interface MetricCardProps {
title: string
value: number
icon: React.ReactNode
trend?: { value: number; direction: 'up' | 'down' }
variant?: 'default' | 'success' | 'warning' | 'error'
}
function MetricCard({ title, value, icon, trend, variant = 'default' }: MetricCardProps) {
const borderColors = {
default: 'border-border',
success: 'border-success/20',
warning: 'border-warning/20',
error: 'border-destructive/20'
}
return (
<Card className={cn('border-l-4', borderColors[variant])}>
<CardHeader className="flex flex-row items-center justify-between pb-2">
<CardTitle className="text-sm font-medium text-muted-foreground">
{title}
</CardTitle>
{icon}
</CardHeader>
<CardContent>
<div className="text-3xl font-semibold">{value}</div>
{trend && (
<div className={cn(
'flex items-center gap-1 mt-1 text-xs',
trend.direction === 'up' ? 'text-success' : 'text-muted-foreground'
)}>
{trend.direction === 'up' ? (
<ArrowUp size={14} weight="bold" />
) : (
<ArrowDown size={14} weight="bold" />
)}
<span>{trend.value}% vs last week</span>
</div>
)}
</CardContent>
</Card>
)
}
interface ActivityItemProps {
icon: React.ReactNode
title: string
description: string
time: string
}
function ActivityItem({ icon, title, description, time }: ActivityItemProps) {
return (
<div className="flex items-start gap-3">
<div className="mt-0.5">{icon}</div>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium">{title}</p>
<p className="text-sm text-muted-foreground truncate">{description}</p>
</div>
<span className="text-xs text-muted-foreground whitespace-nowrap">{time}</span>
</div>
)
}

View File

@@ -0,0 +1,62 @@
import { Badge } from '@/components/ui/badge'
import { cn } from '@/lib/utils'
import { CaretDown, CaretRight } from '@phosphor-icons/react'
interface NavItemProps {
icon: React.ReactNode
label: string
active?: boolean
onClick?: () => void
badge?: number
}
export function NavItem({ icon, label, active, onClick, badge }: NavItemProps) {
return (
<button
onClick={onClick}
className={cn(
'w-full flex items-center gap-3 px-3 py-2 rounded-lg text-sm font-medium transition-colors',
active
? 'bg-accent text-accent-foreground'
: 'text-foreground hover:bg-muted'
)}
>
<span className={active ? 'text-accent-foreground' : 'text-muted-foreground'}>
{icon}
</span>
<span className="flex-1 text-left">{label}</span>
{badge !== undefined && badge > 0 && (
<Badge variant="destructive" className="ml-auto h-5 px-1.5 text-xs">
{badge}
</Badge>
)}
</button>
)
}
interface NavGroupProps {
id: string
label: string
expanded: boolean
onToggle: () => void
children: React.ReactNode
}
export function NavGroup({ label, expanded, onToggle, children }: NavGroupProps) {
return (
<div className="space-y-1">
<button
onClick={onToggle}
className="w-full flex items-center gap-2 px-3 py-2 text-xs font-semibold text-muted-foreground uppercase tracking-wider hover:text-foreground transition-colors"
>
{expanded ? <CaretDown size={14} weight="bold" /> : <CaretRight size={14} weight="bold" />}
<span className="flex-1 text-left">{label}</span>
</button>
{expanded && (
<div className="space-y-1 pl-2">
{children}
</div>
)}
</div>
)
}

View File

@@ -0,0 +1,393 @@
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { CheckCircle, ClockCounterClockwise, MapTrifold, Warning, Download } from '@phosphor-icons/react'
import { cn } from '@/lib/utils'
export function RoadmapView() {
const roadmapContent = `# WorkForce Pro - Development Roadmap
## Phase 1: Foundation & Core Pay/Bill (Quarters 1-2) ✅
### Core Platform Infrastructure
- ✅ Multi-tenancy architecture
- ✅ Entity and division management
- ✅ User authentication and role-based access control
- ✅ Cloud-hosted SaaS deployment
- ✅ Basic security and data access controls
### Timesheet Management - Basic
- ✅ Online web portal timesheet entry
- ✅ Timesheet approval workflows
- ✅ Status tracking (pending/approved/rejected)
- ✅ Agency-initiated timesheet creation
- ✅ Bulk entry by administrators
- ✅ Mobile timesheet submission
- 📋 Batch import capabilities
- ✅ QR-coded paper timesheet scanning
### Basic Billing & Invoicing
- ✅ Invoice generation from timesheets
- ✅ Invoice status tracking
- ✅ Multi-currency support (GBP, USD, EUR)
- ✅ Electronic invoice delivery
- ✅ Sales invoice templates
- ✅ Payment terms configuration
- 📋 Purchase order tracking
### Basic Payroll
- ✅ Payroll run tracking
- ✅ Worker payment calculations
- ✅ One-click payroll processing
- 📋 PAYE payroll integration
- ✅ Holiday pay calculations
### Dashboard & Core Reporting
- ✅ Executive dashboard with key metrics
- ✅ Pending approvals tracking
- ✅ Overdue invoice monitoring
- ✅ Revenue and margin visibility
- ✅ Activity feed
---
## Phase 2: Advanced Operations & Automation (Quarters 3-4) 🔄
### Expense Management
- ✅ Worker expense submission (web portal)
- ✅ Agency-created expense entries
- ✅ Expense approval workflows
- ✅ Integration with billing and payroll
- ✅ Reimbursable vs billable expense tracking
- 📋 Mobile expense capture with receipt photos
### Notifications & Workflow Automation
- ✅ In-system alert notifications
- ✅ Real-time notification center
- ✅ Notification history and tracking
- ✅ Event-driven processing updates
- ✅ Email notification templates
- 📋 Configurable notification rules
- 📋 Automated follow-up reminders
### Timesheet Management - Advanced
- ✅ Multi-step approval routing
- 📋 Time and rate adjustment wizard
- 📋 Automated credit generation
- 📋 Re-invoicing workflows
- 📋 Full audit trail
### Advanced Billing
- 📋 Permanent placement invoices
- 📋 Contractor self-billing
- 📋 Bespoke invoice templates
- 📋 Client self-billing support
- 📋 Withholding tax handling
### Contract, Rate & Rule Enforcement
- ✅ Rate templates by role/client/placement
- 📋 Automatic shift premium calculations
- 📋 Overtime rate automation
- 📋 Time pattern validation
- 📋 AWR monitoring
---
## Phase 3: Compliance & Self-Service (Quarters 5-6) 📋
### Compliance Management - Enhanced
- ✅ Document tracking and monitoring
- ✅ Expiry alerts and notifications
- ✅ Document upload and storage
- 📋 Digital onboarding workflows
- 📋 Automated contract pack generation
- 📋 Compliance enforcement rules
- 📋 Statutory reporting support
### Self-Service Portals
- 📋 Branded worker portal
- 📋 Branded client portal
- 📋 Real-time timesheet visibility
- 📋 Invoice and payment status
- 📋 Paperless document access
- 📋 Mobile-responsive design
### Advanced Payroll Processing
- 📋 CIS processing
- 📋 Agency staff payroll
- 📋 Multiple employment models
- 📋 International payroll preparation
- 📋 Holiday pay automation
---
## Phase 4: Analytics & Integrations (Quarters 7-8) 📋
### Advanced Reporting & Analytics
- ✅ Real-time gross margin reporting
- ✅ Forecasting and predictive analytics
- ✅ Missing timesheet reports
- ✅ Custom report builder
- 📋 Client-level performance dashboards
- 📋 Placement-level profitability
### System Integrations
- 📋 ATS (Applicant Tracking System) integration
- 📋 CRM platform integration
- 📋 Accounting system integration (Xero, QuickBooks, Sage)
- 📋 RESTful API for third-party integrations
- 📋 Webhook support for real-time updates
### Global & Multi-Currency - Advanced
- ✅ Multi-currency billing (expanded)
- 📋 International sales tax handling
- 📋 Withholding tax automation
- 📋 Cross-border margin calculation
---
## Phase 5: Enterprise & Scale (Quarters 9-10) 📋
### Multi-Tenancy - Advanced
- 📋 Franchise management capabilities
- 📋 Agency group consolidation
- 📋 Cross-entity reporting
- 📋 Delegated administration controls
### Configuration & Customisation
- 📋 Custom system labels
- 📋 Agency-defined security roles
- ✅ Editable email templates
- 📋 White-label capabilities
- 📋 Custom workflow builders
### Performance & Scale
- 📋 High-volume processing optimization
- 📋 Batch processing improvements
- 📋 Performance monitoring dashboards
- 📋 Load balancing and scaling
---
## Phase 6: Innovation & AI (Quarters 11-12) 📋
### Intelligent Automation
- 📋 AI-powered timesheet anomaly detection
- 📋 Predictive compliance alerts
- 📋 Smart invoice matching
- 📋 Automated expense categorization
- 📋 Machine learning for margin optimization
### Advanced Analytics
- 📋 Business intelligence dashboards
- 📋 Trend analysis and insights
- 📋 Benchmarking and KPI tracking
- 📋 Predictive workforce planning
### Mobile Excellence
- 📋 Native mobile apps (iOS/Android)
- 📋 Offline capability
- 📋 Biometric authentication
- 📋 Push notifications
- 📋 Geolocation-based features
---
## Legend
- ✅ **Completed** - Feature is implemented and live
- 🔄 **In Progress** - Currently under development
- 📋 **Planned** - Scheduled for future development
---
## Success Metrics
### Operational Efficiency
- 80% reduction in timesheet processing time
- 95% straight-through invoice processing
- 90% reduction in compliance breach incidents
### User Adoption
- 85%+ worker portal adoption
- 75%+ client portal adoption
- <5% support ticket rate per user
### Financial Impact
- 99.5% billing accuracy
- <2% margin leakage
- 30% reduction in administrative overhead`
const parseMarkdown = (text: string) => {
const lines = text.split('\n')
const elements: React.ReactNode[] = []
let inList = false
let listItems: React.ReactNode[] = []
const flushList = (index: number) => {
if (inList && listItems.length > 0) {
elements.push(
<ul key={`list-${index}`} className="space-y-2 mb-4 pl-6">
{listItems}
</ul>
)
listItems = []
inList = false
}
}
lines.forEach((line, i) => {
if (line.startsWith('# ')) {
flushList(i)
elements.push(
<h1 key={i} className="text-3xl font-semibold tracking-tight mb-4 mt-6">
{line.substring(2)}
</h1>
)
} else if (line.startsWith('## ')) {
flushList(i)
const text = line.substring(3)
elements.push(
<h2 key={i} className="text-2xl font-semibold tracking-tight mb-3 mt-6 flex items-center gap-2">
{text}
</h2>
)
} else if (line.startsWith('### ')) {
flushList(i)
elements.push(
<h3 key={i} className="text-lg font-semibold mb-2 mt-4">
{line.substring(4)}
</h3>
)
} else if (line.startsWith('- ')) {
if (!inList) {
inList = true
}
const text = line.substring(2)
const isCompleted = text.startsWith('✅')
const isInProgress = text.startsWith('🔄')
const isPlanned = text.startsWith('📋')
listItems.push(
<li key={i} className="flex items-start gap-2 text-sm">
<span className="mt-0.5">
{isCompleted && <span className="text-success"></span>}
{isInProgress && <span className="text-warning">🔄</span>}
{isPlanned && <span className="text-muted-foreground">📋</span>}
{!isCompleted && !isInProgress && !isPlanned && <span className="text-muted-foreground"></span>}
</span>
<span className={cn(
isCompleted && 'text-foreground',
isInProgress && 'text-foreground font-medium',
isPlanned && 'text-muted-foreground'
)}>
{text.replace(/^[✅🔄📋]\s*/, '')}
</span>
</li>
)
} else if (line.startsWith('---')) {
flushList(i)
elements.push(<hr key={i} className="my-6 border-border" />)
} else if (line.trim() !== '') {
flushList(i)
elements.push(
<p key={i} className="text-sm text-muted-foreground mb-3">
{line}
</p>
)
}
})
flushList(lines.length)
return elements
}
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<div>
<h2 className="text-3xl font-semibold tracking-tight">Product Roadmap</h2>
<p className="text-muted-foreground mt-1">Development phases and feature timeline</p>
</div>
<div className="flex gap-2">
<Button variant="outline">
<Download size={18} className="mr-2" />
Download PDF
</Button>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<Card className="border-l-4 border-success/20">
<CardHeader>
<CardTitle className="text-sm text-muted-foreground flex items-center gap-2">
<CheckCircle size={18} className="text-success" weight="fill" />
Completed
</CardTitle>
</CardHeader>
<CardContent>
<div className="text-3xl font-semibold">Phase 1-2 + Features</div>
<p className="text-sm text-muted-foreground mt-1">Core platform with advanced features</p>
</CardContent>
</Card>
<Card className="border-l-4 border-warning/20">
<CardHeader>
<CardTitle className="text-sm text-muted-foreground flex items-center gap-2">
<ClockCounterClockwise size={18} className="text-warning" weight="fill" />
In Progress
</CardTitle>
</CardHeader>
<CardContent>
<div className="text-3xl font-semibold">Phase 2</div>
<p className="text-sm text-muted-foreground mt-1">Advanced Operations & Automation</p>
</CardContent>
</Card>
<Card className="border-l-4 border-accent/20">
<CardHeader>
<CardTitle className="text-sm text-muted-foreground flex items-center gap-2">
<MapTrifold size={18} className="text-accent" weight="fill" />
Total Phases
</CardTitle>
</CardHeader>
<CardContent>
<div className="text-3xl font-semibold">6</div>
<p className="text-sm text-muted-foreground mt-1">2 years timeline</p>
</CardContent>
</Card>
</div>
<Card>
<CardContent className="p-6 prose prose-sm max-w-none">
{parseMarkdown(roadmapContent)}
</CardContent>
</Card>
<Card className="bg-muted/50">
<CardContent className="p-6">
<h3 className="font-semibold mb-3 flex items-center gap-2">
<Warning size={20} className="text-warning" />
Release Cadence
</h3>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 text-sm">
<div>
<p className="text-muted-foreground">Major Releases</p>
<p className="font-medium">Quarterly</p>
</div>
<div>
<p className="text-muted-foreground">Minor Updates</p>
<p className="font-medium">Monthly</p>
</div>
<div>
<p className="text-muted-foreground">Patches</p>
<p className="font-medium">Weekly</p>
</div>
<div>
<p className="text-muted-foreground">Hotfixes</p>
<p className="font-medium">As needed</p>
</div>
</div>
</CardContent>
</Card>
</div>
)
}

View File

@@ -0,0 +1,5 @@
export { TimesheetsView } from './TimesheetsView'
export { BillingView } from './BillingView'
export { PayrollView } from './PayrollView'
export { ExpensesView } from './ExpensesView'
export { ComplianceView } from './ComplianceView'