219 lines
6.9 KiB
Vue
219 lines
6.9 KiB
Vue
<template>
|
|
<section class="py-8 space-y-6">
|
|
<div class="flex items-center justify-between">
|
|
<h2 class="text-2xl font-semibold">Policies & Privacy</h2>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
<UCard>
|
|
<template #header>
|
|
<h3 class="text-lg font-medium">Wage & Costs</h3>
|
|
</template>
|
|
<div class="space-y-4">
|
|
<div>
|
|
<label class="block text-sm font-medium mb-2"
|
|
>Equal Hourly Wage</label
|
|
>
|
|
<UInput
|
|
v-model="policies.hourlyWage"
|
|
type="number"
|
|
:ui="{ wrapper: 'relative' }">
|
|
<template #leading>
|
|
<span class="text-neutral-500">€</span>
|
|
</template>
|
|
</UInput>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium mb-2"
|
|
>Payroll On-costs (%)</label
|
|
>
|
|
<UInput
|
|
v-model="policies.payrollOncost"
|
|
type="number"
|
|
:ui="{ wrapper: 'relative' }">
|
|
<template #trailing>
|
|
<span class="text-neutral-500">%</span>
|
|
</template>
|
|
</UInput>
|
|
</div>
|
|
</div>
|
|
</UCard>
|
|
|
|
<UCard>
|
|
<template #header>
|
|
<h3 class="text-lg font-medium">Cash Management</h3>
|
|
</template>
|
|
<div class="space-y-4">
|
|
<div>
|
|
<label class="block text-sm font-medium mb-2"
|
|
>Savings Target (months)</label
|
|
>
|
|
<UInput
|
|
v-model="policies.savingsTargetMonths"
|
|
type="number"
|
|
step="0.1" />
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium mb-2"
|
|
>Minimum Cash Cushion</label
|
|
>
|
|
<UInput
|
|
v-model="policies.minCashCushion"
|
|
type="number"
|
|
:ui="{ wrapper: 'relative' }">
|
|
<template #leading>
|
|
<span class="text-neutral-500">€</span>
|
|
</template>
|
|
</UInput>
|
|
</div>
|
|
</div>
|
|
</UCard>
|
|
|
|
<UCard>
|
|
<template #header>
|
|
<h3 class="text-lg font-medium">Deferred Pay Limits</h3>
|
|
</template>
|
|
<div class="space-y-4">
|
|
<div>
|
|
<label class="block text-sm font-medium mb-2"
|
|
>Cap (hours per quarter)</label
|
|
>
|
|
<UInput v-model="policies.deferredCapHours" type="number" />
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium mb-2"
|
|
>Sunset (months)</label
|
|
>
|
|
<UInput v-model="policies.deferredSunsetMonths" type="number" />
|
|
</div>
|
|
</div>
|
|
</UCard>
|
|
|
|
<UCard>
|
|
<template #header>
|
|
<h3 class="text-lg font-medium">Distribution Order</h3>
|
|
</template>
|
|
<div class="space-y-4">
|
|
<p class="text-sm text-neutral-600">
|
|
Order of surplus distribution priorities.
|
|
</p>
|
|
<div class="space-y-2">
|
|
<div
|
|
v-for="(item, index) in distributionOrder"
|
|
:key="item"
|
|
class="flex items-center justify-between p-2 bg-neutral-50 rounded">
|
|
<span class="text-sm font-medium"
|
|
>{{ index + 1 }}. {{ item }}</span
|
|
>
|
|
<div class="flex gap-1">
|
|
<UButton
|
|
size="xs"
|
|
variant="ghost"
|
|
icon="i-heroicons-chevron-up" />
|
|
<UButton
|
|
size="xs"
|
|
variant="ghost"
|
|
icon="i-heroicons-chevron-down" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</UCard>
|
|
</div>
|
|
|
|
<!-- Member Management Section -->
|
|
<UCard id="members">
|
|
<template #header>
|
|
<div class="flex items-center justify-between">
|
|
<h3 class="text-lg font-medium">Team Members</h3>
|
|
<div class="flex items-center gap-2">
|
|
<UBadge v-if="isSetupComplete" color="green" variant="subtle" size="xs">
|
|
Synchronized with Setup
|
|
</UBadge>
|
|
<UButton variant="ghost" size="xs" @click="goToSetup">
|
|
Edit in Setup
|
|
</UButton>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<div class="space-y-4">
|
|
<div v-if="members.length === 0" class="text-center py-8 text-neutral-500">
|
|
<p class="mb-4">No team members found.</p>
|
|
<UButton @click="goToSetup" variant="outline">
|
|
Add Members in Setup
|
|
</UButton>
|
|
</div>
|
|
|
|
<div v-else class="space-y-3">
|
|
<div
|
|
v-for="member in members"
|
|
:key="member.id"
|
|
class="p-4 border border-neutral-200 rounded-lg bg-neutral-50">
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<h4 class="font-medium">{{ member.displayName || member.name }}</h4>
|
|
<div class="text-sm text-neutral-600 space-y-1">
|
|
<div v-if="member.role">Role: {{ member.role }}</div>
|
|
<div>Monthly Target: {{ $format.currency(member.monthlyPayPlanned || 0) }}</div>
|
|
<div v-if="member.minMonthlyNeeds">Min Needs: {{ $format.currency(member.minMonthlyNeeds) }}</div>
|
|
</div>
|
|
</div>
|
|
<div class="text-right text-sm text-neutral-600">
|
|
<div>{{ Math.round((member.hoursPerMonth || member.hoursPerWeek * 4.33)) }} hrs/month</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</UCard>
|
|
|
|
<div class="flex justify-end">
|
|
<UButton color="primary"> Save Policies </UButton>
|
|
</div>
|
|
</section>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
// Store sync and setup state
|
|
const { initSync, getMembers, unifiedMembers } = useStoreSync();
|
|
const { isSetupComplete, goToSetup } = useSetupState();
|
|
const coopStore = useCoopBuilderStore();
|
|
const { $format } = useNuxtApp();
|
|
|
|
// Initialize synchronization on mount
|
|
onMounted(async () => {
|
|
await initSync();
|
|
});
|
|
|
|
// Get reactive synchronized member data
|
|
const members = unifiedMembers;
|
|
|
|
// Get synchronized policy data from setup
|
|
const policies = computed({
|
|
get: () => ({
|
|
hourlyWage: coopStore.equalHourlyWage,
|
|
payrollOncost: coopStore.payrollOncostPct,
|
|
savingsTargetMonths: coopStore.savingsTargetMonths,
|
|
minCashCushion: coopStore.minCashCushion,
|
|
deferredCapHours: 240, // These fields might not be in coop store yet
|
|
deferredSunsetMonths: 12,
|
|
}),
|
|
set: (newPolicies) => {
|
|
// Update the CoopBuilder store when policies change
|
|
coopStore.setEqualWage(newPolicies.hourlyWage);
|
|
coopStore.setOncostPct(newPolicies.payrollOncost);
|
|
coopStore.savingsTargetMonths = newPolicies.savingsTargetMonths;
|
|
coopStore.minCashCushion = newPolicies.minCashCushion;
|
|
}
|
|
});
|
|
|
|
const distributionOrder = ref([
|
|
"Deferred",
|
|
"Savings",
|
|
"Hardship",
|
|
"Training",
|
|
"Patronage",
|
|
"Retained",
|
|
]);
|
|
</script>
|