canina/src/store/usePetStore.ts
parsa aghayi c95c9e5ce0 feat: initialize project structure
- Configure project metadata and dependencies
- Setup Vite with React and TypeScript
- Add environment configuration and .gitignore
- Implement initial application entry point and assets
2026-05-26 11:37:22 +03:30

183 lines
6.1 KiB
TypeScript

import { create } from "zustand";
import { persist } from "zustand/middleware";
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">) => void;
toggleReminder: (petId: string, reminderId: string, date: string) => void;
addHealthLog: (petId: string, log: Omit<HealthLog, "id" | "date">) => void;
}
export const usePetStore = create<PetStore>()(
persist(
(set, get) => ({
pets: [
{
id: "lucy",
name: "لوسی",
type: "سگ",
breed: "گلدن رتریور",
age: 3,
weight: 28,
activityLevel: "زیاد",
medicalConditions: ["حساسیت فصلی"],
reminders: [],
logs: [],
consumptions: []
},
{
id: "rocky",
name: "رکی",
type: "سگ",
breed: "ژرمن شپرد",
age: 5,
weight: 35,
activityLevel: "متوسط",
medicalConditions: ["دیسپلازی لگن"],
reminders: [],
logs: [],
consumptions: []
}
],
activePetId: "lucy",
addPet: (pet) => {
// TODO: [BACKEND_API] POST /api/pets | Payload: Omit<PetProfile, "id"> | Expected: PetProfile | Errors: [400, 401]
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: (id) => {
// TODO: [BACKEND_API] DELETE /api/pets/{id} | Expected: { success: boolean } | Errors: [401, 404]
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: (id, updates) => {
// TODO: [BACKEND_API] PATCH /api/pets/{id} | Payload: Partial<PetProfile> | Expected: PetProfile | Errors: [400, 401, 404]
set((state) => ({
pets: state.pets.map((p) => (p.id === id ? { ...p, ...updates } : p)),
}));
},
addReminder: (petId, reminder) => {
// TODO: [BACKEND_API] POST /api/pets/{petId}/reminders | Payload: Omit<Reminder, "id"> | Expected: Reminder | Errors: [400, 401, 404]
const id = Math.random().toString(36).substring(7);
set((state) => ({
pets: state.pets.map((p) =>
p.id === petId ? { ...p, reminders: [...(p.reminders || []), { ...reminder, id, completedDates: [] }] } : p
)
}));
},
toggleReminder: (petId, reminderId, date) => {
// TODO: [BACKEND_API] POST /api/pets/{petId}/reminders/{reminderId}/toggle | Payload: { date: string } | Expected: { success: boolean, newRemaining?: number } | Errors: [401, 404]
set((state) => ({
pets: state.pets.map((p) => {
if (p.id !== petId) return p;
const reminder = p.reminders?.find(r => r.id === reminderId);
if (!reminder) return p;
const isCompleting = !reminder.completedDates.includes(date);
// 1. Update Reminders
const newReminders = p.reminders.map(r => {
if (r.id !== reminderId) return r;
return {
...r,
completedDates: isCompleting
? [...r.completedDates, date]
: r.completedDates.filter(d => d !== date)
};
});
// 2. Update Consumptions (decrement if completing and product is linked)
let newConsumptions = p.consumptions || [];
if (isCompleting && reminder.productId) {
newConsumptions = newConsumptions.map(c =>
c.productId === reminder.productId
? { ...c, remaining: Math.max(0, c.remaining - 1) }
: c
);
}
return { ...p, reminders: newReminders, consumptions: newConsumptions };
})
}));
},
addHealthLog: (petId, log) => {
// TODO: [BACKEND_API] POST /api/pets/{petId}/health-logs | Payload: Omit<HealthLog, "id" | "date"> | Expected: HealthLog | Errors: [400, 401, 404]
const id = Math.random().toString(36).substring(7);
const date = new Date().toISOString();
set((state) => ({
pets: state.pets.map((p) =>
p.id === petId ? { ...p, logs: [{ ...log, id, date }, ...(p.logs || [])] } : p
)
}));
},
}),
{ name: "canina-pets" }
)
);