refactor: remove deprecated components and streamline member coverage calculations, enhance budget management with improved payroll handling, and update UI elements for better clarity

This commit is contained in:
Jennie Robinson Faber 2025-09-06 09:48:57 +01:00
parent 983aeca2dc
commit 09d8794d72
42 changed files with 2166 additions and 2974 deletions

View file

@ -1,15 +1,12 @@
export type PayRelationship =
| 'equal-pay'
| 'needs-weighted'
| 'role-banded'
| 'hours-weighted'
| 'custom-formula';
export interface Member {
id: string
displayName: string
roleFocus?: string
role?: string
hoursPerWeek?: number
hoursPerMonth?: number
capacity?: {
@ -21,10 +18,8 @@ export interface Member {
// Existing/planned
monthlyPayPlanned?: number
// NEW - early-stage friendly, defaults-safe
// Simplified - only minimum needs for needs-weighted allocation
minMonthlyNeeds?: number
targetMonthlyPay?: number
externalMonthlyIncome?: number
// Compatibility with existing store
payRelationship?: string
@ -35,8 +30,7 @@ export interface Member {
quarterlyDeferredCap?: number
// UI-only derivations
coverageMinPct?: number
coverageTargetPct?: number
coveragePct?: number
}
export interface PayPolicy {
@ -44,22 +38,19 @@ export interface PayPolicy {
notes?: string
equalBase?: number
needsWeight?: number
roleBands?: Record<string, number>
hoursRate?: number
customFormula?: string
}
// Coverage calculation helpers
export function coverage(minNeeds = 0, target = 0, planned = 0, external = 0) {
const base = planned + external
const min = minNeeds > 0 ? Math.min(200, (base / minNeeds) * 100) : undefined
const tgt = target > 0 ? Math.min(200, (base / target) * 100) : undefined
return { minPct: min, targetPct: tgt }
// Simplified coverage calculation
export function coverage(minNeeds = 0, planned = 0) {
const coveragePct = minNeeds > 0 ? Math.min(200, (planned / minNeeds) * 100) : undefined
return { coveragePct }
}
export function teamCoverageStats(members: Member[]) {
const vals = members
.map(m => coverage(m.minMonthlyNeeds, m.targetMonthlyPay, m.monthlyPayPlanned, m.externalMonthlyIncome).minPct)
.map(m => coverage(m.minMonthlyNeeds, m.monthlyPayPlanned).coveragePct)
.filter((v): v is number => typeof v === 'number')
if (!vals.length) return { under100: 0, median: undefined, range: undefined, gini: undefined }
@ -104,12 +95,7 @@ export function allocatePayroll(members: Member[], policy: PayPolicy, payrollBud
return result
}
if (policy.relationship === 'role-banded' && policy.roleBands) {
const bands = result.map(m => policy.roleBands![m.role ?? ''] ?? 0)
const sum = bands.reduce((a, b) => a + b, 0) || 1
result.forEach((m, i) => m.monthlyPayPlanned = payrollBudget * (bands[i] / sum))
return result
}
// Removed role-banded allocation - no longer supported
if (policy.relationship === 'hours-weighted') {
const hours = result.map(m => m.hoursPerMonth ?? (m.hoursPerWeek ? m.hoursPerWeek * 4 : 0) ?? (m.capacity?.targetHours ?? 0))
@ -124,7 +110,7 @@ export function allocatePayroll(members: Member[], policy: PayPolicy, payrollBud
return result
}
// Monthly payroll calculation for runway and cashflow
// Monthly payroll calculation for runway and budgets
export function monthlyPayroll(members: Member[], mode: 'minimum' | 'target' = 'minimum'): number {
return members.reduce((sum, m) => {
const planned = m.monthlyPayPlanned ?? 0