refactor: enhance routing and state management in CoopBuilder, add migration checks on startup, and update Tailwind configuration for improved component styling

This commit is contained in:
Jennie Robinson Faber 2025-08-23 18:24:31 +01:00
parent 848386e3dd
commit 4cea1f71fe
55 changed files with 4053 additions and 1486 deletions

View file

@ -1,4 +1,6 @@
import { defineStore } from "pinia";
import { ref, computed } from 'vue';
import { coverage, teamCoverageStats } from "~/types/members";
export const useMembersStore = defineStore(
"members",
@ -34,10 +36,16 @@ export const useMembersStore = defineStore(
// Normalize a member object to ensure required structure and sane defaults
function normalizeMember(raw) {
// Calculate hoursPerWeek from targetHours (monthly) if not explicitly set
const targetHours = Number(raw.capacity?.targetHours) || 0;
const hoursPerWeek = raw.hoursPerWeek ?? (targetHours > 0 ? targetHours / 4.33 : 0);
const normalized = {
id: raw.id || Date.now().toString(),
displayName: typeof raw.displayName === "string" ? raw.displayName : "",
roleFocus: typeof raw.roleFocus === "string" ? raw.roleFocus : "",
role: raw.role || raw.roleFocus || "",
hoursPerWeek: hoursPerWeek,
payRelationship: raw.payRelationship || "FullyPaid",
capacity: {
minHours: Number(raw.capacity?.minHours) || 0,
@ -49,6 +57,11 @@ export const useMembersStore = defineStore(
privacyNeeds: raw.privacyNeeds || "aggregate_ok",
deferredHours: Number(raw.deferredHours ?? 0),
quarterlyDeferredCap: Number(raw.quarterlyDeferredCap ?? 240),
// NEW fields for needs coverage
minMonthlyNeeds: Number(raw.minMonthlyNeeds) || 0,
targetMonthlyPay: Number(raw.targetMonthlyPay) || 0,
externalMonthlyIncome: Number(raw.externalMonthlyIncome) || 0,
monthlyPayPlanned: Number(raw.monthlyPayPlanned) || 0,
...raw,
};
return normalized;
@ -187,6 +200,56 @@ export const useMembersStore = defineStore(
members.value = [];
}
// Coverage calculations for individual members
function getMemberCoverage(memberId) {
const member = members.value.find((m) => m.id === memberId);
if (!member) return { minPct: undefined, targetPct: undefined };
return coverage(
member.minMonthlyNeeds || 0,
member.targetMonthlyPay || 0,
member.monthlyPayPlanned || 0,
member.externalMonthlyIncome || 0
);
}
// Team-wide coverage statistics
const teamStats = computed(() => teamCoverageStats(members.value));
// Pay policy configuration
const payPolicy = ref({
relationship: 'equal-pay' as const,
notes: '',
equalBase: 0,
needsWeight: 0.5,
roleBands: {},
hoursRate: 0,
customFormula: ''
});
// Setters for new fields
function setMonthlyNeeds(memberId, minNeeds, targetPay) {
const member = members.value.find((m) => m.id === memberId);
if (member) {
member.minMonthlyNeeds = Number(minNeeds) || 0;
member.targetMonthlyPay = Number(targetPay) || 0;
}
}
function setExternalIncome(memberId, income) {
const member = members.value.find((m) => m.id === memberId);
if (member) {
member.externalMonthlyIncome = Number(income) || 0;
}
}
function setPlannedPay(memberId, planned) {
const member = members.value.find((m) => m.id === memberId);
if (member) {
member.monthlyPayPlanned = Number(planned) || 0;
}
}
return {
members,
capacityTotals,
@ -194,6 +257,8 @@ export const useMembersStore = defineStore(
validationDetails,
isValid,
schemaVersion,
payPolicy,
teamStats,
// Wizard actions
upsertMember,
setCapacity,
@ -202,6 +267,11 @@ export const useMembersStore = defineStore(
setExternalCoveragePct,
setPrivacy,
resetMembers,
// New coverage actions
setMonthlyNeeds,
setExternalIncome,
setPlannedPay,
getMemberCoverage,
// Legacy actions
addMember,
updateMember,
@ -211,7 +281,7 @@ export const useMembersStore = defineStore(
{
persist: {
key: "urgent-tools-members",
paths: ["members", "privacyFlags"],
paths: ["members", "privacyFlags", "payPolicy"],
},
}
);