canina/frontend/application/components/SmartAdvisor.tsx

298 lines
15 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import React, { useState } from "react";
import { motion, AnimatePresence } from "motion/react";
import { useSettingsStore } from "../store/settingsStore";
import {
Dog,
Cat,
ChevronLeft,
ChevronRight,
ShieldCheck,
Activity,
Bone,
Sparkles,
Pill,
Sparkle
} from "lucide-react";
import { toPersian, cn } from "../lib/utils";
interface SmartAdvisorProps {
onComplete: (data: {
name: string,
type: "سگ" | "گربه",
breed: string,
age: number,
weight: number,
activityLevel: "کم" | "متوسط" | "زیاد",
medicalConditions: string[]
}) => void;
}
const MEDICAL_OPTIONS = [
{ id: "joint_surgery", label: "جراحی مفاصل", condition: "جراحی مفاصل" },
{ id: "recent_birth", label: "زایمان اخیر", condition: "زایمان اخیر" },
{ id: "pregnancy", label: "بارداری", condition: "بارداری" },
{ id: "digestion", label: "مشکلات گوارشی", condition: "مشکلات گوارشی" },
{ id: "hair_loss", label: "ریزش موی شدید", condition: "ریزش موی شدید" },
{ id: "appetite", label: "بی‌اشتهایی", condition: "بی‌اشتهایی" },
];
export default function SmartAdvisor({ onComplete }: SmartAdvisorProps) {
const texts = useSettingsStore(state => state.texts);
const medicalOptions = React.useMemo(() => {
const jsonStr = texts['medical_options'];
if (jsonStr) {
try {
return JSON.parse(jsonStr) as typeof MEDICAL_OPTIONS;
} catch (e) {
console.error("Failed to parse medical_options JSON:", e);
}
}
return MEDICAL_OPTIONS;
}, [texts]);
const [step, setStep] = useState(1);
// Step 1 Data
const [name, setName] = useState("");
const [type, setType] = useState<"سگ" | "گربه">("سگ");
const [breed, setBreed] = useState("");
// Step 2 Data
const [age, setAge] = useState<number>(3);
const [weight, setWeight] = useState<number>(10);
const [activityLevel, setActivityLevel] = useState<"کم" | "متوسط" | "زیاد">("متوسط");
// Step 3 Data
const [medicalConditions, setMedicalConditions] = useState<string[]>([]);
const handleNext = () => setStep(s => s + 1);
const handleBack = () => setStep(s => s - 1);
const handleSubmit = () => {
onComplete({
name,
type,
breed,
age,
weight,
activityLevel,
medicalConditions
});
};
return (
<section id="canina-advisor" className="py-24 bg-white">
<div className="max-w-4xl mx-auto px-4">
<div className="text-center mb-12">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
className="inline-flex items-center gap-2 px-4 py-2 bg-canina-blue/5 rounded-full mb-6"
>
<Sparkle className="w-4 h-4 text-canina-blue" />
<span className="text-canina-blue text-xs font-black uppercase tracking-widest font-vazir">تکنولوژی پایش هوشمند</span>
</motion.div>
<h2 className="text-4xl lg:text-5xl font-black text-medical-gray-900 mb-4 font-vazir italic">دستیار سلامت کانینا</h2>
<p className="text-medical-gray-500 font-bold text-lg font-vazir">فقط چند قدم تا صدور شناسنامه هوشمند و دریافت پیشنهادات تخصصی</p>
</div>
<div className="bg-medical-gray-50 rounded-[3.5rem] border border-medical-gray-100 p-8 lg:p-12 shadow-2xl shadow-medical-gray-200/50 relative overflow-hidden">
{/* Progress Indicator */}
<div className="absolute top-0 left-0 w-full h-2 bg-medical-gray-200">
<motion.div
className="h-full bg-canina-blue"
initial={{ width: "0%" }}
animate={{ width: `${(step / 3) * 100}%` }}
transition={{ duration: 0.5 }}
/>
</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-8"
>
<div className="text-center">
<h3 className="text-2xl font-black text-medical-gray-900 mb-2 font-vazir uppercase italic tracking-tight">گام اول: هویت بصری</h3>
<p className="text-medical-gray-400 font-bold font-vazir">همدم شما رو با چه اسمی صدا میزنید؟</p>
</div>
<div className="grid grid-cols-2 gap-4">
<button
onClick={() => setType("سگ")}
className={`flex flex-col items-center gap-4 p-8 rounded-[2.5rem] border-4 transition-all ${type === "سگ" ? 'border-canina-blue bg-white shadow-xl shadow-canina-blue/10 scale-105' : 'border-transparent bg-white/50 text-medical-gray-300 hover:bg-white'}`}
>
<Dog className={`w-12 h-12 ${type === "سگ" ? 'text-canina-blue' : 'text-current'}`} />
<span className="text-lg font-black font-vazir">سگ</span>
</button>
<button
onClick={() => setType("گربه")}
className={`flex flex-col items-center gap-4 p-8 rounded-[2.5rem] border-4 transition-all ${type === "گربه" ? 'border-canina-blue bg-white shadow-xl shadow-canina-blue/10 scale-105' : 'border-transparent bg-white/50 text-medical-gray-300 hover:bg-white'}`}
>
<Cat className={`w-12 h-12 ${type === "گربه" ? 'text-canina-blue' : 'text-current'}`} />
<span className="text-lg font-black font-vazir">گربه</span>
</button>
</div>
<div className="grid md:grid-cols-2 gap-6">
<div className="space-y-2">
<label className="text-xs font-black text-medical-gray-400 pr-2">نام پت</label>
<input
type="text"
value={name}
onChange={e => setName(e.target.value)}
placeholder="لوسی، تدی..."
className="w-full bg-white 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 font-vazir"
/>
</div>
<div className="space-y-2">
<label className="text-xs font-black text-medical-gray-400 pr-2">نژاد</label>
<input
type="text"
value={breed}
onChange={e => setBreed(e.target.value)}
placeholder="ژرمن، پرشین..."
className="w-full bg-white border border-medical-gray-200 rounded-2xl py-4 px-6 focus:ring-2 focus:ring-canina-blue/20 outline-none font-bold font-vazir"
/>
</div>
</div>
<button
onClick={handleNext}
disabled={!name || !breed}
className="w-full py-6 bg-medical-gray-900 text-white rounded-3xl font-black text-lg hover:bg-canina-blue transition-all shadow-xl shadow-medical-gray-900/10 flex items-center justify-center gap-3 font-vazir disabled:opacity-50"
>
ثبت هویت و ادامه
<ChevronLeft className="w-5 h-5" />
</button>
</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-8"
>
<div className="text-center">
<h3 className="text-2xl font-black text-medical-gray-900 mb-2 font-vazir uppercase italic tracking-tight">گام دوم: پایش فیزیکی</h3>
<p className="text-medical-gray-400 font-bold font-vazir">اطلاعات فیزیکی دقیق به دوزبندی صحیح مکملها کمک میکند</p>
</div>
<div className="grid grid-cols-2 gap-6">
<div className="space-y-4">
<label className="block text-center text-xs font-black text-medical-gray-400 uppercase tracking-widest font-vazir">سن (سال)</label>
<div className="flex items-center gap-4 bg-white rounded-3xl p-2 border border-medical-gray-200">
<button onClick={() => setAge(Math.max(0, age - 1))} className="w-10 h-10 flex items-center justify-center bg-medical-gray-50 rounded-xl text-medical-gray-900 hover:bg-canina-blue hover:text-white transition-all shadow-sm">-</button>
<span className="flex-1 text-center text-2xl font-black text-canina-blue font-vazir">{toPersian(age.toString())}</span>
<button onClick={() => setAge(age + 1)} className="w-10 h-10 flex items-center justify-center bg-medical-gray-50 rounded-xl text-medical-gray-900 hover:bg-canina-blue hover:text-white transition-all shadow-sm">+</button>
</div>
</div>
<div className="space-y-4">
<label className="block text-center text-xs font-black text-medical-gray-400 uppercase tracking-widest font-vazir">وزن (کیلوگرم)</label>
<div className="flex items-center gap-4 bg-white rounded-3xl p-2 border border-medical-gray-200">
<button onClick={() => setWeight(Math.max(1, weight - 1))} className="w-10 h-10 flex items-center justify-center bg-medical-gray-50 rounded-xl text-medical-gray-900 hover:bg-canina-blue hover:text-white transition-all shadow-sm">-</button>
<span className="flex-1 text-center text-2xl font-black text-canina-blue font-vazir">{toPersian(weight.toString())}</span>
<button onClick={() => setWeight(weight + 1)} className="w-10 h-10 flex items-center justify-center bg-medical-gray-50 rounded-xl text-medical-gray-900 hover:bg-canina-blue hover:text-white transition-all shadow-sm">+</button>
</div>
</div>
</div>
<div className="space-y-4">
<label className="block text-center text-xs font-black text-medical-gray-400 uppercase tracking-widest font-vazir">سطح فعالیت روزانه</label>
<div className="grid grid-cols-3 gap-3">
{["کم", "متوسط", "زیاد"].map(level => (
<button
key={level}
onClick={() => setActivityLevel(level as any)}
className={`py-4 rounded-2xl border-2 transition-all font-black italic ${activityLevel === level ? 'border-canina-blue bg-white text-canina-blue shadow-lg' : 'border-transparent bg-white/50 text-medical-gray-400 hover:bg-white'}`}
>
{level}
</button>
))}
</div>
</div>
<div className="flex gap-4 pt-4">
<button
onClick={handleBack}
className="flex-1 py-6 bg-medical-gray-100 text-medical-gray-500 rounded-3xl font-black text-lg hover:bg-medical-gray-200 transition-all flex items-center justify-center gap-3 font-vazir"
>
<ChevronRight className="w-5 h-5" />
قبلی
</button>
<button
onClick={handleNext}
className="flex-[2] py-6 bg-medical-gray-900 text-white rounded-3xl font-black text-lg hover:bg-canina-blue transition-all shadow-xl shadow-medical-gray-900/10 flex items-center justify-center gap-3 font-vazir"
>
گام نهایی سلامت
<ChevronLeft className="w-5 h-5" />
</button>
</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-8"
>
<div className="text-center">
<h3 className="text-2xl font-black text-medical-gray-900 mb-2 font-vazir uppercase italic tracking-tight">گام آخر: سوابق و حساسیتها</h3>
<p className="text-medical-gray-400 font-bold font-vazir">در صورت وجود هر یک از موارد زیر، آن را تیک بزنید</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{medicalOptions.map((opt) => (
<button
key={opt.id}
onClick={() => {
setMedicalConditions(prev =>
prev.includes(opt.condition) ? prev.filter(c => c !== opt.condition) : [...prev, opt.condition]
);
}}
className={`flex items-center gap-4 p-5 rounded-2xl border-2 transition-all text-right ${medicalConditions.includes(opt.condition) ? 'border-canina-blue bg-white shadow-md' : 'border-transparent bg-white/50 text-medical-gray-500 hover:bg-white'}`}
>
<div className={`w-8 h-8 rounded-lg flex items-center justify-center border ${medicalConditions.includes(opt.condition) ? 'bg-canina-blue border-canina-blue text-white' : 'border-medical-gray-200 text-transparent'}`}>
<ShieldCheck className="w-5 h-5" />
</div>
<span className={`text-sm font-black font-vazir ${medicalConditions.includes(opt.condition) ? 'text-canina-blue' : 'text-medical-gray-700'}`}>{opt.label}</span>
</button>
))}
</div>
<div className="flex gap-4 pt-4">
<button
onClick={handleBack}
className="flex-1 py-6 bg-medical-gray-100 text-medical-gray-500 rounded-3xl font-black text-lg hover:bg-medical-gray-200 transition-all flex items-center justify-center gap-3 font-vazir"
>
<ChevronRight className="w-5 h-5" />
قبلی
</button>
<button
onClick={handleSubmit}
className="flex-[2] py-6 bg-canina-blue text-white rounded-3xl font-black text-lg hover:bg-indigo-700 transition-all shadow-xl shadow-canina-blue/20 flex items-center justify-center gap-3 font-vazir"
>
تکمیل و صدور شناسنامه
<ChevronLeft className="w-5 h-5" />
</button>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
</div>
</section>
);
}