1267 lines
65 KiB
TypeScript
1267 lines
65 KiB
TypeScript
"use client";
|
||
import React, { useState, useMemo, useEffect } from "react";
|
||
import { Product } from "../lib/data/products";
|
||
import { productService } from "../lib/services/productService";
|
||
import { motion, AnimatePresence } from "motion/react";
|
||
import { PetProfileSkeleton } from "./Skeleton";
|
||
import {
|
||
Dog,
|
||
Cat,
|
||
ChevronRight,
|
||
ChevronLeft,
|
||
Heart,
|
||
Activity,
|
||
Weight,
|
||
Calendar,
|
||
ShieldCheck,
|
||
Stethoscope,
|
||
Plus,
|
||
Clock,
|
||
ClipboardList,
|
||
Package,
|
||
Star,
|
||
Edit3,
|
||
Trash2,
|
||
AlertTriangle,
|
||
TrendingUp,
|
||
Check
|
||
} from "lucide-react";
|
||
import { usePetStore, PetProfile as GlobalPetProfile, Reminder, HealthLog } from "../lib/store/usePetStore";
|
||
import { useCartStore } from "../lib/store/cartStore";
|
||
import SafeImage from "./SafeImage";
|
||
import SmartAdvisor from "./SmartAdvisor";
|
||
import { toast } from "sonner";
|
||
import { toPersian, cn } from "../lib/utils";
|
||
import api from "../lib/services/api";
|
||
|
||
import { useRouter } from 'next/navigation';
|
||
|
||
export default function PetProfile({ initialView }: {
|
||
initialView?: "index" | "detail" | "add" | "edit",
|
||
advisorNeed?: string | null
|
||
}) {
|
||
const router = useRouter();
|
||
const { pets, addPet, removePet, setActivePet, getActivePet, updatePet, addReminder, toggleReminder, addHealthLog } = usePetStore();
|
||
const { orders } = useCartStore();
|
||
const activePet = getActivePet();
|
||
const [isLoading, setIsLoading] = useState(true);
|
||
const [products, setProducts] = useState<Product[]>([]);
|
||
|
||
useEffect(() => {
|
||
productService.getProducts({ limit: 999 })
|
||
.then(res => setProducts(res.data))
|
||
.catch(err => console.error("Error fetching products in PetProfile:", err));
|
||
}, []);
|
||
|
||
useEffect(() => {
|
||
const timer = setTimeout(() => setIsLoading(false), 800);
|
||
return () => clearTimeout(timer);
|
||
}, []);
|
||
|
||
const [view, setView] = useState<"index" | "detail" | "add" | "edit">(initialView || (pets.length > 0 ? "index" : "add"));
|
||
|
||
useEffect(() => {
|
||
if (initialView) {
|
||
Promise.resolve().then(() => setView(initialView));
|
||
}
|
||
}, [initialView]);
|
||
|
||
const [activePetTab, setActivePetTab] = useState<"health" | "orders">("health");
|
||
const [petToDelete, setPetToDelete] = useState<GlobalPetProfile | null>(null);
|
||
|
||
// Modal states
|
||
const [isAddingReminder, setIsAddingReminder] = useState(false);
|
||
const [isAddingHealthLog, setIsAddingHealthLog] = useState(false);
|
||
|
||
// New Reminder form state
|
||
const [reminderForm, setReminderForm] = useState<Omit<Reminder, "id" | "completedDates">>({
|
||
title: "",
|
||
time: "08:00",
|
||
frequency: "روزانه",
|
||
productId: ""
|
||
});
|
||
|
||
// Health log form state
|
||
const [logForm, setLogForm] = useState<Omit<HealthLog, "id" | "date">>({
|
||
appetite: "عالی",
|
||
energy: "نرمال",
|
||
digestion: "نرمال",
|
||
note: ""
|
||
});
|
||
|
||
const petOrders = useMemo(() => {
|
||
if (!activePet) return [];
|
||
return orders.filter(o => o.petId === activePet.id);
|
||
}, [activePet, orders]);
|
||
|
||
const [step, setStep] = useState(1);
|
||
const [formData, setFormData] = useState<Omit<GlobalPetProfile, "id">>({
|
||
name: "",
|
||
type: "سگ",
|
||
breed: "",
|
||
age: 0,
|
||
weight: 0,
|
||
activityLevel: "متوسط",
|
||
medicalConditions: [],
|
||
reminders: [],
|
||
logs: [],
|
||
consumptions: []
|
||
});
|
||
|
||
const recommendedProducts = useMemo(() => {
|
||
if (!activePet || products.length === 0) return [];
|
||
|
||
const picks: { product: Product, reason?: string }[] = [];
|
||
|
||
// Priority 1: Match activePet.medicalConditions directly against product.symptoms
|
||
if (activePet.medicalConditions && activePet.medicalConditions.length > 0) {
|
||
products.forEach(p => {
|
||
if (p.suitableFor !== activePet.type && p.suitableFor !== "هر دو") return;
|
||
const matchedSymptoms = (p.symptoms || []).filter(s =>
|
||
activePet.medicalConditions.some(mc => mc.includes(s) || s.includes(mc))
|
||
);
|
||
|
||
if (matchedSymptoms.length > 0 && !picks.some(x => x.product.id === p.id)) {
|
||
picks.push({
|
||
product: p,
|
||
reason: `پیشنهاد هوشمند برای تسکین ${matchedSymptoms.join(" و ")}`
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
// Priority 2: Biological factors
|
||
if (activePet.age > 7) {
|
||
const heart = products.find(p => p.id === "herz-vital" || p.slug?.includes("herz"));
|
||
if (heart && !picks.some(x => x.product.id === heart.id)) {
|
||
picks.push({ product: heart, reason: "محافظت از قلب پتهای مسن" });
|
||
}
|
||
}
|
||
|
||
// Fill defaults if needed
|
||
if (picks.length < 4) {
|
||
const defaults = products.filter(p => !picks.some(x => x.product.id === p.id) && (p.suitableFor === activePet.type || p.suitableFor === "هر دو"));
|
||
defaults.slice(0, 4 - picks.length).forEach(p => picks.push({ product: p }));
|
||
}
|
||
|
||
return picks;
|
||
}, [activePet, products]);
|
||
|
||
const handleNext = () => setStep(s => s + 1);
|
||
const handleBackStep = () => setStep(s => s - 1);
|
||
|
||
const handleSubmit = () => {
|
||
if (view === "edit" && activePet) {
|
||
updatePet(activePet.id, formData);
|
||
toast.success("اطلاعات شناسنامه با موفقیت بروزرسانی شد");
|
||
setView("detail");
|
||
} else {
|
||
addPet(formData);
|
||
toast.success("شناسنامه جدید با موفقیت صادر شد");
|
||
setView("detail");
|
||
}
|
||
setStep(1);
|
||
};
|
||
|
||
const handleHealthLogSubmit = () => {
|
||
if (activePet) {
|
||
addHealthLog(activePet.id, logForm);
|
||
toast.success("گزارش سلامت با موفقیت ثبت شد");
|
||
setIsAddingHealthLog(false);
|
||
setLogForm({
|
||
appetite: "عالی",
|
||
energy: "نرمال",
|
||
digestion: "نرمال",
|
||
note: ""
|
||
});
|
||
}
|
||
};
|
||
|
||
const handleReminderSubmit = () => {
|
||
if (activePet) {
|
||
if (!reminderForm.title) {
|
||
toast.error("لطفاً عنوان یادآور را وارد کنید");
|
||
return;
|
||
}
|
||
addReminder(activePet.id, reminderForm);
|
||
toast.success("یادآور با موفقیت ثبت شد");
|
||
setIsAddingReminder(false);
|
||
setReminderForm({
|
||
title: "",
|
||
time: "08:00",
|
||
frequency: "روزانه",
|
||
productId: ""
|
||
});
|
||
}
|
||
};
|
||
|
||
const handleToggleReminder = (reminder: Reminder) => {
|
||
if (activePet) {
|
||
const today = new Date().toISOString().split('T')[0];
|
||
toggleReminder(activePet.id, reminder.id, today);
|
||
|
||
const isCompleting = !reminder.completedDates.includes(today);
|
||
if (isCompleting) {
|
||
toast.success(`دوز ${reminder.title} تایید شد`);
|
||
}
|
||
}
|
||
};
|
||
|
||
const startEdit = () => {
|
||
if (activePet) {
|
||
setFormData({
|
||
name: activePet.name,
|
||
type: activePet.type,
|
||
breed: activePet.breed,
|
||
age: activePet.age,
|
||
weight: activePet.weight,
|
||
activityLevel: activePet.activityLevel,
|
||
medicalConditions: activePet.medicalConditions,
|
||
reminders: activePet.reminders || [],
|
||
logs: activePet.logs || [],
|
||
consumptions: activePet.consumptions || []
|
||
});
|
||
setView("edit");
|
||
setStep(1);
|
||
}
|
||
};
|
||
|
||
const confirmDelete = (pet: GlobalPetProfile) => {
|
||
setPetToDelete(pet);
|
||
};
|
||
|
||
const handlePetDelete = () => {
|
||
if (petToDelete) {
|
||
removePet(petToDelete.id);
|
||
toast.error(`شناسنامه ${petToDelete.name} حذف شد`);
|
||
setPetToDelete(null);
|
||
if (pets.length <= 1) {
|
||
setView("add");
|
||
} else {
|
||
setView("index");
|
||
}
|
||
}
|
||
};
|
||
|
||
if (isLoading && view === "detail" && activePet) {
|
||
return (
|
||
<div className="max-w-6xl mx-auto px-4 py-20" dir="rtl">
|
||
<PetProfileSkeleton />
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// 1. Pet Index Page
|
||
if (view === "index") {
|
||
return (
|
||
<div className="min-h-screen bg-medical-gray-50 py-20 px-4 font-vazir" dir="rtl">
|
||
<div className="max-w-6xl mx-auto">
|
||
<div className="flex flex-col md:flex-row justify-between items-center mb-12 gap-6">
|
||
<div>
|
||
<h1 className="text-4xl font-black text-medical-gray-900 italic">همدمهای من</h1>
|
||
<p className="text-medical-gray-500 mt-2 font-bold">مدیریت شناسنامههای سلامت و سوابق درمانی</p>
|
||
</div>
|
||
<button
|
||
onClick={() => {
|
||
setFormData({
|
||
name: "",
|
||
type: "سگ",
|
||
breed: "",
|
||
age: 0,
|
||
weight: 0,
|
||
activityLevel: "متوسط",
|
||
medicalConditions: [],
|
||
reminders: [],
|
||
logs: [],
|
||
consumptions: []
|
||
});
|
||
setView("add");
|
||
setStep(1);
|
||
}}
|
||
className="bg-canina-blue text-white px-8 py-4 rounded-[2rem] font-black shadow-xl shadow-canina-blue/20 hover:scale-105 transition-all flex items-center gap-3"
|
||
>
|
||
<Plus className="w-5 h-5" />
|
||
صدور شناسنامه جدید
|
||
</button>
|
||
</div>
|
||
|
||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||
{pets.map(pet => (
|
||
<motion.div
|
||
key={pet.id}
|
||
whileHover={{ y: -5 }}
|
||
className="bg-white rounded-[3rem] border border-medical-gray-200 p-8 shadow-sm hover:shadow-2xl transition-all relative group"
|
||
>
|
||
<div className="w-24 h-24 bg-medical-gray-50 rounded-[2rem] flex items-center justify-center text-canina-blue mb-6 group-hover:bg-canina-blue group-hover:text-white transition-all shadow-inner">
|
||
{pet.type === "سگ" ? <Dog className="w-12 h-12" /> : <Cat className="w-12 h-12" />}
|
||
</div>
|
||
<h3 className="text-2xl font-black text-medical-gray-900 mb-1">{pet.name}</h3>
|
||
<p className="text-sm font-bold text-medical-gray-400 mb-6">{pet.breed}</p>
|
||
|
||
<div className="grid grid-cols-2 gap-4 mb-8">
|
||
<div className="bg-medical-gray-50 p-4 rounded-2xl flex flex-col items-center">
|
||
<span className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest">وزن</span>
|
||
<span className="text-lg font-black text-medical-gray-900">{toPersian(pet.weight)} <span className="text-xs">کیلوگرم</span></span>
|
||
</div>
|
||
<div className="bg-medical-gray-50 p-4 rounded-2xl flex flex-col items-center">
|
||
<span className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest">سن</span>
|
||
<span className="text-lg font-black text-medical-gray-900">{toPersian(pet.age)} <span className="text-xs">سال</span></span>
|
||
</div>
|
||
</div>
|
||
|
||
<button
|
||
onClick={() => { setActivePet(pet.id); setView("detail"); }}
|
||
className="w-full py-4 bg-medical-gray-900 text-white rounded-2xl font-black hover:bg-canina-blue transition-all flex items-center justify-center gap-2 group-hover:shadow-lg"
|
||
>
|
||
مشاهده شناسنامه سلامت
|
||
<ChevronLeft className="w-4 h-4" />
|
||
</button>
|
||
|
||
<button
|
||
onClick={() => confirmDelete(pet)}
|
||
className="absolute top-8 left-8 text-medical-gray-300 hover:text-red-500 transition-colors"
|
||
>
|
||
<Trash2 className="w-5 h-5" />
|
||
</button>
|
||
</motion.div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Delete Confirmation Modal */}
|
||
<AnimatePresence>
|
||
{petToDelete && (
|
||
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4">
|
||
<motion.div
|
||
initial={{ opacity: 0 }}
|
||
animate={{ opacity: 1 }}
|
||
exit={{ opacity: 0 }}
|
||
className="absolute inset-0 bg-medical-gray-900/60 backdrop-blur-sm"
|
||
onClick={() => setPetToDelete(null)}
|
||
/>
|
||
<motion.div
|
||
initial={{ scale: 0.9, opacity: 0 }}
|
||
animate={{ scale: 1, opacity: 1 }}
|
||
exit={{ scale: 0.9, opacity: 0 }}
|
||
className="relative bg-white rounded-[3rem] p-10 max-w-md w-full shadow-2xl text-center"
|
||
>
|
||
<div className="w-16 h-16 bg-red-50 text-red-500 rounded-2xl flex items-center justify-center mx-auto mb-6">
|
||
<AlertTriangle className="w-8 h-8" />
|
||
</div>
|
||
<h3 className="text-2xl font-black text-medical-gray-900 mb-4">حذف شناسنامه {petToDelete.name}؟</h3>
|
||
<p className="text-medical-gray-500 font-medium mb-10 leading-relaxed italic">
|
||
آیا از حذف شناسنامه {petToDelete.name} اطمینان دارید؟ این عمل غیرقابل بازگشت است و تمام سوابق سلامت او حذف خواهد شد.
|
||
</p>
|
||
<div className="flex gap-4">
|
||
<button
|
||
onClick={() => setPetToDelete(null)}
|
||
className="flex-1 py-4 bg-medical-gray-100 text-medical-gray-900 rounded-2xl font-black hover:bg-medical-gray-200 transition-all"
|
||
>
|
||
انصراف
|
||
</button>
|
||
<button
|
||
onClick={handlePetDelete}
|
||
className="flex-1 py-4 bg-red-500 text-white rounded-2xl font-black hover:bg-red-600 transition-all shadow-lg shadow-red-500/20"
|
||
>
|
||
حذف نهایی
|
||
</button>
|
||
</div>
|
||
</motion.div>
|
||
</div>
|
||
)}
|
||
</AnimatePresence>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// 2. Add / Edit Pet Steps
|
||
if (view === "add" || view === "edit") {
|
||
if (view === "add" && pets.length === 0) {
|
||
return (
|
||
<div className="min-h-screen bg-medical-gray-50 py-8 px-4 font-vazir" dir="rtl">
|
||
<div className="max-w-5xl mx-auto space-y-6">
|
||
{/* Render SmartAdvisor directly without redundant top banner */}
|
||
<SmartAdvisor
|
||
onComplete={(data) => {
|
||
const newPet = { ...data, id: Date.now().toString(), reminders: [], logs: [], consumptions: [] } as unknown as GlobalPetProfile;
|
||
addPet(newPet);
|
||
setActivePet(newPet.id);
|
||
toast.success(`شناسنامه سلامت ${data.name} با موفقیت صادر شد`);
|
||
setView("detail");
|
||
}}
|
||
/>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
return (
|
||
<div className="min-h-screen bg-medical-gray-50 py-20 px-4 font-vazir" dir="rtl">
|
||
<div className="max-w-2xl mx-auto">
|
||
<div className="bg-white rounded-[3rem] p-12 border border-medical-gray-200 shadow-2xl relative overflow-hidden">
|
||
{/* Progress Bar */}
|
||
<div className="absolute top-0 left-0 w-full h-1.5 bg-medical-gray-100 font-vazir">
|
||
<motion.div
|
||
className="h-full bg-canina-blue"
|
||
initial={{ width: "0%" }}
|
||
animate={{ width: `${(step / 3) * 100}%` }}
|
||
/>
|
||
</div>
|
||
|
||
<div className="mb-10 flex items-center justify-between">
|
||
<div>
|
||
<h2 className="text-3xl font-black text-medical-gray-900">{view === "edit" ? "ویرایش شناسنامه" : "صدور شناسنامه سلامت"}</h2>
|
||
<p className="text-medical-gray-500 mt-2 font-medium">مرحله {toPersian(step)} از {toPersian(3)}: بروزرسانی اطلاعات تخصصی</p>
|
||
</div>
|
||
<div className="w-16 h-16 bg-canina-blue/10 rounded-2xl flex items-center justify-center text-canina-blue">
|
||
<ClipboardList className="w-8 h-8" />
|
||
</div>
|
||
</div>
|
||
|
||
<AnimatePresence mode="wait">
|
||
{step === 1 && (
|
||
<motion.div
|
||
key="step1"
|
||
initial={{ opacity: 0, x: 20 }}
|
||
animate={{ opacity: 1, x: 0 }}
|
||
exit={{ opacity: 0, x: -20 }}
|
||
className="space-y-6"
|
||
>
|
||
<div>
|
||
<label className="block text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-3 pr-2">نام همدم</label>
|
||
<input
|
||
autoFocus
|
||
type="text"
|
||
value={formData.name}
|
||
onChange={e => setFormData({ ...formData, name: e.target.value })}
|
||
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-4 px-6 focus:ring-2 focus:ring-canina-blue/20 outline-none font-black text-lg"
|
||
placeholder="مثلاً: لوسی"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-3 pr-2">گونه جانوری</label>
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<button
|
||
onClick={() => setFormData({ ...formData, type: "سگ" })}
|
||
className={`flex items-center justify-center gap-3 py-4 border-2 rounded-2xl transition-all ${formData.type === "سگ" ? 'border-canina-blue bg-canina-blue/5 text-canina-blue font-black' : 'border-medical-gray-200 text-medical-gray-400 font-bold'}`}
|
||
>
|
||
<Dog className="w-6 h-6" />
|
||
سگ
|
||
</button>
|
||
<button
|
||
onClick={() => setFormData({ ...formData, type: "گربه" })}
|
||
className={`flex items-center justify-center gap-3 py-4 border-2 rounded-2xl transition-all ${formData.type === "گربه" ? 'border-canina-blue bg-canina-blue/5 text-canina-blue font-black' : 'border-medical-gray-200 text-medical-gray-400 font-bold'}`}
|
||
>
|
||
<Cat className="w-6 h-6" />
|
||
گربه
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<label className="block text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-3 pr-2">نژاد دقیق</label>
|
||
<input
|
||
type="text"
|
||
value={formData.breed}
|
||
onChange={e => setFormData({ ...formData, breed: e.target.value })}
|
||
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-4 px-6 focus:ring-2 focus:ring-canina-blue/20 outline-none font-bold"
|
||
placeholder="آلمانی، پرشین، میکس و..."
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-3 pr-2">تصویر یا عکس پت</label>
|
||
<div className="flex gap-2">
|
||
<input
|
||
type="text"
|
||
value={formData.imageUrl || ''}
|
||
onChange={e => setFormData({ ...formData, imageUrl: e.target.value })}
|
||
className="flex-1 bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-3 px-4 focus:ring-2 focus:ring-canina-blue/20 outline-none text-xs font-mono"
|
||
placeholder="آدرس یا لینک عکس..."
|
||
/>
|
||
<label className="bg-canina-blue text-white px-4 py-3 rounded-2xl text-xs font-bold cursor-pointer hover:bg-canina-dark transition-all flex items-center justify-center shrink-0">
|
||
آپلود فایل
|
||
<input
|
||
type="file"
|
||
accept="image/*"
|
||
className="hidden"
|
||
onChange={async (e) => {
|
||
const file = e.target.files?.[0];
|
||
if (!file) return;
|
||
const fd = new FormData();
|
||
fd.append('file', file);
|
||
try {
|
||
const res = await api.post('/upload', fd, { headers: { 'Content-Type': 'multipart/form-data' } });
|
||
const url = res.data?.url || res.data?.path || res.data?.data?.url;
|
||
if (url) {
|
||
setFormData({ ...formData, imageUrl: url });
|
||
toast.success("تصویر با موفقیت آپلود شد");
|
||
}
|
||
} catch {
|
||
toast.error("خطا در آپلود تصویر");
|
||
}
|
||
}}
|
||
/>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</motion.div>
|
||
)}
|
||
|
||
{step === 2 && (
|
||
<motion.div
|
||
key="step2"
|
||
initial={{ opacity: 0, x: 20 }}
|
||
animate={{ opacity: 1, x: 0 }}
|
||
exit={{ opacity: 0, x: -20 }}
|
||
className="space-y-6"
|
||
>
|
||
<div className="grid md:grid-cols-2 gap-6">
|
||
<div>
|
||
<label className="block text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-3 pr-2">سن (سال)</label>
|
||
<input
|
||
type="number"
|
||
value={formData.age || ""}
|
||
onChange={e => setFormData({ ...formData, age: parseInt(e.target.value) })}
|
||
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-4 px-6 focus:ring-2 focus:ring-canina-blue/20 outline-none font-black text-lg"
|
||
placeholder="۳"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-3 pr-2">وزن نهایی (کیلوگرم)</label>
|
||
<div className="relative">
|
||
<input
|
||
type="number"
|
||
value={formData.weight || ""}
|
||
onChange={e => setFormData({ ...formData, weight: parseFloat(e.target.value) })}
|
||
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-4 pr-6 pl-12 focus:ring-2 focus:ring-canina-blue/20 outline-none font-black text-lg"
|
||
/>
|
||
<Weight className="absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-medical-gray-400" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<label className="block text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-3 pr-2">سطح فعالیت روزانه</label>
|
||
<div className="grid grid-cols-3 gap-3">
|
||
{["کم", "متوسط", "زیاد"].map(level => (
|
||
<button
|
||
key={level}
|
||
onClick={() => setFormData({ ...formData, activityLevel: level as "کم" | "متوسط" | "زیاد" })}
|
||
className={`py-4 border-2 rounded-2xl transition-all text-sm font-black italic ${formData.activityLevel === level ? 'border-canina-blue bg-canina-blue/5 text-canina-blue shadow-lg shadow-canina-blue/10' : 'border-medical-gray-100 text-medical-gray-400'}`}
|
||
>
|
||
{level}
|
||
</button>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</motion.div>
|
||
)}
|
||
|
||
{step === 3 && (
|
||
<motion.div
|
||
key="step3"
|
||
initial={{ opacity: 0, x: 20 }}
|
||
animate={{ opacity: 1, x: 0 }}
|
||
exit={{ opacity: 0, x: -20 }}
|
||
className="space-y-6"
|
||
>
|
||
<label className="block text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-3 pr-2">سوابق درمانی و موارد خاص</label>
|
||
<div className="grid grid-cols-1 gap-3">
|
||
{[
|
||
"جراحی مفاصل",
|
||
"زایمان اخیر",
|
||
"بارداری",
|
||
"مشکلات گوارشی",
|
||
"ریزش موی شدید",
|
||
"بیاشتهایی"
|
||
].map(condition => (
|
||
<button
|
||
key={condition}
|
||
onClick={() => {
|
||
const current = formData.medicalConditions;
|
||
setFormData({
|
||
...formData,
|
||
medicalConditions: current.includes(condition)
|
||
? current.filter(c => c !== condition)
|
||
: [...current, condition]
|
||
})
|
||
}}
|
||
className={`flex items-center gap-4 p-4 border-2 rounded-2xl transition-all ${formData.medicalConditions.includes(condition) ? 'border-canina-blue bg-canina-blue/5 text-canina-blue' : 'border-medical-gray-50 text-medical-gray-500'}`}
|
||
>
|
||
<div className={`w-6 h-6 rounded-lg flex items-center justify-center border ${formData.medicalConditions.includes(condition) ? 'bg-canina-blue border-canina-blue text-white' : 'border-medical-gray-200'}`}>
|
||
{formData.medicalConditions.includes(condition) && <ShieldCheck className="w-3.5 h-3.5" />}
|
||
</div>
|
||
<span className="font-black text-sm italic">{condition}</span>
|
||
</button>
|
||
))}
|
||
</div>
|
||
</motion.div>
|
||
)}
|
||
</AnimatePresence>
|
||
|
||
<div className="mt-12 flex gap-4">
|
||
<button
|
||
onClick={() => {
|
||
if (view === "edit") setView("detail");
|
||
else if (pets.length > 0) setView("index");
|
||
else router.back();
|
||
}}
|
||
className="absolute top-8 left-8 text-medical-gray-400 hover:text-canina-blue transition-colors text-[10px] font-black uppercase tracking-widest"
|
||
>
|
||
انصراف
|
||
</button>
|
||
{step > 1 && (
|
||
<button
|
||
onClick={handleBackStep}
|
||
className="flex-1 bg-medical-gray-50 text-medical-gray-500 py-4 rounded-2xl font-black flex items-center justify-center gap-2 hover:bg-medical-gray-100 transition-all"
|
||
>
|
||
<ChevronRight className="w-5 h-5" />
|
||
قبلی
|
||
</button>
|
||
)}
|
||
<button
|
||
onClick={step === 3 ? handleSubmit : handleNext}
|
||
disabled={step === 1 && (!formData.name || !formData.type)}
|
||
className="flex-[2] bg-medical-gray-900 text-white py-4 rounded-2xl font-black flex items-center justify-center gap-2 disabled:opacity-50 hover:bg-canina-blue transition-all"
|
||
>
|
||
{step === 3 ? (view === "edit" ? "ذخیره تغییرات" : "مشاهده پرونده سلامت") : "گام بعدی"}
|
||
<ChevronLeft className="w-5 h-5" />
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// 3. Detailed Dashboard View
|
||
if (!activePet) return null;
|
||
|
||
return (
|
||
<div className="min-h-screen bg-medical-gray-50 py-12 px-4 font-vazir" dir="rtl">
|
||
<div className="max-w-7xl mx-auto space-y-10">
|
||
|
||
{/* Navigation Bar */}
|
||
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-6">
|
||
<div className="flex items-center gap-2 text-[10px] font-black text-medical-gray-400 uppercase tracking-widest">
|
||
<span className="cursor-pointer hover:text-canina-blue" onClick={() => setView("index")}>همدمهای من</span>
|
||
<ChevronLeft className="w-3 h-3" />
|
||
<span className="text-canina-blue">پرونده سلامت {activePet?.name}</span>
|
||
</div>
|
||
|
||
<div className="flex gap-3">
|
||
<button
|
||
onClick={() => setView("index")}
|
||
className="px-6 py-2.5 bg-white border border-medical-gray-200 rounded-xl text-[10px] font-black uppercase tracking-widest hover:border-canina-blue transition-all"
|
||
>
|
||
بازگشت به لیست
|
||
</button>
|
||
<button
|
||
onClick={startEdit}
|
||
className="px-6 py-2.5 bg-canina-blue text-white rounded-xl text-[10px] font-black uppercase tracking-widest shadow-lg shadow-canina-blue/20 hover:scale-105 transition-all flex items-center gap-2"
|
||
>
|
||
<Edit3 className="w-3.5 h-3.5" />
|
||
ویرایش پرونده
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Profile Card */}
|
||
<section className="grid lg:grid-cols-3 gap-8">
|
||
<div className="lg:col-span-2 bg-white rounded-[3.5rem] p-10 border border-medical-gray-200 shadow-xl flex flex-col md:flex-row gap-10 items-center relative overflow-hidden">
|
||
<div className="absolute top-0 left-0 w-32 h-32 bg-canina-blue/5 rounded-full blur-[60px] -translate-x-1/2 -translate-y-1/2" />
|
||
|
||
<div className="w-44 h-44 rounded-[2.5rem] bg-gradient-to-tr from-canina-blue to-blue-400 p-1 flex-shrink-0 shadow-2xl">
|
||
<div className="w-full h-full bg-white rounded-[2.2rem] flex items-center justify-center text-canina-blue">
|
||
{activePet.type === "سگ" ? <Dog className="w-24 h-24" /> : <Cat className="w-24 h-24" />}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex-1 text-center md:text-right relative z-10">
|
||
<div className="flex flex-col md:flex-row md:items-end gap-3 mb-6">
|
||
<h2 className="text-5xl font-black text-medical-gray-900 italic leading-none">{activePet.name}</h2>
|
||
<span className="text-sm font-bold text-medical-gray-400 mb-1">{activePet.breed}</span>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4 mb-8">
|
||
{[
|
||
{ icon: <Weight className="w-3.5 h-3.5" />, label: "وزن", value: `${toPersian(activePet.weight)} کیلوگرم` },
|
||
{ icon: <Calendar className="w-3.5 h-3.5" />, label: "سن", value: `${toPersian(activePet.age)} سال` },
|
||
{ icon: <Activity className="w-3.5 h-3.5" />, label: "فعالیت", value: activePet.activityLevel },
|
||
{ icon: <ShieldCheck className="w-3.5 h-3.5" />, label: "وضعیت", value: "نرمال" },
|
||
].map((item, idx) => (
|
||
<div key={idx} className="bg-medical-gray-50 border border-medical-gray-100 p-3 rounded-2xl">
|
||
<div className="flex items-center gap-1.5 text-[8px] font-black text-medical-gray-400 uppercase tracking-widest mb-1">
|
||
{item.icon}
|
||
{item.label}
|
||
</div>
|
||
<div className="text-[12px] font-black text-medical-gray-900">{item.value}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
<div className="flex flex-wrap gap-2">
|
||
{activePet.medicalConditions.map(c => (
|
||
<div key={c} className="bg-canina-blue text-white px-4 py-2 rounded-xl text-[10px] font-bold border border-white/10 flex items-center gap-2 shadow-sm">
|
||
<Stethoscope className="w-3 h-3" />
|
||
{c}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-canina-blue rounded-[3.5rem] p-10 text-white flex flex-col justify-between relative overflow-hidden shadow-2xl group">
|
||
<div className="absolute top-0 right-0 p-8 opacity-20 group-hover:rotate-12 transition-transform duration-700">
|
||
<Activity className="w-16 h-16 text-white" />
|
||
</div>
|
||
<div className="mt-16 relative z-10">
|
||
<div className="text-[10px] font-black uppercase tracking-[0.3em] text-white/70 mb-2">امتیاز سلامت پایش شده</div>
|
||
<div className="text-7xl font-black italic text-white flex items-baseline gap-2">
|
||
{toPersian(88)}
|
||
<span className="text-2xl not-italic text-white/50">/۱۰۰</span>
|
||
</div>
|
||
</div>
|
||
<div className="mt-10 relative z-10">
|
||
<div className="h-2 w-full bg-white/10 rounded-full overflow-hidden border border-white/10 mb-3">
|
||
<motion.div initial={{ width: 0 }} animate={{ width: "88%" }} className="h-full bg-white" />
|
||
</div>
|
||
<p className="text-[10px] text-white font-bold italic leading-relaxed">
|
||
بر اساس پایش هوشمند، وضعیت {activePet.name} در سطح «ایدهآل» قرار دارد.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Kindness Footprint Widget for Pet */}
|
||
<div className="bg-pink-50 rounded-[3.5rem] p-10 border border-pink-100 relative overflow-hidden flex flex-col justify-between">
|
||
<div className="absolute -bottom-8 -left-8 opacity-5">
|
||
<Heart className="w-48 h-48 fill-pink-500" />
|
||
</div>
|
||
<div className="flex items-center gap-4 mb-6">
|
||
<div className="w-12 h-12 bg-pink-500 rounded-2xl flex items-center justify-center text-white shadow-lg shadow-pink-100">
|
||
<Heart className="w-6 h-6 fill-white" />
|
||
</div>
|
||
<div>
|
||
<h3 className="text-xl font-black text-medical-gray-900 italic">ردپای مهربانی</h3>
|
||
<p className="text-[10px] font-black text-pink-500 uppercase tracking-widest mt-1">سفیر مهربانی: {activePet.name}</p>
|
||
</div>
|
||
</div>
|
||
<div className="space-y-4 relative z-10">
|
||
<p className="text-sm text-medical-gray-600 font-medium leading-relaxed">
|
||
{activePet.name} با خریدهای مکمل خود، تا کنون به تامین هزینههای درمانی و تغذیه حیوانات پناهگاهی کمک کرده است.
|
||
</p>
|
||
<div className="bg-white/80 backdrop-blur-sm rounded-2xl p-4 border border-pink-100">
|
||
<div className="flex items-center justify-between">
|
||
<span className="text-[10px] font-black text-medical-gray-400">سطح مشارکت</span>
|
||
<span className="text-sm font-black text-pink-500 italic">حامی وفادار</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
{/* Refill & Stock Tracking */}
|
||
<section className="bg-white rounded-[3.5rem] p-10 border border-medical-gray-200 shadow-sm relative overflow-hidden">
|
||
<div className="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-canina-blue to-blue-300" />
|
||
<div className="flex items-center justify-between mb-8">
|
||
<div className="flex items-center gap-3">
|
||
<Package className="w-8 h-8 text-canina-blue" />
|
||
<h3 className="text-2xl font-black text-medical-gray-900 italic">پایش موجودی مکملها</h3>
|
||
</div>
|
||
<div className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest hidden md:block">بروزرسانی لحظهای بر اساس مصرف</div>
|
||
</div>
|
||
|
||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||
{(activePet?.consumptions || []).length === 0 ? (
|
||
<div className="col-span-full py-12 text-center bg-medical-gray-50 rounded-[2.5rem] border border-dashed border-medical-gray-200 p-6 space-y-4">
|
||
<div>
|
||
<p className="text-medical-gray-700 font-black text-sm">هنوز محصولی برای پایش موجودی این پت ثبت نشده است.</p>
|
||
<p className="text-xs text-medical-gray-500 mt-1">با ثبت سفارش جدید، الگوریتم پایش هوشمند کالا به صورت خودکار فعال میشود. همچنین میتوانید محصول مورد مصرف فعلی را انتخاب کنید:</p>
|
||
</div>
|
||
<div className="flex flex-wrap justify-center gap-2 max-w-xl mx-auto pt-2">
|
||
{products.slice(0, 5).map(p => (
|
||
<button
|
||
key={p.id}
|
||
onClick={() => {
|
||
const newConsumptions = [...(activePet.consumptions || []), {
|
||
productId: p.id,
|
||
packageSize: p.packageSize || 60,
|
||
remaining: p.packageSize || 60
|
||
}];
|
||
updatePet(activePet.id, { consumptions: newConsumptions });
|
||
toast.success(`مکمل ${p.name} به لیست پایش اضافه شد`);
|
||
}}
|
||
className="px-3.5 py-2 bg-white border border-medical-gray-200 hover:border-canina-blue hover:bg-canina-blue/5 text-medical-gray-800 text-xs font-bold rounded-xl transition-all flex items-center gap-1.5 shadow-xs"
|
||
>
|
||
<span>+ افزودن {p.name}</span>
|
||
</button>
|
||
))}
|
||
</div>
|
||
</div>
|
||
) : (
|
||
activePet.consumptions.map((consumption) => {
|
||
const product = products.find(p => p.id === consumption.productId);
|
||
if (!product) return null;
|
||
|
||
const percentage = (consumption.remaining / (consumption.packageSize || 1)) * 100;
|
||
const isLow = percentage < 25;
|
||
|
||
return (
|
||
<div key={consumption.productId} className="bg-medical-gray-50 p-6 rounded-3xl border border-medical-gray-100 group">
|
||
<div className="flex justify-between items-start mb-4">
|
||
<div>
|
||
<h4 className="text-sm font-black text-medical-gray-900 mb-1">{product.name}</h4>
|
||
<p className="text-[10px] text-medical-gray-400 font-bold">باقیمانده: {toPersian(consumption.remaining)} واحد</p>
|
||
</div>
|
||
<div className={cn(
|
||
"w-12 h-12 rounded-2xl flex items-center justify-center font-black text-xs",
|
||
isLow ? "bg-red-50 text-red-500 animate-pulse" : "bg-white text-canina-blue shadow-inner"
|
||
)}>
|
||
{toPersian(Math.round(percentage))}%
|
||
</div>
|
||
</div>
|
||
|
||
<div className="h-2 w-full bg-medical-gray-200 rounded-full overflow-hidden mb-4">
|
||
<motion.div
|
||
initial={{ width: 0 }}
|
||
animate={{ width: `${Math.min(100, Math.max(0, percentage))}%` }}
|
||
className={cn("h-full", isLow ? "bg-red-500" : "bg-canina-blue")}
|
||
/>
|
||
</div>
|
||
|
||
{isLow && (
|
||
<button
|
||
onClick={() => router.push(`/shop/${product.id}`)}
|
||
className="w-full py-2 bg-red-500 text-white rounded-xl text-[10px] font-black uppercase tracking-widest hover:bg-red-600 transition-all flex items-center justify-center gap-2"
|
||
>
|
||
<Package className="w-3.5 h-3.5" />
|
||
سفارش مجدد (Refill)
|
||
</button>
|
||
)}
|
||
</div>
|
||
);
|
||
})
|
||
)}
|
||
</div>
|
||
</section>
|
||
|
||
{/* Tab Selection */}
|
||
<div className="flex border-b border-medical-gray-200 gap-8 px-4">
|
||
{[
|
||
{ id: "health", label: "داشبورد سلامت و یادآوریها", icon: <Heart className="w-4 h-4" /> },
|
||
{ id: "orders", label: "سوابق خرید و تاریخچه تخصصی", icon: <Package className="w-4 h-4" /> },
|
||
].map(tab => (
|
||
<button
|
||
key={tab.id}
|
||
onClick={() => setActivePetTab(tab.id as "health" | "orders")}
|
||
className={`pb-4 flex items-center gap-2 text-sm font-black transition-all relative ${activePetTab === tab.id ? "text-canina-blue" : "text-medical-gray-400 hover:text-medical-gray-600"}`}
|
||
>
|
||
{tab.icon}
|
||
{tab.label}
|
||
{activePetTab === tab.id && <motion.div layoutId="petTab" className="absolute bottom-0 left-0 right-0 h-1 bg-canina-blue rounded-full" />}
|
||
</button>
|
||
))}
|
||
</div>
|
||
|
||
{activePetTab === "health" ? (
|
||
<>
|
||
{/* Personalized Recommendations */}
|
||
<section>
|
||
<div className="flex items-center justify-between mb-8 px-4">
|
||
<div className="flex items-center gap-4">
|
||
<div className="w-12 h-12 bg-white rounded-2xl flex items-center justify-center text-canina-blue shadow-lg border border-medical-gray-100">
|
||
<Star className="w-6 h-6" />
|
||
</div>
|
||
<h3 className="text-3xl font-black text-medical-gray-900 italic">توصیههای درمانی اختصاصی</h3>
|
||
</div>
|
||
<p className="text-[10px] font-black text-medical-gray-400 uppercase tracking-wider">کاتالوگ ۲۰۲۴ آلمان</p>
|
||
</div>
|
||
|
||
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||
{recommendedProducts.map(({ product: p, reason }) => (
|
||
<motion.div
|
||
key={p.id}
|
||
initial={{ opacity: 0, y: 20 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
onClick={() => router.push(`/shop/${p.slug || p.id}`)}
|
||
className="bg-white rounded-[2.5rem] p-6 border border-medical-gray-200 hover:shadow-2xl transition-all cursor-pointer group flex flex-col justify-between relative overflow-hidden"
|
||
>
|
||
{reason && (
|
||
<div className="absolute top-0 left-0 right-0 bg-canina-blue/10 py-2 px-4 text-[9px] font-black text-canina-blue text-center border-b border-canina-blue/10">
|
||
{reason}
|
||
</div>
|
||
)}
|
||
<div className={cn("aspect-square bg-medical-gray-50 rounded-[1.5rem] p-6 mb-6 flex items-center justify-center overflow-hidden", reason ? "mt-8" : "")}>
|
||
<SafeImage src={p.image} alt={p.name} className="w-full h-full group-hover:scale-110 transition-transform duration-500" imgClassName="object-contain" />
|
||
</div>
|
||
<div>
|
||
<h4 className="text-lg font-black text-medical-gray-900 group-hover:text-canina-blue transition-colors mb-2 italic">{p.name}</h4>
|
||
<p className="text-xs text-medical-gray-400 line-clamp-2 leading-relaxed mb-6 font-medium">{p.benefits}</p>
|
||
<button className="w-full py-3.5 bg-medical-gray-900 text-white rounded-xl text-[10px] font-black uppercase tracking-widest group-hover:bg-canina-blue transition-colors flex items-center justify-center gap-2">
|
||
بررسی تخصصی
|
||
<ChevronLeft className="w-3.5 h-3.5" />
|
||
</button>
|
||
</div>
|
||
</motion.div>
|
||
))}
|
||
</div>
|
||
</section>
|
||
|
||
<div className="grid lg:grid-cols-2 gap-10">
|
||
{/* Reminders */}
|
||
<div className="bg-white rounded-[3rem] border border-medical-gray-200 p-10 shadow-sm">
|
||
<div className="flex items-center justify-between mb-10">
|
||
<div className="flex items-center gap-3">
|
||
<Clock className="w-8 h-8 text-canina-blue" />
|
||
<h3 className="text-2xl font-black text-medical-gray-900 italic">یادآورهای فعال</h3>
|
||
</div>
|
||
<TrendingUp className="w-6 h-6 text-green-500" />
|
||
</div>
|
||
|
||
<div className="space-y-4">
|
||
{(activePet?.reminders || []).length === 0 ? (
|
||
<div className="text-center py-10 bg-medical-gray-50 rounded-2xl border border-dashed border-medical-gray-200">
|
||
<p className="text-sm font-bold text-medical-gray-400 italic">هنوز یادآوری ثبت نشده است</p>
|
||
</div>
|
||
) : (
|
||
(activePet?.reminders || []).map((reminder) => {
|
||
const today = new Date().toISOString().split('T')[0];
|
||
const isCompleted = reminder.completedDates.includes(today);
|
||
|
||
return (
|
||
<div
|
||
key={reminder.id}
|
||
className={cn(
|
||
"flex items-center justify-between p-5 rounded-2xl border transition-all group",
|
||
isCompleted ? "bg-green-50 border-green-200" : "bg-medical-gray-50 border-medical-gray-100 hover:border-canina-blue/30"
|
||
)}
|
||
>
|
||
<div className="flex items-center gap-4">
|
||
<button
|
||
onClick={() => handleToggleReminder(reminder)}
|
||
className={cn(
|
||
"w-10 h-10 rounded-xl flex items-center justify-center shadow-inner transition-colors",
|
||
isCompleted ? "bg-green-500 text-white" : "bg-white text-medical-gray-300 hover:text-canina-blue"
|
||
)}
|
||
>
|
||
{isCompleted ? <Check className="w-5 h-5" /> : <Clock className="w-5 h-5" />}
|
||
</button>
|
||
<div>
|
||
<h5 className={cn("text-[12px] font-black italic", isCompleted ? "text-green-700" : "text-medical-gray-900")}>{reminder.title}</h5>
|
||
<p className="text-[10px] text-medical-gray-400 font-bold">{toPersian(reminder.time)} - {reminder.frequency}</p>
|
||
</div>
|
||
</div>
|
||
<span className={cn("text-[10px] font-black italic", isCompleted ? "text-green-600" : "text-medical-gray-400")}>
|
||
{isCompleted ? "انجام شد" : "در انتظار"}
|
||
</span>
|
||
</div>
|
||
);
|
||
})
|
||
)}
|
||
</div>
|
||
|
||
<button
|
||
onClick={() => setIsAddingReminder(true)}
|
||
className="w-full mt-8 py-5 border-2 border-dashed border-medical-gray-200 rounded-2xl text-medical-gray-400 font-black hover:border-canina-blue hover:text-canina-blue transition-all flex items-center justify-center gap-2"
|
||
>
|
||
<Plus className="w-4 h-4" />
|
||
افزودن یادآور جدید
|
||
</button>
|
||
</div>
|
||
|
||
{/* Health Logs */}
|
||
<div className="bg-white rounded-[3rem] border border-medical-gray-200 p-10 shadow-sm">
|
||
<div className="flex items-center gap-3 mb-10">
|
||
<ClipboardList className="w-8 h-8 text-canina-blue" />
|
||
<h3 className="text-2xl font-black text-medical-gray-900 italic">تایملاین سلامت</h3>
|
||
</div>
|
||
|
||
<div className="space-y-6 max-h-[400px] overflow-y-auto pr-2 scrollbar-hide">
|
||
{(activePet?.logs || []).length === 0 ? (
|
||
<div className="text-center py-10 bg-medical-gray-50 rounded-2xl border border-dashed border-medical-gray-200">
|
||
<p className="text-sm font-bold text-medical-gray-400 italic">هنوز گزارشی ثبت نشده است</p>
|
||
</div>
|
||
) : (
|
||
(activePet?.logs || []).map((log) => (
|
||
<div key={log.id} className="bg-medical-gray-50 p-6 rounded-[2.5rem] border border-medical-gray-100 relative group">
|
||
<div className="flex justify-between items-center mb-4">
|
||
<div className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest">
|
||
{toPersian(new Date(log.date).toLocaleDateString("fa-IR"))}
|
||
</div>
|
||
<div className="flex gap-2">
|
||
<div className="px-2 py-0.5 bg-canina-blue/10 text-canina-blue rounded-full text-[8px] font-black">{log.appetite}</div>
|
||
<div className="px-2 py-0.5 bg-green-500/10 text-green-500 rounded-full text-[8px] font-black">{log.energy}</div>
|
||
</div>
|
||
</div>
|
||
<p className="text-sm text-medical-gray-600 font-medium leading-relaxed italic">
|
||
{log.note || "وضعیتی برای این روز ثبت نشده است."}
|
||
</p>
|
||
</div>
|
||
))
|
||
)}
|
||
</div>
|
||
|
||
<button
|
||
onClick={() => setIsAddingHealthLog(true)}
|
||
className="w-full mt-8 py-5 bg-canina-blue text-white rounded-2xl font-black hover:bg-medical-gray-900 transition-all shadow-xl shadow-canina-blue/20"
|
||
>
|
||
ثبت گزارش روزانه جدید
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</>
|
||
) : (
|
||
<motion.div
|
||
initial={{ opacity: 0, y: 20 }}
|
||
animate={{ opacity: 1, y: 0 }}
|
||
className="bg-white rounded-[4rem] border border-medical-gray-200 p-12 min-h-[500px] shadow-sm"
|
||
>
|
||
<div className="flex items-center justify-between mb-12">
|
||
<div className="flex items-center gap-4">
|
||
<div className="w-14 h-14 bg-medical-gray-900 text-white rounded-[1.5rem] flex items-center justify-center shadow-xl">
|
||
<Package className="w-7 h-7" />
|
||
</div>
|
||
<div>
|
||
<h3 className="text-3xl font-black text-medical-gray-900 italic">تاریخچه خرید {activePet.name}</h3>
|
||
<p className="text-xs font-bold text-medical-gray-400 mt-1">لیست محصولات خریداری شده ویژه این پرونده سلامت</p>
|
||
</div>
|
||
</div>
|
||
<div className="text-left">
|
||
<div className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest">مجموع هزینه درمانی</div>
|
||
<div className="text-2xl font-black text-canina-blue">{toPersian(petOrders.reduce((a, b) => a + b.total, 0).toLocaleString())} <span className="text-xs italic">تومان</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
{petOrders.length === 0 ? (
|
||
<div className="py-24 text-center space-y-6">
|
||
<div className="w-24 h-24 bg-medical-gray-50 rounded-full flex items-center justify-center mx-auto text-medical-gray-200 shadow-inner">
|
||
<Package className="w-12 h-12" />
|
||
</div>
|
||
<p className="text-medical-gray-400 font-black text-xl italic">هنوز سفارشی برای {activePet.name} ثبت نکردهاید.</p>
|
||
<button onClick={() => router.push('/shop')} className="bg-canina-blue text-white px-10 py-4 rounded-2xl font-black hover:scale-105 transition-all">شروع اولین خرید</button>
|
||
</div>
|
||
) : (
|
||
<div className="space-y-5">
|
||
{petOrders.map(order =>
|
||
order.items.map(item => (
|
||
<motion.div
|
||
key={`${order.id}-${item.product.id}`}
|
||
initial={{ opacity: 0, x: 20 }}
|
||
animate={{ opacity: 1, x: 0 }}
|
||
whileHover={{ x: -10, backgroundColor: "rgba(249, 250, 251, 1)" }}
|
||
className="p-6 bg-white rounded-[2.5rem] border border-medical-gray-200 flex items-center justify-between gap-6 transition-all cursor-pointer group shadow-sm hover:shadow-xl"
|
||
onClick={() => router.push(`/shop/${item.product.slug || item.product.id}`)}
|
||
>
|
||
{/* Info on Left (Text side) */}
|
||
<div className="flex-1 text-right">
|
||
<h4 className="text-xl font-black text-medical-gray-900 group-hover:text-canina-blue transition-colors mb-2 italic">{item.product.name}</h4>
|
||
<div className="flex flex-wrap gap-x-6 gap-y-2 items-center">
|
||
<div className="flex items-center gap-1.5 text-xs font-bold text-medical-gray-500">
|
||
<Calendar className="w-4 h-4 text-canina-blue/40" />
|
||
{toPersian(new Date(order.date).toLocaleDateString("fa-IR", { year: 'numeric', month: 'long', day: 'numeric' }))}
|
||
</div>
|
||
<div className="flex items-center gap-1.5 px-3 py-1 bg-green-50 text-green-600 rounded-full text-[10px] font-black italic">
|
||
<Check className="w-3 h-3" />
|
||
تایید شده و تحویل شده
|
||
</div>
|
||
<div className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest bg-medical-gray-50 px-3 py-1 rounded-lg">
|
||
فاکتور: {toPersian(order.id)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Image on Right (Icon side) */}
|
||
<div className="w-20 h-20 shrink-0 bg-medical-gray-50 rounded-[1.5rem] p-3 flex items-center justify-center border border-medical-gray-100 group-hover:bg-white transition-colors relative overflow-hidden">
|
||
<SafeImage
|
||
src={item.product.image}
|
||
alt={item.product.name}
|
||
className="w-full h-full"
|
||
imgClassName="object-contain"
|
||
/>
|
||
</div>
|
||
</motion.div>
|
||
))
|
||
)}
|
||
</div>
|
||
)}
|
||
</motion.div>
|
||
)}
|
||
|
||
{/* Seal of Authenticity */}
|
||
<div className="p-16 border-4 border-double border-medical-gray-200 rounded-[5rem] text-center max-w-4xl mx-auto opacity-40 grayscale hover:opacity-100 hover:grayscale-0 transition-all duration-1000">
|
||
<div className="flex justify-center mb-8">
|
||
<div className="w-20 h-20 border-4 border-canina-blue rounded-full flex items-center justify-center text-canina-blue font-black text-2xl rotate-12 shadow-2xl">آلمان</div>
|
||
</div>
|
||
<h4 className="text-2xl font-black text-medical-gray-900 mb-4 italic">ضمانت اصالت و سلامت کنینا آلمان</h4>
|
||
<p className="text-sm text-medical-gray-500 font-medium leading-relaxed max-w-2xl mx-auto italic">
|
||
این گزارش توسط سیستم هوش مصنوعی کنینا و با انطباق کامل بر پروتکلهای درمانی Canina Pharma GmbH آلمان تولید شده است. تمامی مکملهای پیشنهادی دارای استاندارد دارویی (Pharmaceutical Grade) میباشند.
|
||
</p>
|
||
</div>
|
||
|
||
{/* Add Reminder Modal */}
|
||
<AnimatePresence>
|
||
{isAddingReminder && (
|
||
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4">
|
||
<motion.div
|
||
initial={{ opacity: 0 }}
|
||
animate={{ opacity: 1 }}
|
||
exit={{ opacity: 0 }}
|
||
className="absolute inset-0 bg-medical-gray-900/60 backdrop-blur-sm"
|
||
onClick={() => setIsAddingReminder(false)}
|
||
/>
|
||
<motion.div
|
||
initial={{ y: 50, opacity: 0, scale: 0.95 }}
|
||
animate={{ y: 0, opacity: 1, scale: 1 }}
|
||
exit={{ y: 50, opacity: 0, scale: 0.95 }}
|
||
className="relative bg-white rounded-[3rem] p-10 max-w-md w-full shadow-2xl"
|
||
>
|
||
<div className="flex items-center justify-between mb-8">
|
||
<h3 className="text-2xl font-black text-medical-gray-900 italic font-vazir">افزودن یادآور جدید</h3>
|
||
<button onClick={() => setIsAddingReminder(false)} className="text-medical-gray-400 hover:text-red-500"><Plus className="w-6 h-6 rotate-45" /></button>
|
||
</div>
|
||
|
||
<div className="space-y-6">
|
||
<div>
|
||
<label className="block text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-2 pr-2">عنوان یادآور</label>
|
||
<input
|
||
autoFocus
|
||
type="text"
|
||
value={reminderForm.title}
|
||
onChange={e => setReminderForm({ ...reminderForm, title: e.target.value })}
|
||
placeholder="مثلاً: قرص مفاصل، کانهیدروکس"
|
||
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-4 px-6 focus:ring-2 focus:ring-canina-blue/20 outline-none font-bold"
|
||
/>
|
||
</div>
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label className="block text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-2 pr-2">زمان (ساعت)</label>
|
||
<input
|
||
type="time"
|
||
value={reminderForm.time}
|
||
onChange={e => setReminderForm({ ...reminderForm, time: e.target.value })}
|
||
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-4 px-6 focus:ring-2 focus:ring-canina-blue/20 outline-none font-black"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-2 pr-2">دوره تکرار</label>
|
||
<select
|
||
value={reminderForm.frequency}
|
||
onChange={e => setReminderForm({ ...reminderForm, frequency: e.target.value as "روزانه" | "هفتگی" })}
|
||
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-4 px-6 focus:ring-2 focus:ring-canina-blue/20 outline-none font-black text-sm"
|
||
>
|
||
<option value="روزانه">روزانه</option>
|
||
<option value="هفتگی">هفتگی</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<label className="block text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-2 pr-2">متصل به محصول (اختیاری)</label>
|
||
<select
|
||
value={reminderForm.productId}
|
||
onChange={e => setReminderForm({ ...reminderForm, productId: e.target.value })}
|
||
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-4 px-6 focus:ring-2 focus:ring-canina-blue/20 outline-none font-black text-sm"
|
||
>
|
||
<option value="">هیچکدام</option>
|
||
{recommendedProducts.map(p => (
|
||
<option key={p.product.id} value={p.product.id}>{p.product.name}</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
<button
|
||
onClick={handleReminderSubmit}
|
||
className="w-full py-5 bg-canina-blue text-white rounded-2xl font-black hover:bg-medical-gray-900 transition-all shadow-xl shadow-canina-blue/20 mt-4"
|
||
>
|
||
ثبت نهایی یادآور
|
||
</button>
|
||
</div>
|
||
</motion.div>
|
||
</div>
|
||
)}
|
||
</AnimatePresence>
|
||
|
||
{/* Add Health Log Modal */}
|
||
<AnimatePresence>
|
||
{isAddingHealthLog && (
|
||
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4">
|
||
<motion.div
|
||
initial={{ opacity: 0 }}
|
||
animate={{ opacity: 1 }}
|
||
exit={{ opacity: 0 }}
|
||
className="absolute inset-0 bg-medical-gray-900/60 backdrop-blur-sm transition-all"
|
||
onClick={() => setIsAddingHealthLog(false)}
|
||
/>
|
||
<motion.div
|
||
initial={{ y: 50, opacity: 0, scale: 0.95 }}
|
||
animate={{ y: 0, opacity: 1, scale: 1 }}
|
||
exit={{ y: 50, opacity: 0, scale: 0.95 }}
|
||
className="relative bg-white rounded-[3rem] p-10 max-w-lg w-full shadow-2xl"
|
||
>
|
||
<div className="flex items-center justify-between mb-8">
|
||
<h3 className="text-2xl font-black text-medical-gray-900 italic">ثبت گزارش روزانه سلامت</h3>
|
||
<button onClick={() => setIsAddingHealthLog(false)} className="text-medical-gray-400 hover:text-red-500"><Plus className="w-6 h-6 rotate-45" /></button>
|
||
</div>
|
||
|
||
<div className="space-y-6">
|
||
<div>
|
||
<label className="block text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-4 pr-2">میزان اشتها و جذب غذا</label>
|
||
<div className="grid grid-cols-3 gap-3">
|
||
{["عالی", "متوسط", "کم"].map(opt => (
|
||
<button
|
||
key={opt}
|
||
onClick={() => setLogForm({ ...logForm, appetite: opt as "عالی" | "متوسط" | "کم" })}
|
||
className={cn(
|
||
"py-3 border-2 rounded-xl text-xs font-black transition-all",
|
||
logForm.appetite === opt ? "border-canina-blue bg-canina-blue/10 text-canina-blue" : "border-medical-gray-100 text-medical-gray-400"
|
||
)}
|
||
>
|
||
{opt}
|
||
</button>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-4 pr-2">سطح انرژی و فعالیت</label>
|
||
<div className="grid grid-cols-3 gap-3">
|
||
{["زیاد", "نرمال", "بیحال"].map(opt => (
|
||
<button
|
||
key={opt}
|
||
onClick={() => setLogForm({ ...logForm, energy: opt as "زیاد" | "نرمال" | "بیحال" })}
|
||
className={cn(
|
||
"py-3 border-2 rounded-xl text-xs font-black transition-all",
|
||
logForm.energy === opt ? "border-green-500 bg-green-500/10 text-green-500" : "border-medical-gray-100 text-medical-gray-400"
|
||
)}
|
||
>
|
||
{opt}
|
||
</button>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-2 pr-2">یادداشتهای تخصصی (تغییرات رفتاری، دفع و...)</label>
|
||
<textarea
|
||
rows={4}
|
||
value={logForm.note}
|
||
onChange={e => setLogForm({ ...logForm, note: e.target.value })}
|
||
placeholder="مشاهدات خود را برای بررسی دکتر سیستم اینجا بنویسید..."
|
||
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-4 px-6 focus:ring-2 focus:ring-canina-blue/20 outline-none font-bold text-sm resize-none"
|
||
/>
|
||
</div>
|
||
|
||
<button
|
||
onClick={handleHealthLogSubmit}
|
||
className="w-full py-5 bg-medical-gray-900 text-white rounded-2xl font-black hover:bg-canina-blue transition-all shadow-xl mt-2"
|
||
>
|
||
ثبت نهایی گزارش
|
||
</button>
|
||
</div>
|
||
</motion.div>
|
||
</div>
|
||
)}
|
||
</AnimatePresence>
|
||
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|