refactor: streamline WizardPoliciesStep component layout and improve budget management forms with enhanced initial value handling
This commit is contained in:
parent
4cea1f71fe
commit
fc2d9ed56b
3 changed files with 272 additions and 267 deletions
|
|
@ -3,19 +3,13 @@
|
|||
<!-- Section Header with Export Controls -->
|
||||
<div class="flex items-center justify-between mb-8">
|
||||
<div>
|
||||
<h3 class="text-2xl font-black text-black mb-2">
|
||||
Set your wage & pay policy
|
||||
</h3>
|
||||
<h3 class="text-2xl font-black text-black mb-2">Set your wage & pay policy</h3>
|
||||
<p class="text-neutral-600">
|
||||
Choose how to allocate payroll among members and set the base hourly rate.
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<UButton
|
||||
variant="outline"
|
||||
color="gray"
|
||||
size="sm"
|
||||
@click="exportPolicies">
|
||||
<UButton variant="outline" color="neutral" size="sm" @click="exportPolicies">
|
||||
<UIcon name="i-heroicons-arrow-down-tray" class="mr-1" />
|
||||
Export
|
||||
</UButton>
|
||||
|
|
@ -26,7 +20,11 @@
|
|||
<div class="p-6 border-3 border-black rounded-xl bg-white shadow-md">
|
||||
<h4 class="font-bold mb-4">Pay Allocation Policy</h4>
|
||||
<div class="space-y-3">
|
||||
<label v-for="option in policyOptions" :key="option.value" class="flex items-start gap-3 cursor-pointer hover:bg-gray-50 p-2 rounded-lg transition-colors">
|
||||
<label
|
||||
v-for="option in policyOptions"
|
||||
:key="option.value"
|
||||
class="flex items-start gap-3 cursor-pointer hover:bg-gray-50 p-2 rounded-lg transition-colors"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
:value="option.value"
|
||||
|
|
@ -37,13 +35,17 @@
|
|||
<span class="text-sm flex-1">{{ option.label }}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Role bands editor if role-banded is selected -->
|
||||
<div v-if="selectedPolicy === 'role-banded'" class="mt-4 p-4 bg-gray-50 rounded-lg">
|
||||
<h5 class="text-sm font-medium mb-3">Role Bands (monthly € or weight)</h5>
|
||||
<div class="space-y-2">
|
||||
<div v-for="member in uniqueRoles" :key="member.role" class="flex items-center gap-2">
|
||||
<span class="text-sm w-32">{{ member.role || 'No role' }}</span>
|
||||
<div
|
||||
v-for="member in uniqueRoles"
|
||||
:key="member.role"
|
||||
class="flex items-center gap-2"
|
||||
>
|
||||
<span class="text-sm w-32">{{ member.role || "No role" }}</span>
|
||||
<UInput
|
||||
v-model="roleBands[member.role || '']"
|
||||
type="text"
|
||||
|
|
@ -55,7 +57,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<UAlert
|
||||
class="mt-4"
|
||||
color="primary"
|
||||
|
|
@ -67,7 +69,7 @@
|
|||
</template>
|
||||
</UAlert>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Hourly Wage Input -->
|
||||
<div class="p-6 border-3 border-black rounded-xl bg-white shadow-md">
|
||||
<h4 class="font-bold mb-4">Base Hourly Wage</h4>
|
||||
|
|
@ -78,7 +80,8 @@
|
|||
placeholder="0.00"
|
||||
size="xl"
|
||||
class="text-4xl font-black w-full h-20"
|
||||
@update:model-value="validateAndSaveWage">
|
||||
@update:model-value="validateAndSaveWage"
|
||||
>
|
||||
<template #leading>
|
||||
<span class="text-neutral-500 text-3xl">€</span>
|
||||
</template>
|
||||
|
|
@ -98,9 +101,9 @@ const coop = useCoopBuilder();
|
|||
const store = useCoopBuilderStore();
|
||||
|
||||
// Initialize from store
|
||||
const selectedPolicy = ref(coop.policy.value?.relationship || 'equal-pay')
|
||||
const roleBands = ref(coop.policy.value?.roleBands || {})
|
||||
const wageText = ref(String(store.equalHourlyWage || ''))
|
||||
const selectedPolicy = ref(coop.policy.value?.relationship || "equal-pay");
|
||||
const roleBands = ref(coop.policy.value?.roleBands || {});
|
||||
const wageText = ref(String(store.equalHourlyWage || ""));
|
||||
|
||||
function parseNumberInput(val: unknown): number {
|
||||
if (typeof val === "number") return val;
|
||||
|
|
@ -114,43 +117,54 @@ function parseNumberInput(val: unknown): number {
|
|||
|
||||
// Pay policy options
|
||||
const policyOptions = [
|
||||
{ value: 'equal-pay', label: 'Equal pay - Everyone gets the same monthly amount' },
|
||||
{ value: 'needs-weighted', label: 'Needs-weighted - Allocate based on minimum needs' },
|
||||
{ value: 'hours-weighted', label: 'Hours-weighted - Allocate based on hours worked' },
|
||||
{ value: 'role-banded', label: 'Role-banded - Different amounts per role' }
|
||||
]
|
||||
{
|
||||
value: "equal-pay",
|
||||
label: "Equal pay - Everyone gets the same monthly amount",
|
||||
},
|
||||
{
|
||||
value: "needs-weighted",
|
||||
label: "Needs-weighted - Allocate based on minimum needs",
|
||||
},
|
||||
{
|
||||
value: "hours-weighted",
|
||||
label: "Hours-weighted - Allocate based on hours worked",
|
||||
},
|
||||
{ value: "role-banded", label: "Role-banded - Different amounts per role" },
|
||||
];
|
||||
|
||||
// Already initialized above with store values
|
||||
|
||||
const uniqueRoles = computed(() => {
|
||||
const roles = new Set(coop.members.value.map(m => m.role || ''))
|
||||
return Array.from(roles).map(role => ({ role }))
|
||||
})
|
||||
const roles = new Set(coop.members.value.map((m) => m.role || ""));
|
||||
return Array.from(roles).map((role) => ({ role }));
|
||||
});
|
||||
|
||||
function updatePolicy(value: string) {
|
||||
selectedPolicy.value = value
|
||||
coop.setPolicy(value as "equal-pay" | "needs-weighted" | "hours-weighted" | "role-banded")
|
||||
|
||||
selectedPolicy.value = value;
|
||||
coop.setPolicy(
|
||||
value as "equal-pay" | "needs-weighted" | "hours-weighted" | "role-banded"
|
||||
);
|
||||
|
||||
// Trigger payroll reallocation after policy change
|
||||
const allocatedMembers = coop.allocatePayroll()
|
||||
allocatedMembers.forEach(m => {
|
||||
coop.upsertMember(m)
|
||||
})
|
||||
|
||||
const allocatedMembers = coop.allocatePayroll();
|
||||
allocatedMembers.forEach((m) => {
|
||||
coop.upsertMember(m);
|
||||
});
|
||||
|
||||
emit("save-status", "saved");
|
||||
}
|
||||
|
||||
function updateRoleBands() {
|
||||
coop.setRoleBands(roleBands.value)
|
||||
|
||||
coop.setRoleBands(roleBands.value);
|
||||
|
||||
// Trigger payroll reallocation after role bands change
|
||||
if (selectedPolicy.value === 'role-banded') {
|
||||
const allocatedMembers = coop.allocatePayroll()
|
||||
allocatedMembers.forEach(m => {
|
||||
coop.upsertMember(m)
|
||||
})
|
||||
if (selectedPolicy.value === "role-banded") {
|
||||
const allocatedMembers = coop.allocatePayroll();
|
||||
allocatedMembers.forEach((m) => {
|
||||
coop.upsertMember(m);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
emit("save-status", "saved");
|
||||
}
|
||||
|
||||
|
|
@ -163,14 +177,14 @@ function validateAndSaveWage(value: string) {
|
|||
wageText.value = cleanValue;
|
||||
|
||||
if (!isNaN(numValue) && numValue >= 0) {
|
||||
coop.setEqualWage(numValue)
|
||||
|
||||
coop.setEqualWage(numValue);
|
||||
|
||||
// Trigger payroll reallocation after wage change
|
||||
const allocatedMembers = coop.allocatePayroll()
|
||||
allocatedMembers.forEach(m => {
|
||||
coop.upsertMember(m)
|
||||
})
|
||||
|
||||
const allocatedMembers = coop.allocatePayroll();
|
||||
allocatedMembers.forEach((m) => {
|
||||
coop.upsertMember(m);
|
||||
});
|
||||
|
||||
emit("save-status", "saved");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue