fix(pets): persist supplement consumptions array in usePetStore and update target pet on checkout

This commit is contained in:
parsa aghaei 2026-07-26 20:44:55 +03:30
parent 5be2b9693d
commit a0145ffd9a
4 changed files with 68 additions and 44 deletions

View File

@ -399,14 +399,33 @@
{ "index": 2, "name": "implement_wallet_deduction_in_orders_service", "description": "Deduct user walletBalance and create WalletTransaction in OrdersService.create", "status": "done" }, { "index": 2, "name": "implement_wallet_deduction_in_orders_service", "description": "Deduct user walletBalance and create WalletTransaction in OrdersService.create", "status": "done" },
{ "index": 3, "name": "wire_checkout_page_wallet_sync", "description": "Send paymentMethod in API call and trigger fetchProfile() in CheckoutPage.tsx", "status": "done" } { "index": 3, "name": "wire_checkout_page_wallet_sync", "description": "Send paymentMethod in API call and trigger fetchProfile() in CheckoutPage.tsx", "status": "done" }
] ]
},
{
"id": "EPIC-11-TASK-01",
"title": "Pet Supplement Consumption Tracking & Depletion Monitor Fix",
"description": "Ensure updatePet and setPets in usePetStore update and preserve pet consumptions array locally upon order placement so AI depletion monitor displays remaining supplement levels for active pets.",
"architectural_layer": "state_management",
"assigned_role": "06_dev_frontend",
"priority": "HIGH",
"status": "completed",
"max_files_allowed": 4,
"estimated_minutes": 20,
"dependency_task_ids": ["EPIC-10-TASK-01"],
"acceptance_criteria": [
"ثبت واقعی میزان مکمل‌های خریده‌شده در پایش موجودی پت (usePetStore)",
"نمایش لحظه‌ای مکمل‌های خریده‌شده، درصد باقیمانده و تخمین اتمام مصرف پت در پروفایل"
],
"sub_steps": [
{ "index": 1, "name": "update_pet_store_consumptions_persistence", "description": "Always update local pets array in updatePet and preserve consumptions in setPets", "status": "done" }
]
} }
], ],
"metadata": { "metadata": {
"total": 20, "total": 21,
"completed": 20, "completed": 21,
"in_progress": 0, "in_progress": 0,
"pending": 0, "pending": 0,
"generated_at": "2026-07-26T20:40:00Z", "generated_at": "2026-07-26T20:45:00Z",
"decomposition_pass": 8 "decomposition_pass": 9
} }
} }

View File

@ -7,12 +7,12 @@
"status": "COMPLETE", "status": "COMPLETE",
"checkpoint": { "checkpoint": {
"active_agent": "01_auditor", "active_agent": "01_auditor",
"current_ticket_id": "EPIC-10-TASK-01", "current_ticket_id": "EPIC-11-TASK-01",
"sub_step": { "sub_step": {
"index": 3, "index": 1,
"total": 3, "total": 1,
"name": "wire_checkout_page_wallet_sync", "name": "update_pet_store_consumptions_persistence",
"description": "Full-stack wallet balance deduction and charity donation total persistence in PostgreSQL complete and 100% verified" "description": "Pet supplement consumption tracking and AI stock monitor persistence complete and 100% verified"
} }
}, },
"review_phase": { "review_phase": {

View File

@ -125,8 +125,9 @@ export default function CheckoutPage() {
await fetchProfile(); await fetchProfile();
// 4. Update pet consumptions and charity contributions for active pet // 4. Update pet consumptions and charity contributions for active pet
if (activePet) { const targetPet = activePet || usePetStore.getState().pets[0];
const newConsumptions = [...(activePet.consumptions || [])]; if (targetPet) {
const newConsumptions = [...(targetPet.consumptions || [])];
items.forEach(item => { items.forEach(item => {
const existing = newConsumptions.find(c => c.productId === item.product.id); const existing = newConsumptions.find(c => c.productId === item.product.id);
@ -142,7 +143,7 @@ export default function CheckoutPage() {
} }
}); });
updatePet(activePet.id, { updatePet(targetPet.id, {
consumptions: newConsumptions consumptions: newConsumptions
}); });
} }

View File

@ -135,6 +135,10 @@ export const usePetStore = create<PetStore>()(
return pets.find((p) => p.id === activePetId) || null; return pets.find((p) => p.id === activePetId) || null;
}, },
updatePet: async (id, updates) => { updatePet: async (id, updates) => {
set((state) => ({
pets: state.pets.map((p) => (p.id === id ? { ...p, ...updates } : p)),
}));
const { useUserStore } = await import("./userStore"); const { useUserStore } = await import("./userStore");
const isLoggedIn = useUserStore.getState().isLoggedIn; const isLoggedIn = useUserStore.getState().isLoggedIn;
@ -154,10 +158,6 @@ export const usePetStore = create<PetStore>()(
console.error("Failed to update pet via backend API:", error); console.error("Failed to update pet via backend API:", error);
toast.error("خطا در ویرایش اطلاعات پت در سرور"); toast.error("خطا در ویرایش اطلاعات پت در سرور");
} }
} else {
set((state) => ({
pets: state.pets.map((p) => (p.id === id ? { ...p, ...updates } : p)),
}));
} }
}, },
addReminder: async (petId, reminder) => { addReminder: async (petId, reminder) => {
@ -191,34 +191,38 @@ export const usePetStore = create<PetStore>()(
} }
}, },
setPets: (backendPets) => { setPets: (backendPets) => {
const mappedPets: PetProfile[] = backendPets.map(bp => ({ const currentLocalPets = get().pets;
id: bp.id, const mappedPets: PetProfile[] = backendPets.map(bp => {
name: bp.name, const existingLocal = currentLocalPets.find(lp => lp.id === bp.id);
type: bp.type as any, return {
breed: bp.breed, id: bp.id,
age: Number(bp.age), name: bp.name,
weight: Number(bp.weight), type: bp.type as any,
activityLevel: bp.activityLevel as any, breed: bp.breed,
medicalConditions: bp.medicalConditions?.map((mc: any) => mc.condition) || [], age: Number(bp.age),
image: bp.imageUrl || undefined, weight: Number(bp.weight),
reminders: bp.reminders?.map((r: any) => ({ activityLevel: bp.activityLevel as any,
id: r.id, medicalConditions: bp.medicalConditions?.map((mc: any) => mc.condition) || [],
title: r.title, image: bp.imageUrl || undefined,
time: r.time, reminders: bp.reminders?.map((r: any) => ({
frequency: r.frequency as any, id: r.id,
productId: r.productId, title: r.title,
completedDates: r.completions?.map((c: any) => c.completedDate) || [] time: r.time,
})) || [], frequency: r.frequency as any,
logs: bp.healthLogs?.map((hl: any) => ({ productId: r.productId,
id: hl.id, completedDates: r.completions?.map((c: any) => c.completedDate) || []
date: hl.loggedDate, })) || [],
appetite: hl.appetite as any, logs: bp.healthLogs?.map((hl: any) => ({
energy: hl.energy as any, id: hl.id,
digestion: hl.digestion as any, date: hl.loggedDate,
note: hl.note || undefined appetite: hl.appetite as any,
})) || [], energy: hl.energy as any,
consumptions: [] digestion: hl.digestion as any,
})); note: hl.note || undefined
})) || [],
consumptions: existingLocal?.consumptions || []
};
});
set({ pets: mappedPets, activePetId: get().activePetId && mappedPets.some(p => p.id === get().activePetId) ? get().activePetId : (mappedPets[0]?.id || null) }); set({ pets: mappedPets, activePetId: get().activePetId && mappedPets.some(p => p.id === get().activePetId) ? get().activePetId : (mappedPets[0]?.id || null) });
}, },
}), }),