246 lines
6.5 KiB
TypeScript
246 lines
6.5 KiB
TypeScript
/**
|
|
* Composable for loading and managing fixture data
|
|
* Provides centralized access to demo data for all screens
|
|
*/
|
|
export const useFixtures = () => {
|
|
// Load fixture data (in real app, this would come from API or stores)
|
|
const loadMembers = async () => {
|
|
// In production, this would fetch from content/fixtures/members.json
|
|
// For now, return inline data that matches the fixture structure
|
|
return {
|
|
members: [
|
|
{
|
|
id: 'member-1',
|
|
displayName: 'Alex Chen',
|
|
roleFocus: 'Technical Lead',
|
|
payRelationship: 'Hybrid',
|
|
capacity: { minHours: 20, targetHours: 120, maxHours: 160 },
|
|
riskBand: 'Medium',
|
|
externalCoveragePct: 60,
|
|
privacyNeeds: 'aggregate_ok',
|
|
deferredHours: 85,
|
|
quarterlyDeferredCap: 240
|
|
},
|
|
{
|
|
id: 'member-2',
|
|
displayName: 'Jordan Silva',
|
|
roleFocus: 'Design & UX',
|
|
payRelationship: 'FullyPaid',
|
|
capacity: { minHours: 30, targetHours: 140, maxHours: 180 },
|
|
riskBand: 'Low',
|
|
externalCoveragePct: 20,
|
|
privacyNeeds: 'aggregate_ok',
|
|
deferredHours: 0,
|
|
quarterlyDeferredCap: 240
|
|
},
|
|
{
|
|
id: 'member-3',
|
|
displayName: 'Sam Rodriguez',
|
|
roleFocus: 'Operations & Growth',
|
|
payRelationship: 'Supplemental',
|
|
capacity: { minHours: 10, targetHours: 60, maxHours: 100 },
|
|
riskBand: 'High',
|
|
externalCoveragePct: 85,
|
|
privacyNeeds: 'steward_only',
|
|
deferredHours: 32,
|
|
quarterlyDeferredCap: 120
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
const loadStreams = async () => {
|
|
return {
|
|
revenueStreams: [
|
|
{
|
|
id: 'stream-1',
|
|
name: 'Client Services',
|
|
category: 'Services',
|
|
subcategory: 'Development',
|
|
targetPct: 65,
|
|
targetMonthlyAmount: 13000,
|
|
certainty: 'Committed',
|
|
payoutDelayDays: 30,
|
|
terms: 'Net 30',
|
|
revenueSharePct: 0,
|
|
platformFeePct: 0,
|
|
restrictions: 'General',
|
|
effortHoursPerMonth: 180
|
|
},
|
|
{
|
|
id: 'stream-2',
|
|
name: 'Platform Sales',
|
|
category: 'Product',
|
|
subcategory: 'Digital Tools',
|
|
targetPct: 20,
|
|
targetMonthlyAmount: 4000,
|
|
certainty: 'Probable',
|
|
payoutDelayDays: 14,
|
|
terms: 'Platform payout',
|
|
revenueSharePct: 0,
|
|
platformFeePct: 5,
|
|
restrictions: 'General',
|
|
effortHoursPerMonth: 40
|
|
},
|
|
{
|
|
id: 'stream-3',
|
|
name: 'Innovation Grant',
|
|
category: 'Grant',
|
|
subcategory: 'Government',
|
|
targetPct: 10,
|
|
targetMonthlyAmount: 2000,
|
|
certainty: 'Committed',
|
|
payoutDelayDays: 45,
|
|
terms: 'Quarterly disbursement',
|
|
revenueSharePct: 0,
|
|
platformFeePct: 0,
|
|
restrictions: 'Restricted',
|
|
effortHoursPerMonth: 8
|
|
},
|
|
{
|
|
id: 'stream-4',
|
|
name: 'Community Donations',
|
|
category: 'Donation',
|
|
subcategory: 'Individual',
|
|
targetPct: 3,
|
|
targetMonthlyAmount: 600,
|
|
certainty: 'Aspirational',
|
|
payoutDelayDays: 3,
|
|
terms: 'Immediate',
|
|
revenueSharePct: 0,
|
|
platformFeePct: 2.9,
|
|
restrictions: 'General',
|
|
effortHoursPerMonth: 5
|
|
},
|
|
{
|
|
id: 'stream-5',
|
|
name: 'Consulting & Training',
|
|
category: 'Other',
|
|
subcategory: 'Professional Services',
|
|
targetPct: 2,
|
|
targetMonthlyAmount: 400,
|
|
certainty: 'Probable',
|
|
payoutDelayDays: 21,
|
|
terms: 'Net 21',
|
|
revenueSharePct: 0,
|
|
platformFeePct: 0,
|
|
restrictions: 'General',
|
|
effortHoursPerMonth: 12
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
const loadFinances = async () => {
|
|
return {
|
|
currentBalances: {
|
|
cash: 5000,
|
|
savings: 8000,
|
|
totalLiquid: 13000
|
|
},
|
|
policies: {
|
|
equalHourlyWage: 20,
|
|
payrollOncostPct: 25,
|
|
savingsTargetMonths: 3,
|
|
minCashCushionAmount: 3000,
|
|
deferredCapHoursPerQtr: 240,
|
|
deferredSunsetMonths: 12
|
|
},
|
|
deferredLiabilities: {
|
|
totalDeferred: 2340,
|
|
byMember: {
|
|
'member-1': 1700,
|
|
'member-2': 0,
|
|
'member-3': 640
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const loadCosts = async () => {
|
|
return {
|
|
overheadCosts: [
|
|
{
|
|
id: 'overhead-1',
|
|
name: 'Coworking Space',
|
|
amount: 0,
|
|
category: 'Workspace',
|
|
recurring: true
|
|
},
|
|
{
|
|
id: 'overhead-2',
|
|
name: 'Tools & Software',
|
|
amount: 0,
|
|
category: 'Technology',
|
|
recurring: true
|
|
},
|
|
{
|
|
id: 'overhead-3',
|
|
name: 'Business Insurance',
|
|
amount: 0,
|
|
category: 'Legal & Compliance',
|
|
recurring: true
|
|
}
|
|
],
|
|
productionCosts: [
|
|
{
|
|
id: 'production-1',
|
|
name: 'Development Kits',
|
|
amount: 0,
|
|
category: 'Hardware',
|
|
period: '2024-01'
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
// Calculate derived metrics from fixture data
|
|
const calculateMetrics = async () => {
|
|
const [members, streams, finances, costs] = await Promise.all([
|
|
loadMembers(),
|
|
loadStreams(),
|
|
loadFinances(),
|
|
loadCosts()
|
|
])
|
|
|
|
const totalTargetHours = members.members.reduce((sum, member) =>
|
|
sum + member.capacity.targetHours, 0
|
|
)
|
|
|
|
const totalTargetRevenue = streams.revenueStreams.reduce((sum, stream) =>
|
|
sum + stream.targetMonthlyAmount, 0
|
|
)
|
|
|
|
const totalOverheadCosts = costs.overheadCosts.reduce((sum, cost) =>
|
|
sum + cost.amount, 0
|
|
)
|
|
|
|
const monthlyPayroll = totalTargetHours * finances.policies.equalHourlyWage *
|
|
(1 + finances.policies.payrollOncostPct / 100)
|
|
|
|
const monthlyBurn = monthlyPayroll + totalOverheadCosts +
|
|
costs.productionCosts.reduce((sum, cost) => sum + cost.amount, 0)
|
|
|
|
const runway = finances.currentBalances.totalLiquid / monthlyBurn
|
|
|
|
return {
|
|
totalTargetHours,
|
|
totalTargetRevenue,
|
|
monthlyPayroll,
|
|
monthlyBurn,
|
|
runway,
|
|
members: members.members,
|
|
streams: streams.revenueStreams,
|
|
finances: finances,
|
|
costs: costs
|
|
}
|
|
}
|
|
|
|
return {
|
|
loadMembers,
|
|
loadStreams,
|
|
loadFinances,
|
|
loadCosts,
|
|
calculateMetrics
|
|
}
|
|
}
|