app/composables/useFixtures.ts

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
}
}