refactor: remove deprecated components and streamline member coverage calculations, enhance budget management with improved payroll handling, and update UI elements for better clarity
This commit is contained in:
parent
983aeca2dc
commit
09d8794d72
42 changed files with 2166 additions and 2974 deletions
260
composables/useStorSync.ts
Normal file
260
composables/useStorSync.ts
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
/**
|
||||
* Store Synchronization Composable
|
||||
*
|
||||
* Ensures that the legacy stores (streams, members, policies) always stay
|
||||
* synchronized with the new CoopBuilderStore. This makes the setup interface
|
||||
* the single source of truth while maintaining backward compatibility.
|
||||
*/
|
||||
|
||||
export const useStoreSync = () => {
|
||||
const coopStore = useCoopBuilderStore()
|
||||
const streamsStore = useStreamsStore()
|
||||
const membersStore = useMembersStore()
|
||||
const policiesStore = usePoliciesStore()
|
||||
|
||||
// Flags to prevent recursive syncing and duplicate watchers
|
||||
let isSyncing = false
|
||||
let watchersSetup = false
|
||||
|
||||
// Sync CoopBuilder -> Legacy Stores
|
||||
const syncToLegacyStores = () => {
|
||||
if (isSyncing) return
|
||||
isSyncing = true
|
||||
// Sync streams
|
||||
streamsStore.resetStreams()
|
||||
coopStore.streams.forEach((stream: any) => {
|
||||
streamsStore.upsertStream({
|
||||
id: stream.id,
|
||||
name: stream.label,
|
||||
category: stream.category || 'services',
|
||||
targetMonthlyAmount: stream.monthly,
|
||||
certainty: stream.certainty || 'Probable',
|
||||
payoutDelayDays: 30,
|
||||
terms: 'Net 30',
|
||||
targetPct: 0,
|
||||
revenueSharePct: 0,
|
||||
platformFeePct: 0,
|
||||
restrictions: 'General',
|
||||
seasonalityWeights: new Array(12).fill(1),
|
||||
effortHoursPerMonth: 0
|
||||
})
|
||||
})
|
||||
|
||||
// Sync members
|
||||
membersStore.resetMembers()
|
||||
coopStore.members.forEach((member: any) => {
|
||||
membersStore.upsertMember({
|
||||
id: member.id,
|
||||
displayName: member.name,
|
||||
role: member.role || '',
|
||||
hoursPerWeek: Math.round((member.hoursPerMonth || 0) / 4.33),
|
||||
minMonthlyNeeds: member.minMonthlyNeeds || 0,
|
||||
monthlyPayPlanned: member.monthlyPayPlanned || 0,
|
||||
targetMonthlyPay: member.targetMonthlyPay || 0,
|
||||
externalMonthlyIncome: member.externalMonthlyIncome || 0
|
||||
})
|
||||
})
|
||||
|
||||
// Sync policies - using individual update calls based on store structure
|
||||
policiesStore.updatePolicy('equalHourlyWage', coopStore.equalHourlyWage)
|
||||
policiesStore.updatePolicy('payrollOncostPct', coopStore.payrollOncostPct)
|
||||
policiesStore.updatePolicy('savingsTargetMonths', coopStore.savingsTargetMonths)
|
||||
policiesStore.updatePolicy('minCashCushionAmount', coopStore.minCashCushion)
|
||||
|
||||
// Reset flag after sync completes
|
||||
nextTick(() => {
|
||||
isSyncing = false
|
||||
})
|
||||
}
|
||||
|
||||
// Sync Legacy Stores -> CoopBuilder
|
||||
const syncFromLegacyStores = () => {
|
||||
if (isSyncing) return
|
||||
isSyncing = true
|
||||
// Sync streams from legacy store
|
||||
streamsStore.streams.forEach((stream: any) => {
|
||||
coopStore.upsertStream({
|
||||
id: stream.id,
|
||||
label: stream.name,
|
||||
monthly: stream.targetMonthlyAmount,
|
||||
category: stream.category,
|
||||
certainty: stream.certainty
|
||||
})
|
||||
})
|
||||
|
||||
// Sync members from legacy store
|
||||
membersStore.members.forEach((member: any) => {
|
||||
coopStore.upsertMember({
|
||||
id: member.id,
|
||||
name: member.displayName,
|
||||
role: member.role,
|
||||
hoursPerMonth: Math.round((member.hoursPerWeek || 0) * 4.33),
|
||||
minMonthlyNeeds: member.minMonthlyNeeds,
|
||||
monthlyPayPlanned: member.monthlyPayPlanned,
|
||||
targetMonthlyPay: member.targetMonthlyPay,
|
||||
externalMonthlyIncome: member.externalMonthlyIncome
|
||||
})
|
||||
})
|
||||
|
||||
// Sync policies from legacy store
|
||||
if (policiesStore.isValid) {
|
||||
coopStore.setEqualWage(policiesStore.equalHourlyWage)
|
||||
coopStore.setOncostPct(policiesStore.payrollOncostPct)
|
||||
coopStore.savingsTargetMonths = policiesStore.savingsTargetMonths
|
||||
coopStore.minCashCushion = policiesStore.minCashCushionAmount
|
||||
if (policiesStore.payPolicy?.relationship) {
|
||||
coopStore.setPolicy(policiesStore.payPolicy.relationship as any)
|
||||
}
|
||||
}
|
||||
|
||||
// Reset flag after sync completes
|
||||
nextTick(() => {
|
||||
isSyncing = false
|
||||
})
|
||||
}
|
||||
|
||||
// Watch for changes in CoopBuilder and sync to legacy stores
|
||||
const setupCoopBuilderWatchers = () => {
|
||||
// Watch streams changes
|
||||
watch(() => coopStore.streams, () => {
|
||||
if (!isSyncing) {
|
||||
syncToLegacyStores()
|
||||
}
|
||||
}, { deep: true })
|
||||
|
||||
// Watch members changes
|
||||
watch(() => coopStore.members, () => {
|
||||
if (!isSyncing) {
|
||||
syncToLegacyStores()
|
||||
}
|
||||
}, { deep: true })
|
||||
|
||||
// Watch policy changes
|
||||
watch(() => [
|
||||
coopStore.equalHourlyWage,
|
||||
coopStore.payrollOncostPct,
|
||||
coopStore.savingsTargetMonths,
|
||||
coopStore.minCashCushion,
|
||||
coopStore.currency,
|
||||
coopStore.policy.relationship
|
||||
], () => {
|
||||
if (!isSyncing) {
|
||||
syncToLegacyStores()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Watch for changes in legacy stores and sync to CoopBuilder
|
||||
const setupLegacyStoreWatchers = () => {
|
||||
// Watch streams store changes
|
||||
watch(() => streamsStore.streams, () => {
|
||||
if (!isSyncing) {
|
||||
syncFromLegacyStores()
|
||||
}
|
||||
}, { deep: true })
|
||||
|
||||
// Watch members store changes
|
||||
watch(() => membersStore.members, () => {
|
||||
if (!isSyncing) {
|
||||
syncFromLegacyStores()
|
||||
}
|
||||
}, { deep: true })
|
||||
|
||||
// Watch policies store changes
|
||||
watch(() => [
|
||||
policiesStore.equalHourlyWage,
|
||||
policiesStore.payrollOncostPct,
|
||||
policiesStore.savingsTargetMonths,
|
||||
policiesStore.minCashCushionAmount,
|
||||
policiesStore.payPolicy?.relationship
|
||||
], () => {
|
||||
if (!isSyncing) {
|
||||
syncFromLegacyStores()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Initialize synchronization
|
||||
const initSync = async () => {
|
||||
// Wait for next tick to ensure stores are mounted
|
||||
await nextTick()
|
||||
|
||||
// Force store hydration by accessing $state
|
||||
if (coopStore.$state) {
|
||||
console.log('🔄 CoopBuilder store hydrated')
|
||||
}
|
||||
|
||||
// Small delay to ensure localStorage is loaded
|
||||
await new Promise(resolve => setTimeout(resolve, 10))
|
||||
|
||||
// Determine which store has data and sync accordingly
|
||||
const coopHasData = coopStore.members.length > 0 || coopStore.streams.length > 0
|
||||
const legacyHasData = streamsStore.streams.length > 0 || membersStore.members.length > 0
|
||||
|
||||
console.log('🔄 InitSync: CoopBuilder data:', coopHasData, 'Legacy data:', legacyHasData)
|
||||
console.log('🔄 CoopBuilder members:', coopStore.members.length, 'streams:', coopStore.streams.length)
|
||||
console.log('🔄 Legacy members:', membersStore.members.length, 'streams:', streamsStore.streams.length)
|
||||
|
||||
if (coopHasData && !legacyHasData) {
|
||||
console.log('🔄 Syncing CoopBuilder → Legacy')
|
||||
syncToLegacyStores()
|
||||
} else if (legacyHasData && !coopHasData) {
|
||||
console.log('🔄 Syncing Legacy → CoopBuilder')
|
||||
syncFromLegacyStores()
|
||||
} else if (coopHasData && legacyHasData) {
|
||||
console.log('🔄 Both have data, keeping in sync')
|
||||
// Both have data, ensure consistency by syncing from CoopBuilder (primary source)
|
||||
syncToLegacyStores()
|
||||
} else {
|
||||
console.log('🔄 No data in either store')
|
||||
}
|
||||
|
||||
// Set up watchers for ongoing sync (only once)
|
||||
if (!watchersSetup) {
|
||||
setupCoopBuilderWatchers()
|
||||
setupLegacyStoreWatchers()
|
||||
watchersSetup = true
|
||||
}
|
||||
|
||||
// Return promise to allow awaiting
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
// Get unified streams data (prioritize CoopBuilder) - make reactive
|
||||
const unifiedStreams = computed(() => {
|
||||
if (coopStore.streams.length > 0) {
|
||||
return coopStore.streams.map(stream => ({
|
||||
...stream,
|
||||
name: stream.label,
|
||||
targetMonthlyAmount: stream.monthly
|
||||
}))
|
||||
}
|
||||
return streamsStore.streams
|
||||
})
|
||||
|
||||
// Get unified members data (prioritize CoopBuilder) - make reactive
|
||||
const unifiedMembers = computed(() => {
|
||||
if (coopStore.members.length > 0) {
|
||||
return coopStore.members.map(member => ({
|
||||
...member,
|
||||
displayName: member.name,
|
||||
hoursPerWeek: Math.round((member.hoursPerMonth || 0) / 4.33)
|
||||
}))
|
||||
}
|
||||
return membersStore.members
|
||||
})
|
||||
|
||||
// Getter functions for backward compatibility
|
||||
const getStreams = () => unifiedStreams.value
|
||||
const getMembers = () => unifiedMembers.value
|
||||
|
||||
return {
|
||||
syncToLegacyStores,
|
||||
syncFromLegacyStores,
|
||||
initSync,
|
||||
getStreams,
|
||||
getMembers,
|
||||
unifiedStreams,
|
||||
unifiedMembers
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue