mirror of
https://github.com/johndoe6345789/workforce-pay-bill-p.git
synced 2026-04-24 13:24:57 +00:00
9.5 KiB
9.5 KiB
JSON Data Structure
All UI data is now loaded from JSON files, making the application data-driven and easily configurable without code changes.
Data Source
Primary Data File: /src/data/app-data.json
This file contains all sample data for the application, including:
- Timesheets
- Invoices
- Payroll Runs
- Workers
- Compliance Documents
- Expenses
- Rate Cards
- Clients
How It Works
1. Data Loading Flow
app-data.json → use-sample-data hook → KV Storage → use-app-data hook → Components
- app-data.json - Static JSON file with initial/seed data
- use-sample-data - Loads JSON data into KV storage on first run
- use-app-data - Reads from KV storage and provides reactive state
- Components - Use the reactive data from use-app-data
2. Initial Data Load
When the application first loads (or when KV storage is empty), the useSampleData hook:
- Checks if data has been initialized (
sample-data-initializedflag) - If not initialized, loads data from
app-data.json - Populates KV storage with all data entities
- Sets the initialization flag to prevent re-loading
3. Persistent Storage
All data is stored in KV storage, which persists between sessions. This means:
- User modifications are preserved
- Data survives page refreshes
- Each user has their own data instance
Data Schema
Timesheets
{
id: string // Unique timesheet ID
workerId: string // Reference to worker
workerName: string // Worker's name
clientName: string // Client company name
weekEnding: string // ISO date string
totalHours: number // Total hours worked
regularHours: number // Standard hours
overtimeHours: number // Overtime hours
status: string // "pending" | "approved" | "disputed"
rate: number // Hourly rate
total: number // Total amount
submittedDate: string // ISO date-time string
approvedDate?: string // Optional approval date
shifts: Shift[] // Array of shift details
}
Shift: {
date: string // ISO date string
start: string // Time "HH:MM"
end: string // Time "HH:MM"
hours: number // Hours worked
type: string // "regular" | "night" | "weekend" | "overtime"
}
Invoices
{
id: string // Unique invoice ID
clientName: string // Client company name
amount: number // Net amount
vat: number // VAT/Tax amount
total: number // Gross total
status: string // "paid" | "pending" | "overdue" | "draft"
dueDate: string // ISO date string
invoiceDate: string // ISO date string
paidDate?: string // Optional payment date
items: InvoiceItem[] // Line items
}
InvoiceItem: {
description: string // Item description
quantity: number // Quantity
rate: number // Unit rate
amount: number // Total amount
}
Payroll Runs
{
id: string // Unique payroll run ID
periodEnding: string // ISO date string
status: string // "completed" | "pending" | "processing"
totalGross: number // Total gross pay
totalNet: number // Total net pay
totalTax: number // Total tax withheld
totalNI: number // Total National Insurance
processedDate?: string // Optional processing date
paymentDate?: string // Optional payment date
workerCount: number // Number of workers
entries: PayrollEntry[] // Individual worker entries
}
PayrollEntry: {
workerId: string // Reference to worker
workerName: string // Worker's name
gross: number // Gross pay
net: number // Net pay
tax: number // Tax withheld
ni: number // National Insurance
}
Workers
{
id: string // Unique worker ID
name: string // Full name
email: string // Email address
phone: string // Phone number
status: string // "active" | "inactive" | "onboarding"
role: string // Job role/title
startDate: string // ISO date string
paymentType: string // "Limited Company" | "PAYE" | "CIS"
currentClient: string // Current client assignment
}
Compliance Documents
{
id: string // Unique document ID
workerId: string // Reference to worker
workerName: string // Worker's name
documentType: string // Document type name
status: string // "valid" | "expiring" | "expired"
expiryDate: string // ISO date string
uploadDate: string // ISO date string
verifiedDate: string // ISO date string
}
Expenses
{
id: string // Unique expense ID
workerId: string // Reference to worker
workerName: string // Worker's name
category: string // "Travel" | "Accommodation" | "Meals" | "Equipment"
amount: number // Expense amount
date: string // ISO date string
status: string // "approved" | "pending" | "rejected"
description: string // Expense description
receiptAttached: boolean// Receipt attachment status
approvedDate?: string // Optional approval date
rejectedDate?: string // Optional rejection date
rejectionReason?: string// Optional rejection reason
}
Rate Cards
{
id: string // Unique rate card ID
role: string // Job role/title
clientName: string // Client company name
payRate: number // Hourly pay rate
chargeRate: number // Hourly charge rate
margin: number // Margin amount
marginPercent: number // Margin percentage
currency: string // Currency code (e.g., "GBP")
validFrom: string // ISO date string
validUntil: string // ISO date string
overtimeMultiplier: number // Overtime rate multiplier
weekendMultiplier: number // Weekend rate multiplier
nightMultiplier?: number // Optional night shift multiplier
}
Clients
{
id: string // Unique client ID
name: string // Company name
industry: string // Industry sector
status: string // "active" | "inactive" | "suspended"
creditLimit: number // Credit limit
outstandingBalance: number // Current outstanding balance
paymentTerms: number // Payment terms in days
activeWorkers: number // Number of active workers
address: string // Full address
}
Hooks
useJsonData()
Direct access to JSON data (read-only).
import { useJsonData } from '@/hooks'
function MyComponent() {
const { data, isLoading, error } = useJsonData()
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return <div>{data.workers.length} workers</div>
}
useSampleData()
Initializes KV storage with JSON data (runs automatically on app start).
import { useSampleData } from '@/hooks'
function App() {
useSampleData() // Call once at app root
// ...
}
useAppData()
Reactive access to data with setters (recommended for components).
import { useAppData } from '@/hooks'
function MyComponent() {
const { timesheets, setTimesheets, workers, metrics } = useAppData()
// Use data
console.log(timesheets.length)
// Update data
setTimesheets(prev => [...prev, newTimesheet])
}
Modifying Data
Option 1: Edit JSON File (Recommended for Bulk Changes)
- Edit
/src/data/app-data.json - Clear KV storage (or set
sample-data-initializedtofalse) - Refresh the application
Option 2: Programmatic Updates (Recommended for User Actions)
const { timesheets, setTimesheets } = useAppData()
// Add new timesheet
setTimesheets(current => [...current, newTimesheet])
// Update timesheet
setTimesheets(current =>
current.map(ts =>
ts.id === id ? { ...ts, status: 'approved' } : ts
)
)
// Delete timesheet
setTimesheets(current => current.filter(ts => ts.id !== id))
Adding New Data Entities
To add new data entities:
-
Add to JSON file (
/src/data/app-data.json){ "timesheets": [...], "newEntity": [ { "id": "1", "name": "Example" } ] } -
Add TypeScript interface (
/src/hooks/use-json-data.ts)export interface NewEntity { id: string name: string } export interface AppData { timesheets: Timesheet[] newEntity: NewEntity[] } -
Initialize in use-sample-data
const [, setNewEntity] = useKV<NewEntity[]>('new-entity', []) setNewEntity(appData.newEntity) -
Expose in use-app-data
const [newEntity = [], setNewEntity] = useKV<NewEntity[]>('new-entity', []) return { newEntity, setNewEntity, // ... }
Benefits
- Centralized Data: All sample data in one place
- Easy Configuration: Modify JSON instead of code
- Type Safety: Full TypeScript support
- Persistence: Data survives page refreshes
- Reactive: Automatic UI updates
- Testable: Easy to swap data for testing
- Scalable: Add new entities without code changes
Future Enhancements
- Support for multiple JSON files (e.g.,
timesheets.json,invoices.json) - Remote JSON loading from API
- Data validation on load
- Migration scripts for schema changes
- Import/export functionality
- Data versioning