84 lines
No EOL
3.2 KiB
Vue
84 lines
No EOL
3.2 KiB
Vue
<template>
|
|
<div class="space-y-8">
|
|
<div class="text-center">
|
|
<h1 class="text-3xl font-bold mb-4">Project Budget Estimate</h1>
|
|
<p class="text-gray-600 max-w-2xl mx-auto mb-4">
|
|
Get a quick estimate of what it would cost to build your project with fair pay.
|
|
This tool helps worker co-ops sketch project budgets and break-even scenarios.
|
|
</p>
|
|
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4 max-w-2xl mx-auto">
|
|
<div class="flex items-start gap-2">
|
|
<UIcon name="i-heroicons-information-circle" class="h-5 w-5 text-blue-500 mt-0.5 flex-shrink-0" />
|
|
<div class="text-sm text-blue-800">
|
|
<p class="font-medium mb-1">About the calculations:</p>
|
|
<p>These estimates are based on <strong>sustainable payroll</strong> — what you can actually afford to pay based on your revenue minus overhead costs. This may be different from theoretical maximum wages if revenue is limited.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="membersWithPay.length === 0" class="text-center py-8">
|
|
<p class="text-gray-600 mb-4">No team members set up yet.</p>
|
|
<NuxtLink
|
|
to="/coop-builder"
|
|
class="px-4 py-2 border-2 border-black bg-white font-bold hover:bg-gray-100"
|
|
>
|
|
Set up your team in Setup Wizard
|
|
</NuxtLink>
|
|
</div>
|
|
|
|
<ProjectBudgetEstimate
|
|
v-else
|
|
:members="membersWithPay"
|
|
:oncost-rate="coopStore.payrollOncostPct / 100"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
const coopStore = useCoopBuilderStore()
|
|
const budgetStore = useBudgetStore()
|
|
|
|
// Calculate member pay using the same allocation logic as the budget system
|
|
const membersWithPay = computed(() => {
|
|
// Get current month's payroll from budget store (matches budget page)
|
|
const today = new Date()
|
|
const currentMonthKey = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, "0")}`
|
|
|
|
const payrollExpense = budgetStore.budgetWorksheet.expenses.find(item =>
|
|
item.id === "expense-payroll-base" || item.id === "expense-payroll"
|
|
)
|
|
const actualPayrollBudget = payrollExpense?.monthlyValues?.[currentMonthKey] || 0
|
|
|
|
// Use the member's desired hours (targetHours if available, otherwise hoursPerMonth)
|
|
const getHoursForMember = (member: any) => {
|
|
return member.capacity?.targetHours || member.hoursPerMonth || 0
|
|
}
|
|
|
|
// Get theoretical allocation then scale to actual budget
|
|
const { allocatePayroll } = useCoopBuilder()
|
|
const theoreticalMembers = allocatePayroll()
|
|
const theoreticalTotal = theoreticalMembers.reduce((sum, m) => sum + (m.monthlyPayPlanned || 0), 0)
|
|
const scaleFactor = theoreticalTotal > 0 ? actualPayrollBudget / theoreticalTotal : 0
|
|
|
|
const allocatedMembers = theoreticalMembers.map(member => ({
|
|
...member,
|
|
monthlyPayPlanned: (member.monthlyPayPlanned || 0) * scaleFactor
|
|
}))
|
|
|
|
return allocatedMembers.map(member => {
|
|
const hours = getHoursForMember(member)
|
|
|
|
return {
|
|
name: member.name || 'Unnamed',
|
|
hoursPerMonth: hours,
|
|
monthlyPay: member.monthlyPayPlanned || 0
|
|
}
|
|
}).filter(m => m.hoursPerMonth > 0) // Only include members with hours
|
|
})
|
|
|
|
// Set page meta
|
|
definePageMeta({
|
|
title: 'Project Budget Estimate'
|
|
})
|
|
</script> |