feat: add initial application structure with configuration, UI components, and state management

This commit is contained in:
Jennie Robinson Faber 2025-08-09 18:13:16 +01:00
parent fadf94002c
commit 0af6b17792
56 changed files with 6137 additions and 129 deletions

View file

@ -0,0 +1,89 @@
/**
* Calculates deferred balance and ratio vs monthly payroll
* Formula: total deferred wage liability ÷ one month of payroll
*/
export const useDeferredMetrics = () => {
const calculateDeferredBalance = (
members: Array<{ deferredHours: number }>,
hourlyWage: number
): number => {
const totalDeferredHours = members.reduce((sum, member) => sum + (member.deferredHours || 0), 0)
return totalDeferredHours * hourlyWage
}
const calculateMonthlyPayroll = (
members: Array<{ targetHours: number }>,
hourlyWage: number,
oncostPct: number
): number => {
const totalTargetHours = members.reduce((sum, member) => sum + (member.targetHours || 0), 0)
const grossPayroll = totalTargetHours * hourlyWage
return grossPayroll * (1 + oncostPct / 100)
}
const calculateDeferredRatio = (
deferredBalance: number,
monthlyPayroll: number
): number => {
if (monthlyPayroll <= 0) return 0
return deferredBalance / monthlyPayroll
}
const getDeferredRatioStatus = (ratio: number): 'green' | 'yellow' | 'red' => {
if (ratio > 1.5) return 'red' // Flag red if >1.5× monthly payroll
if (ratio > 1.0) return 'yellow'
return 'green'
}
const getDeferredRatioMessage = (status: 'green' | 'yellow' | 'red'): string => {
switch (status) {
case 'red':
return 'Deferred balance is high. Consider repaying or reducing scope.'
case 'yellow':
return 'Deferred balance is building up. Monitor closely.'
case 'green':
return 'Deferred balance is manageable.'
}
}
const checkDeferredCap = (
memberHours: number,
capHoursPerQtr: number,
quarterProgress: number
): { withinCap: boolean; remainingHours: number } => {
const quarterlyLimit = capHoursPerQtr * quarterProgress
const withinCap = memberHours <= quarterlyLimit
const remainingHours = Math.max(0, quarterlyLimit - memberHours)
return { withinCap, remainingHours }
}
const analyzeDeferredMetrics = (
members: Array<{ deferredHours?: number; targetHours?: number }>,
hourlyWage: number,
oncostPct: number
) => {
const deferredBalance = calculateDeferredBalance(members, hourlyWage)
const monthlyPayroll = calculateMonthlyPayroll(members, hourlyWage, oncostPct)
const ratio = calculateDeferredRatio(deferredBalance, monthlyPayroll)
const status = getDeferredRatioStatus(ratio)
return {
deferredBalance,
monthlyPayroll,
ratio: Number(ratio.toFixed(2)),
status,
message: getDeferredRatioMessage(status)
}
}
return {
calculateDeferredBalance,
calculateMonthlyPayroll,
calculateDeferredRatio,
getDeferredRatioStatus,
getDeferredRatioMessage,
checkDeferredCap,
analyzeDeferredMetrics
}
}