173 lines
5.6 KiB
Vue
173 lines
5.6 KiB
Vue
<template>
|
|
<section class="py-8 space-y-6">
|
|
<div class="flex items-center justify-between">
|
|
<h2 class="text-2xl font-semibold">Value Accounting Session</h2>
|
|
<UBadge color="primary" variant="subtle">January 2024</UBadge>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
<UCard>
|
|
<template #header>
|
|
<h3 class="text-lg font-medium">Checklist</h3>
|
|
</template>
|
|
<div class="space-y-3">
|
|
<div class="flex items-center gap-3">
|
|
<UCheckbox v-model="checklist.monthClosed" />
|
|
<span class="text-sm">Month closed & reviewed</span>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
<UCheckbox v-model="checklist.contributionsLogged" />
|
|
<span class="text-sm">Contributions logged</span>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
<UCheckbox v-model="checklist.surplusCalculated" />
|
|
<span class="text-sm">Surplus calculated</span>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
<UCheckbox v-model="checklist.needsReviewed" />
|
|
<span class="text-sm">Member needs reviewed</span>
|
|
</div>
|
|
</div>
|
|
</UCard>
|
|
|
|
<UCard>
|
|
<template #header>
|
|
<h3 class="text-lg font-medium">Available</h3>
|
|
</template>
|
|
<div class="space-y-3">
|
|
<div class="flex justify-between text-sm">
|
|
<span class="text-neutral-600">Surplus</span>
|
|
<span class="font-medium text-green-600"
|
|
>€{{ availableAmounts.surplus.toLocaleString() }}</span
|
|
>
|
|
</div>
|
|
<div class="flex justify-between text-sm">
|
|
<span class="text-neutral-600">Deferred owed</span>
|
|
<span class="font-medium text-orange-600"
|
|
>€{{ availableAmounts.deferredOwed.toLocaleString() }}</span
|
|
>
|
|
</div>
|
|
<div class="flex justify-between text-sm">
|
|
<span class="text-neutral-600">Savings gap</span>
|
|
<span class="font-medium text-blue-600"
|
|
>€{{ availableAmounts.savingsNeeded.toLocaleString() }}</span
|
|
>
|
|
</div>
|
|
</div>
|
|
</UCard>
|
|
|
|
<UCard>
|
|
<template #header>
|
|
<h3 class="text-lg font-medium">Distribution</h3>
|
|
</template>
|
|
<div class="space-y-4">
|
|
<div>
|
|
<label class="block text-xs font-medium mb-1">Deferred Repay</label>
|
|
<UInput
|
|
v-model.number="draftAllocations.deferredRepay"
|
|
type="number"
|
|
size="sm" />
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs font-medium mb-1">Savings</label>
|
|
<UInput
|
|
v-model.number="draftAllocations.savings"
|
|
type="number"
|
|
size="sm" />
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs font-medium mb-1">Training</label>
|
|
<UInput
|
|
v-model.number="draftAllocations.training"
|
|
type="number"
|
|
size="sm" />
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs font-medium mb-1">Retained</label>
|
|
<UInput
|
|
v-model.number="draftAllocations.retained"
|
|
type="number"
|
|
size="sm"
|
|
readonly />
|
|
</div>
|
|
</div>
|
|
</UCard>
|
|
</div>
|
|
|
|
<UCard>
|
|
<template #header>
|
|
<h3 class="text-lg font-medium">Decision Record</h3>
|
|
</template>
|
|
<div class="space-y-4">
|
|
<UTextarea
|
|
v-model="rationale"
|
|
placeholder="Brief rationale for this month's distribution decisions..."
|
|
rows="3" />
|
|
<div class="flex justify-end gap-3">
|
|
<UButton variant="ghost"> Save Draft </UButton>
|
|
<UButton color="primary" :disabled="!allChecklistComplete">
|
|
Complete Session
|
|
</UButton>
|
|
</div>
|
|
</div>
|
|
</UCard>
|
|
</section>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
// Use stores
|
|
const sessionStore = useSessionStore();
|
|
const membersStore = useMembersStore();
|
|
const policiesStore = usePoliciesStore();
|
|
const budgetStore = useBudgetStore();
|
|
const streamsStore = useStreamsStore();
|
|
|
|
// Use store refs
|
|
const { checklist, draftAllocations, rationale, availableAmounts } =
|
|
storeToRefs(sessionStore);
|
|
|
|
const allChecklistComplete = computed(() => {
|
|
return Object.values(checklist.value).every(Boolean);
|
|
});
|
|
|
|
// Calculate available amounts from real data
|
|
const calculatedAvailableAmounts = computed(() => {
|
|
// Calculate surplus from budget metrics
|
|
const totalRevenue = streamsStore.totalMonthlyAmount || 0;
|
|
const totalHours = membersStore.capacityTotals.targetHours || 0;
|
|
const hourlyWage = policiesStore.equalHourlyWage || 0;
|
|
const oncostPct = policiesStore.payrollOncostPct || 0;
|
|
|
|
const totalPayroll = totalHours * hourlyWage * (1 + oncostPct / 100);
|
|
const totalOverhead = budgetStore.overheadCosts.reduce(
|
|
(sum, cost) => sum + (cost.amount || 0),
|
|
0
|
|
);
|
|
const surplus = Math.max(0, totalRevenue - totalPayroll - totalOverhead);
|
|
|
|
// Calculate deferred owed
|
|
const deferredOwed = membersStore.members.reduce((sum, member) => {
|
|
return sum + (member.deferredHours || 0) * hourlyWage;
|
|
}, 0);
|
|
|
|
// Calculate savings gap
|
|
const savingsTarget =
|
|
(policiesStore.savingsTargetMonths || 0) * (totalPayroll + totalOverhead);
|
|
const savingsNeeded = Math.max(0, savingsTarget);
|
|
|
|
return {
|
|
surplus,
|
|
deferredOwed,
|
|
savingsNeeded,
|
|
};
|
|
});
|
|
|
|
// Update store available amounts when calculated values change
|
|
watch(
|
|
calculatedAvailableAmounts,
|
|
(newAmounts) => {
|
|
sessionStore.updateAvailableAmounts(newAmounts);
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
</script>
|