diff --git a/src/components/views/ComplianceView.tsx b/src/components/views/ComplianceView.tsx
index 443c1c0..b334267 100644
--- a/src/components/views/ComplianceView.tsx
+++ b/src/components/views/ComplianceView.tsx
@@ -3,16 +3,21 @@ import {
UploadSimple,
Warning,
XCircle,
- CheckCircle
+ CheckCircle,
+ FileText
} from '@phosphor-icons/react'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
-import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
+import { Card, CardContent } from '@/components/ui/card'
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
+import { PageHeader } from '@/components/ui/page-header'
+import { Grid } from '@/components/ui/grid'
+import { Stack } from '@/components/ui/stack'
+import { MetricCard } from '@/components/ui/metric-card'
import { toast } from 'sonner'
import { ComplianceDetailDialog } from '@/components/ComplianceDetailDialog'
import { AdvancedSearch, type FilterField } from '@/components/AdvancedSearch'
@@ -44,6 +49,7 @@ export function ComplianceView({ complianceDocs, onUploadDocument }: ComplianceV
const expiringDocs = filteredDocs.filter(d => d.status === 'expiring')
const expiredDocs = filteredDocs.filter(d => d.status === 'expired')
+ const validDocs = filteredDocs.filter(d => d.status === 'valid')
const [uploadFormData, setUploadFormData] = useState({
workerId: '',
@@ -94,77 +100,77 @@ export function ComplianceView({ complianceDocs, onUploadDocument }: ComplianceV
}
return (
-
-
-
-
Compliance Monitoring
-
Track worker documentation and certifications
-
-
-
+
+
+
+
+
+
+
+ Upload Compliance Document
+
+ Add a new document for a worker
+
+
+
+
+
+ setUploadFormData({ ...uploadFormData, workerName: e.target.value })}
+ />
+
+
+
+
+
+
+
+ setUploadFormData({ ...uploadFormData, expiryDate: e.target.value })}
+ />
+
+
+
+
Click to upload or drag and drop
+
PDF, JPG, PNG up to 10MB
+
+
+
+
+
+
+
+
+ }
+ />
-
-
-
-
-
- Expiring Soon
-
-
-
- {expiringDocs.length}
- Documents expiring within 30 days
-
-
-
-
-
-
-
- Expired
-
-
-
- {expiredDocs.length}
- Workers blocked from engagement
-
-
-
+
+ }
+ />
+ }
+ />
+ }
+ />
+
@@ -256,7 +255,7 @@ export function ComplianceView({ complianceDocs, onUploadDocument }: ComplianceV
if (!open) setViewingDocument(null)
}}
/>
-
+
)
}
@@ -278,19 +277,19 @@ function ComplianceCard({ document, onViewDetails }: ComplianceCardProps) {
onViewDetails?.(document)}>
-
-
+
+
-
+
{document.workerName}
{document.status}
-
-
+
+
Document Type
{document.documentType}
@@ -309,14 +308,14 @@ function ComplianceCard({ document, onViewDetails }: ComplianceCardProps) {
{document.daysUntilExpiry < 0 ? 'Expired' : `${document.daysUntilExpiry} days`}
-
+
-
-
-
e.stopPropagation()}>
+
+
+ e.stopPropagation()}>
-
+
diff --git a/src/components/views/ExpensesView.tsx b/src/components/views/ExpensesView.tsx
index 37180ea..f8b5181 100644
--- a/src/components/views/ExpensesView.tsx
+++ b/src/components/views/ExpensesView.tsx
@@ -6,7 +6,8 @@ import {
CheckCircle,
ClockCounterClockwise,
XCircle,
- Camera
+ Camera,
+ CurrencyDollar
} from '@phosphor-icons/react'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
@@ -17,6 +18,10 @@ import { Label } from '@/components/ui/label'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { Textarea } from '@/components/ui/textarea'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
+import { PageHeader } from '@/components/ui/page-header'
+import { Grid } from '@/components/ui/grid'
+import { Stack } from '@/components/ui/stack'
+import { MetricCard } from '@/components/ui/metric-card'
import { toast } from 'sonner'
import { ExpenseDetailDialog } from '@/components/ExpenseDetailDialog'
import { AdvancedSearch, type FilterField } from '@/components/AdvancedSearch'
@@ -68,6 +73,26 @@ export function ExpensesView({
setFilteredExpenses(results)
}, [])
+ const pendingExpenses = useMemo(() =>
+ expenses.filter(e => e.status === 'pending'),
+ [expenses]
+ )
+
+ const approvedExpenses = useMemo(() =>
+ expenses.filter(e => e.status === 'approved'),
+ [expenses]
+ )
+
+ const totalPendingAmount = useMemo(() =>
+ pendingExpenses.reduce((sum, e) => sum + e.amount, 0),
+ [pendingExpenses]
+ )
+
+ const totalApprovedAmount = useMemo(() =>
+ approvedExpenses.reduce((sum, e) => sum + e.amount, 0),
+ [approvedExpenses]
+ )
+
const [formData, setFormData] = useState({
workerName: '',
clientName: '',
@@ -132,113 +157,113 @@ export function ExpensesView({
}
return (
-
-
-
-
Expense Management
-
Manage worker expenses and reimbursements
-
-
+
+
+ setFormData({ ...formData, clientName: e.target.value })}
+ />
+
+
+
+ setFormData({ ...formData, date: e.target.value })}
+ />
+
+
+
+
+
+
+
+
+
+
+ setFormData({ ...formData, amount: e.target.value })}
+ />
+
+
+
+
+
+
+
+
+
+
+
+ }
+ />
-
+
+ }
+ />
+ }
+ />
+ e.status === 'rejected').length}
+ icon={}
+ />
+ e.status === 'paid').length}
+ icon={}
+ />
+
+
+
@@ -340,7 +390,7 @@ export function ExpensesView({
onApprove={onApprove}
onReject={onReject}
/>
-
+
)
}
@@ -365,15 +415,15 @@ function ExpenseCard({ expense, onApprove, onReject, onViewDetails }: ExpenseCar
onViewDetails?.(expense)}>
-
-
+
+
-
+
{expense.workerName}
{expense.status}
@@ -381,8 +431,8 @@ function ExpenseCard({ expense, onApprove, onReject, onViewDetails }: ExpenseCar
{expense.billable && (
Billable
)}
-
-
+
+
Client
{expense.clientName}
@@ -403,7 +453,7 @@ function ExpenseCard({ expense, onApprove, onReject, onViewDetails }: ExpenseCar
Currency
{expense.currency}
-
+
{expense.description && (
{expense.description}
@@ -413,10 +463,10 @@ function ExpenseCard({ expense, onApprove, onReject, onViewDetails }: ExpenseCar
Submitted {new Date(expense.submittedDate).toLocaleDateString()}
-
-
+
+
-
e.stopPropagation()}>
+ e.stopPropagation()}>
{expense.status === 'pending' && onApprove && onReject && (
<>
+
diff --git a/src/components/views/PayrollView.tsx b/src/components/views/PayrollView.tsx
index 044a7e5..17079b7 100644
--- a/src/components/views/PayrollView.tsx
+++ b/src/components/views/PayrollView.tsx
@@ -4,12 +4,19 @@ import {
CurrencyDollar,
Download,
ChartBar,
- Calculator
+ Calculator,
+ Users,
+ CalendarBlank,
+ ClockCounterClockwise
} from '@phosphor-icons/react'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
-import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'
+import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'
+import { PageHeader } from '@/components/ui/page-header'
+import { Grid } from '@/components/ui/grid'
+import { Stack } from '@/components/ui/stack'
+import { MetricCard } from '@/components/ui/metric-card'
import { PayrollDetailDialog } from '@/components/PayrollDetailDialog'
import { OneClickPayroll } from '@/components/OneClickPayroll'
import { usePayrollCalculations } from '@/hooks/use-payroll-calculations'
@@ -67,98 +74,98 @@ export function PayrollView({ payrollRuns, timesheets, onPayrollComplete }: Payr
}
return (
-
-
-
-
Payroll Processing
-
Manage payroll runs and worker payments
-
-
-
-
+
+
+ {calculatorResult && (
+
+
+
+
+ Gross Pay
+
+ £{calculatorResult.grossPay.toFixed(2)}
+
+
+
+
+
+ Net Pay
+
+ £{calculatorResult.netPay.toFixed(2)}
+
+
+
+
+
+
+
+ Breakdown
+
+
+ {calculatorResult.breakdown.map((item: any, idx: number) => (
+
+ {item.description}
+
+ £{Math.abs(item.amount).toFixed(2)}
+
+
+ ))}
+
+
+
+
+
+ Tax Year: {payrollConfig.taxYear}
+ Personal Allowance: £{payrollConfig.personalAllowance.toLocaleString()}
+
+
+
+ )}
+
+
+
+
+
+ }
+ />
{showAnalytics && (
-
-
-
- Approved Timesheets
-
-
- {approvedTimesheets.length}
- Ready for payroll
-
-
-
-
- Pending Approval
-
-
- {pendingTimesheets.length}
-
- £{totalPendingValue.toLocaleString()} value
-
-
-
-
-
- Total Payroll Runs
-
-
- {payrollRuns.length}
-
-
-
-
- Last Run Total
-
-
-
- £{lastRun ? lastRun.totalAmount.toLocaleString() : '0'}
-
-
- {lastRun ? `${lastRun.workersCount} workers paid` : 'No runs yet'}
-
-
-
-
+
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+
)}
-
-
-
- Next Pay Date
-
-
- 22 Jan 2025
- Weekly run in 3 days
-
-
+
+ }
+ />
+ }
+ />
+ }
+ />
+
-
-
- Pending Approval
-
-
- {pendingTimesheets.length} timesheets
- Must be approved for payroll
-
-
-
-
-
- Last Run Total
-
-
-
- £{lastRun ? lastRun.totalAmount.toLocaleString() : '0'}
-
-
- {lastRun ? `${lastRun.workersCount} workers paid` : 'No runs yet'}
-
-
-
-
-
-
+
{payrollRuns.map(run => (
-
-
+
+
Payroll Run
{run.status}
-
-
+
+
Period Ending
{new Date(run.periodEnding).toLocaleDateString()}
@@ -283,9 +257,9 @@ export function PayrollView({ payrollRuns, timesheets, onPayrollComplete }: Payr
{run.processedDate ? new Date(run.processedDate).toLocaleDateString() : 'Not yet'}
-
-
-
e.stopPropagation()}>
+
+
+ e.stopPropagation()}>
@@ -295,7 +269,7 @@ export function PayrollView({ payrollRuns, timesheets, onPayrollComplete }: Payr
Export
)}
-
+
@@ -308,7 +282,7 @@ export function PayrollView({ payrollRuns, timesheets, onPayrollComplete }: Payr
Create your first payroll run to get started
)}
-
+
-
+
)
}