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,58 @@
/**
* Calculates weighted average payout delay across revenue streams
*/
export const usePayoutExposure = () => {
const calculateWeightedAverageDelay = (
streams: Array<{ targetMonthlyAmount: number; payoutDelayDays: number }>
): number => {
const totalRevenue = streams.reduce((sum, stream) => sum + (stream.targetMonthlyAmount || 0), 0)
if (totalRevenue === 0) return 0
const weightedSum = streams.reduce((sum, stream) => {
const weight = (stream.targetMonthlyAmount || 0) / totalRevenue
return sum + (weight * (stream.payoutDelayDays || 0))
}, 0)
return weightedSum
}
const getPayoutExposureStatus = (avgDelayDays: number, minCashCushion: number): 'green' | 'yellow' | 'red' => {
// Flag if weighted average >30 days and cushion <1 month
if (avgDelayDays > 30 && minCashCushion < 8000) return 'red' // Assuming ~8k = 1 month expenses
if (avgDelayDays > 21) return 'yellow'
return 'green'
}
const getPayoutExposureMessage = (status: 'green' | 'yellow' | 'red'): string => {
switch (status) {
case 'red':
return 'Money is earned now but arrives later. Delays can create mid-month dips.'
case 'yellow':
return 'Some payout delays present. Monitor cash timing.'
case 'green':
return 'Payout timing looks manageable.'
}
}
const analyzePayoutExposure = (
streams: Array<{ targetMonthlyAmount: number; payoutDelayDays: number }>,
minCashCushion: number
) => {
const avgDelayDays = calculateWeightedAverageDelay(streams)
const status = getPayoutExposureStatus(avgDelayDays, minCashCushion)
return {
avgDelayDays: Math.round(avgDelayDays),
status,
message: getPayoutExposureMessage(status)
}
}
return {
calculateWeightedAverageDelay,
getPayoutExposureStatus,
getPayoutExposureMessage,
analyzePayoutExposure
}
}