refactor: remove CashFlowChart and UnifiedCashFlowDashboard components, update routing paths in app.vue, and enhance budget page with cumulative balance calculations and payroll explanation modal for improved user experience
This commit is contained in:
parent
864a81065c
commit
f1889b3a70
17 changed files with 922 additions and 1004 deletions
112
pages/budget.vue
112
pages/budget.vue
|
|
@ -28,6 +28,9 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<UButton @click="showCalculationModal = true" variant="ghost" size="sm">
|
||||
How are these calculated?
|
||||
</UButton>
|
||||
<UButton @click="exportBudget" variant="ghost" size="sm">
|
||||
Export
|
||||
</UButton>
|
||||
|
|
@ -222,7 +225,7 @@
|
|||
<div class="font-medium flex items-start gap-2">
|
||||
<UTooltip
|
||||
v-if="isPayrollItem(item.id)"
|
||||
text="Calculated from compensation settings"
|
||||
text="Calculated based on available revenue after overhead costs. This represents realistic, sustainable payroll."
|
||||
:content="{ side: 'top', align: 'start' }">
|
||||
<span class="cursor-help">{{ item.name }}</span>
|
||||
</UTooltip>
|
||||
|
|
@ -315,6 +318,23 @@
|
|||
{{ formatCurrency(monthlyTotals[month.key]?.net || 0) }}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Cumulative Balance Row -->
|
||||
<tr class="border-t-1 border-gray-400 font-bold text-lg bg-blue-50">
|
||||
<td
|
||||
class="border-r-1 border-black px-4 py-3 sticky left-0 bg-blue-50 z-10">
|
||||
CUMULATIVE BALANCE
|
||||
</td>
|
||||
<td
|
||||
v-for="month in monthlyHeaders"
|
||||
:key="month.key"
|
||||
class="border-r border-gray-400 px-2 py-3 text-right last:border-r-0"
|
||||
:class="
|
||||
getCumulativeBalanceClass(cumulativeBalances[month.key] || 0)
|
||||
">
|
||||
{{ formatCurrency(cumulativeBalances[month.key] || 0) }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
@ -622,6 +642,77 @@
|
|||
<PayrollOncostModal
|
||||
v-model:open="showPayrollOncostModal"
|
||||
@save="handlePayrollOncostUpdate" />
|
||||
|
||||
<!-- Calculation Explanation Modal -->
|
||||
<UModal v-model:open="showCalculationModal" title="How Budget Calculations Work">
|
||||
<template #content>
|
||||
<div class="space-y-6 max-w-2xl p-6">
|
||||
<!-- Revenue Section -->
|
||||
<div>
|
||||
<h4 class="font-semibold text-green-600 mb-2">📈 Revenue Calculation</h4>
|
||||
<p class="text-sm text-gray-600 mb-2">Revenue comes from your setup wizard streams and any manual additions:</p>
|
||||
<ul class="text-sm text-gray-600 space-y-1 ml-4">
|
||||
<li>• Monthly amounts you entered for each revenue stream</li>
|
||||
<li>• Varies by month based on your specific projections</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Payroll Section -->
|
||||
<div>
|
||||
<h4 class="font-semibold text-blue-600 mb-2">👥 Smart Payroll Calculation</h4>
|
||||
<p class="text-sm text-gray-600 mb-2">Payroll uses a <strong>cumulative balance approach</strong> to ensure sustainability:</p>
|
||||
<div class="bg-blue-50 border border-blue-200 rounded p-3 text-sm">
|
||||
<p class="font-medium mb-2">Step-by-step process:</p>
|
||||
<ol class="space-y-1 ml-4">
|
||||
<li>1. Calculate available funds: Revenue - Other Expenses</li>
|
||||
<li>2. Check if this maintains minimum cash threshold (${{ $format.currency(coopBuilderStore.minCashThreshold || 0) }})</li>
|
||||
<li>3. Allocate using your chosen policy ({{ getPolicyName() }})</li>
|
||||
<li>4. Account for payroll taxes ({{ coopBuilderStore.payrollOncostPct || 0 }}%)</li>
|
||||
<li>5. Ensure cumulative balance doesn't fall below threshold</li>
|
||||
</ol>
|
||||
</div>
|
||||
<p class="text-sm text-gray-600 mt-2">
|
||||
This means payroll varies by month - higher in good cash flow months, lower when cash is tight.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Cumulative Balance Section -->
|
||||
<div>
|
||||
<h4 class="font-semibold text-purple-600 mb-2">💰 Cumulative Balance</h4>
|
||||
<p class="text-sm text-gray-600 mb-2">Shows your running cash position over time:</p>
|
||||
<ul class="text-sm text-gray-600 space-y-1 ml-4">
|
||||
<li>• Starts at $0 (current cash position)</li>
|
||||
<li>• Adds each month's net income (Revenue - All Expenses)</li>
|
||||
<li>• Helps you see when cash might run low</li>
|
||||
<li>• Payroll is reduced to prevent going below minimum threshold</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Policy Explanation -->
|
||||
<div>
|
||||
<h4 class="font-semibold text-orange-600 mb-2">⚖️ Pay Policy: {{ getPolicyName() }}</h4>
|
||||
<div class="text-sm text-gray-600">
|
||||
<p v-if="coopBuilderStore.policy?.relationship === 'equal-pay'">
|
||||
Everyone gets equal hourly wage (${{ coopBuilderStore.equalHourlyWage || 0 }}/hour) based on their monthly hours.
|
||||
</p>
|
||||
<p v-else-if="coopBuilderStore.policy?.relationship === 'needs-weighted'">
|
||||
Pay is allocated proportionally based on each member's minimum monthly needs, ensuring fair coverage.
|
||||
</p>
|
||||
<p v-else-if="coopBuilderStore.policy?.relationship === 'hours-weighted'">
|
||||
Pay is allocated proportionally based on hours worked, with higher hours getting more pay.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-50 border border-gray-200 rounded p-3">
|
||||
<p class="text-sm text-gray-700">
|
||||
<strong>Key insight:</strong> This system prioritizes sustainability over theoretical maximums.
|
||||
You might not always get full theoretical wages, but you'll never run out of cash.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</UModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -749,6 +840,7 @@ const activeView = ref("monthly");
|
|||
const showAddRevenueModal = ref(false);
|
||||
const showAddExpenseModal = ref(false);
|
||||
const showPayrollOncostModal = ref(false);
|
||||
const showCalculationModal = ref(false);
|
||||
const activeTab = ref(0);
|
||||
const highlightedItemId = ref<string | null>(null);
|
||||
|
||||
|
|
@ -863,6 +955,7 @@ const budgetWorksheet = computed(
|
|||
const groupedRevenue = computed(() => budgetStore.groupedRevenue);
|
||||
const groupedExpenses = computed(() => budgetStore.groupedExpenses);
|
||||
const monthlyTotals = computed(() => budgetStore.monthlyTotals);
|
||||
const cumulativeBalances = computed(() => budgetStore.cumulativeBalances);
|
||||
|
||||
// Initialize on mount
|
||||
// Removed duplicate onMounted - initialization is now handled above
|
||||
|
|
@ -1141,6 +1234,23 @@ function getNetIncomeClass(amount: number): string {
|
|||
return "text-gray-600";
|
||||
}
|
||||
|
||||
function getCumulativeBalanceClass(amount: number): string {
|
||||
if (amount > 50000) return "text-green-700 font-bold"; // Healthy cash position
|
||||
if (amount > 10000) return "text-green-600 font-bold"; // Good cash position
|
||||
if (amount > 0) return "text-blue-600 font-bold"; // Positive but low
|
||||
if (amount > -10000) return "text-orange-600 font-bold"; // Concerning
|
||||
return "text-red-700 font-bold"; // Critical cash position
|
||||
}
|
||||
|
||||
function getPolicyName(): string {
|
||||
const policyType = coopBuilderStore.policy?.relationship || 'equal-pay';
|
||||
|
||||
if (policyType === 'equal-pay') return 'Equal Pay';
|
||||
if (policyType === 'hours-weighted') return 'Hours Based';
|
||||
if (policyType === 'needs-weighted') return 'Needs Weighted';
|
||||
return 'Equal Pay';
|
||||
}
|
||||
|
||||
// Payroll oncost handling
|
||||
function handlePayrollOncostUpdate(newPercentage: number) {
|
||||
// Update the coop store
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue