canina/frontend/application/lib/store/usePetStore.ts
parsa aghaei 7615aa0e51 fix: resolve seed encoding, search param, and wiki data source issues
- Fix seed-products.ts TS error (implicit any) and BOM handling
- Re-run full seed to restore correct Persian encoding in DB
- Fix productService query→search param mismatch
- Fix IngredientWiki hardcoded port 4000→use settingsStore
- Restore seed-products-data.json from git after accidental corruption
2026-07-11 15:40:49 +03:30

218 lines
7.5 KiB
TypeScript

"use client";
import { create } from "zustand";
import { persist } from "zustand/middleware";
import api from "../services/api";
import { toast } from "sonner";
export interface Reminder {
id: string;
title: string;
time: string;
frequency: "روزانه" | "هفتگی";
productId?: string;
completedDates: string[]; // List of ISO dates when this was completed
}
export interface HealthLog {
id: string;
date: string;
appetite: "عالی" | "متوسط" | "کم";
energy: "زیاد" | "نرمال" | "بی‌حال";
digestion: "نرمال" | "حساس" | "مشکل‌دار";
note?: string;
}
export interface PetConsumption {
productId: string;
packageSize: number;
remaining: number;
}
export interface PetProfile {
id: string;
name: string;
type: "سگ" | "گربه";
breed: string;
age: number;
weight: number;
activityLevel: "کم" | "متوسط" | "زیاد";
medicalConditions: string[];
image?: string;
reminders: Reminder[];
logs: HealthLog[];
consumptions: PetConsumption[];
}
interface PetStore {
pets: PetProfile[];
activePetId: string | null;
addPet: (pet: Omit<PetProfile, "id">) => void;
removePet: (id: string) => void;
setActivePet: (id: string) => void;
getActivePet: () => PetProfile | null;
updatePet: (id: string, updates: Partial<PetProfile>) => void;
addReminder: (petId: string, reminder: Omit<Reminder, "id" | "completedDates">) => Promise<void>;
toggleReminder: (petId: string, reminderId: string, date: string) => Promise<void>;
addHealthLog: (petId: string, log: Omit<HealthLog, "id" | "date">) => Promise<void>;
setPets: (pets: any[]) => void;
}
export const usePetStore = create<PetStore>()(
persist(
(set, get) => ({
pets: [],
activePetId: null,
addPet: async (pet) => {
const { useUserStore } = await import("./userStore");
const isLoggedIn = useUserStore.getState().isLoggedIn;
if (isLoggedIn) {
try {
await api.post("/pets", {
name: pet.name,
type: pet.type,
breed: pet.breed || "",
weight: Number(pet.weight) || 0,
age: Number(pet.age) || 0,
activityLevel: pet.activityLevel || "متوسط",
medicalConditions: pet.medicalConditions || []
});
await useUserStore.getState().fetchProfile();
} catch (error) {
console.error("Failed to add pet via backend API:", error);
toast.error("خطا در ثبت پت در سرور");
}
} else {
const id = Math.random().toString(36).substring(7);
const newPet = {
...pet,
id,
reminders: pet.reminders || [],
logs: pet.logs || [],
consumptions: pet.consumptions || []
};
set((state) => ({
pets: [...state.pets, newPet],
activePetId: state.activePetId || id,
}));
}
},
removePet: async (id) => {
const { useUserStore } = await import("./userStore");
const isLoggedIn = useUserStore.getState().isLoggedIn;
if (isLoggedIn) {
try {
await api.delete(`/pets/${id}`);
await useUserStore.getState().fetchProfile();
} catch (error) {
console.error("Failed to remove pet via backend API:", error);
toast.error("خطا در حذف پت از سرور");
}
} else {
set((state) => {
const newPets = state.pets.filter((p) => p.id !== id);
return {
pets: newPets,
activePetId: state.activePetId === id ? (newPets[0]?.id || null) : state.activePetId,
};
});
}
},
setActivePet: (id) => set({ activePetId: id }),
getActivePet: () => {
const { pets, activePetId } = get();
return pets.find((p) => p.id === activePetId) || null;
},
updatePet: async (id, updates) => {
const { useUserStore } = await import("./userStore");
const isLoggedIn = useUserStore.getState().isLoggedIn;
if (isLoggedIn) {
try {
await api.patch(`/pets/${id}`, {
name: updates.name,
type: updates.type,
breed: updates.breed,
weight: updates.weight ? Number(updates.weight) : undefined,
age: updates.age ? Number(updates.age) : undefined,
activityLevel: updates.activityLevel,
medicalConditions: updates.medicalConditions
});
await useUserStore.getState().fetchProfile();
} catch (error) {
console.error("Failed to update pet via backend API:", error);
toast.error("خطا در ویرایش اطلاعات پت در سرور");
}
} else {
set((state) => ({
pets: state.pets.map((p) => (p.id === id ? { ...p, ...updates } : p)),
}));
}
},
addReminder: async (petId, reminder) => {
try {
await api.post(`/pets/${petId}/reminders`, reminder);
const { useUserStore } = await import("./userStore");
await useUserStore.getState().fetchProfile();
} catch (error) {
console.error("Failed to add reminder:", error);
toast.error("خطا در ثبت یادآور");
}
},
toggleReminder: async (petId, reminderId, date) => {
try {
await api.post(`/pets/${petId}/reminders/${reminderId}/toggle`, { date });
const { useUserStore } = await import("./userStore");
await useUserStore.getState().fetchProfile();
} catch (error) {
console.error("Failed to toggle reminder:", error);
toast.error("خطا در تغییر وضعیت یادآور");
}
},
addHealthLog: async (petId, log) => {
try {
await api.post(`/pets/${petId}/health-logs`, log);
const { useUserStore } = await import("./userStore");
await useUserStore.getState().fetchProfile();
} catch (error) {
console.error("Failed to add health log:", error);
toast.error("خطا در ثبت گزارش سلامتی");
}
},
setPets: (backendPets) => {
const mappedPets: PetProfile[] = backendPets.map(bp => ({
id: bp.id,
name: bp.name,
type: bp.type as any,
breed: bp.breed,
age: Number(bp.age),
weight: Number(bp.weight),
activityLevel: bp.activityLevel as any,
medicalConditions: bp.medicalConditions?.map((mc: any) => mc.condition) || [],
image: bp.imageUrl || undefined,
reminders: bp.reminders?.map((r: any) => ({
id: r.id,
title: r.title,
time: r.time,
frequency: r.frequency as any,
productId: r.productId,
completedDates: r.completions?.map((c: any) => c.completedDate) || []
})) || [],
logs: bp.healthLogs?.map((hl: any) => ({
id: hl.id,
date: hl.loggedDate,
appetite: hl.appetite as any,
energy: hl.energy as any,
digestion: hl.digestion as any,
note: hl.note || undefined
})) || [],
consumptions: []
}));
set({ pets: mappedPets, activePetId: get().activePetId && mappedPets.some(p => p.id === get().activePetId) ? get().activePetId : (mappedPets[0]?.id || null) });
},
}),
{ name: "canina-pets" }
)
);