refactor: update app.vue and various components to improve routing paths, enhance UI consistency, and streamline layout for better user experience
This commit is contained in:
parent
b6e8d3b7ec
commit
78af43770c
29 changed files with 1699 additions and 1990 deletions
|
|
@ -1,130 +0,0 @@
|
|||
<template>
|
||||
<div class="space-y-8">
|
||||
<div class="">
|
||||
<h1 class="font-bold text-2xl mb-4">Project Budget Estimate</h1>
|
||||
<p class="text-neutral-600 dark:text-neutral-400 mx-auto mb-4">
|
||||
This tool provides a rough estimate of what it would cost to build your
|
||||
project using the pay policy you've set in the setup.
|
||||
</p>
|
||||
<div class="space-y-4">
|
||||
<!-- Sustainable payroll toggle hidden - defaulting to theoretical maximum -->
|
||||
<div class="hidden">
|
||||
<span class="text-sm font-medium">Sustainable Payroll</span>
|
||||
<USwitch v-model="useTheoreticalPayroll" size="md" />
|
||||
<span class="text-sm font-medium">Theoretical Maximum</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="membersWithPay.length === 0" class="text-center py-8">
|
||||
<p class="text-neutral-600 dark:text-neutral-400 mb-4">
|
||||
No team members set up yet.
|
||||
</p>
|
||||
<NuxtLink
|
||||
to="/coop-builder"
|
||||
class="px-4 py-2 border-2 border-black dark:border-white bg-white dark:bg-black text-black dark:text-white font-bold hover:bg-neutral-100 dark:hover:bg-neutral-900">
|
||||
Set up your team in Setup Wizard
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
<ProjectBudgetEstimate
|
||||
v-else
|
||||
:members="membersWithPay"
|
||||
:oncost-rate="coopStore.payrollOncostPct / 100"
|
||||
:payroll-mode="useTheoreticalPayroll ? 'theoretical' : 'sustainable'" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { allocatePayroll as allocatePayrollImpl } from "~/types/members";
|
||||
|
||||
const coopStore = useCoopBuilderStore();
|
||||
const budgetStore = useBudgetStore();
|
||||
|
||||
// Toggle between sustainable and theoretical payroll modes - defaulting to theoretical maximum
|
||||
const useTheoreticalPayroll = ref(true);
|
||||
|
||||
// Calculate member pay using different logic based on payroll mode
|
||||
const membersWithPay = computed(() => {
|
||||
// Use the member's desired hours (targetHours if available, otherwise hoursPerMonth)
|
||||
const getHoursForMember = (member: any) => {
|
||||
return member.capacity?.targetHours || member.hoursPerMonth || 0;
|
||||
};
|
||||
|
||||
let allocatedMembers;
|
||||
|
||||
if (useTheoreticalPayroll.value) {
|
||||
// Theoretical mode: Calculate true theoretical maximum without revenue constraints
|
||||
const allMembers = coopStore.members.map((m: any) => ({
|
||||
...m,
|
||||
displayName: m.name,
|
||||
monthlyPayPlanned: m.monthlyPayPlanned || 0,
|
||||
minMonthlyNeeds: m.minMonthlyNeeds || 0,
|
||||
hoursPerMonth: m.hoursPerMonth || 0,
|
||||
}));
|
||||
|
||||
const payPolicy = {
|
||||
relationship: coopStore.policy.relationship || ("equal-pay" as const),
|
||||
};
|
||||
|
||||
// Calculate theoretical maximum budget: total hours × hourly wage
|
||||
const totalHours = allMembers.reduce(
|
||||
(sum, m) => sum + (m.hoursPerMonth || 0),
|
||||
0
|
||||
);
|
||||
const hourlyWage = coopStore.equalHourlyWage || 0;
|
||||
const theoreticalMaxBudget = totalHours * hourlyWage;
|
||||
|
||||
allocatedMembers = allocatePayrollImpl(
|
||||
allMembers,
|
||||
payPolicy,
|
||||
theoreticalMaxBudget
|
||||
);
|
||||
} else {
|
||||
// Sustainable mode: Use revenue-constrained allocation (current behavior)
|
||||
const { allocatePayroll } = useCoopBuilder();
|
||||
const sustainableMembers = allocatePayroll();
|
||||
|
||||
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;
|
||||
|
||||
const theoreticalTotal = sustainableMembers.reduce(
|
||||
(sum, m) => sum + (m.monthlyPayPlanned || 0),
|
||||
0
|
||||
);
|
||||
const scaleFactor =
|
||||
theoreticalTotal > 0 ? actualPayrollBudget / theoreticalTotal : 0;
|
||||
|
||||
allocatedMembers = sustainableMembers.map((member) => ({
|
||||
...member,
|
||||
monthlyPayPlanned: (member.monthlyPayPlanned || 0) * scaleFactor,
|
||||
}));
|
||||
}
|
||||
|
||||
return allocatedMembers
|
||||
.map((member: any) => {
|
||||
const hours = getHoursForMember(member);
|
||||
|
||||
return {
|
||||
name: member.displayName || "Unnamed",
|
||||
hoursPerMonth: hours,
|
||||
monthlyPay: member.monthlyPayPlanned || 0,
|
||||
};
|
||||
})
|
||||
.filter((m: any) => m.hoursPerMonth > 0); // Only include members with hours
|
||||
});
|
||||
|
||||
// Set page meta
|
||||
definePageMeta({
|
||||
title: "Project Budget Estimate",
|
||||
});
|
||||
</script>
|
||||
Loading…
Add table
Add a link
Reference in a new issue