556 lines
33 KiB
TypeScript
556 lines
33 KiB
TypeScript
"use client";
|
||
import React, { useState, useEffect } from "react";
|
||
import Link from "next/link";
|
||
import { motion, AnimatePresence } from "motion/react";
|
||
import { useSettingsStore } from "../lib/store/settingsStore";
|
||
import {
|
||
Dog,
|
||
Cat,
|
||
ChevronLeft,
|
||
ChevronRight,
|
||
ShieldCheck,
|
||
Activity,
|
||
Pill,
|
||
Sparkle,
|
||
Check,
|
||
Download
|
||
} from "lucide-react";
|
||
import { cn } from "../lib/utils";
|
||
import { PRODUCTS } from "../lib/data/products";
|
||
import { productService } from "../lib/services/productService";
|
||
|
||
import { useRouter } from "next/navigation";
|
||
import { useUserStore } from "../lib/store/userStore";
|
||
import { useUIStore } from "../lib/store/uiStore";
|
||
import { usePetStore } from "../lib/store/usePetStore";
|
||
import api from "../lib/services/api";
|
||
|
||
interface SmartAdvisorProps {
|
||
onComplete?: (data: Record<string, unknown>) => void;
|
||
}
|
||
|
||
const CURRENT_SYMPTOMS = [
|
||
{ id: "joint_pain", label: "درد مفاصل و لنگیدن", condition: "درد مفاصل" },
|
||
{ id: "hair_loss", label: "ریزش مو، خارش و خشکی پوست", condition: "ریزش مو" },
|
||
{ id: "diarrhea", label: "اسهال، یبوست و مشکلات گوارشی", condition: "اسهال" },
|
||
{ id: "appetite", label: "بیاشتهایی و کاهش وزن", condition: "بیاشتهایی" },
|
||
{ id: "dental", label: "جرم دندان و بوی بد دهان", condition: "جرم دندان" },
|
||
{ id: "eye", label: "ترشحات یا مشکلات چشم", condition: "چشم" },
|
||
{ id: "puppy_growth", label: "رشد سریع و تعادل استخوانی", condition: "رشد سریع تولهسگ" },
|
||
{ id: "tendon", label: "سفتی عضلات و ضعف تاندون", condition: "تاندون" },
|
||
];
|
||
|
||
const MEDICAL_HISTORIES = [
|
||
{ id: "joint_surgery", label: "سابقه جراحی مفاصل/استخوان", condition: "جراحی مفاصل" },
|
||
{ id: "recent_birth", label: "زایمان اخیر / شیردهی فعال", condition: "زایمان اخیر" },
|
||
{ id: "pregnancy", label: "دوران بارداری", condition: "بارداری" },
|
||
{ id: "weakness", label: "ضعف بعد از بیماری / نقاهت", condition: "ضعف بعد از بیماری" },
|
||
{ id: "heart", label: "نارسایی یا حساسیت قلبی", condition: "نارسایی قلبی" },
|
||
{ id: "parasite", label: "سابقه آلودگی انگلی", condition: "آلودگی انگلی" },
|
||
];
|
||
|
||
export default function SmartAdvisor({ onComplete, rules = [] }: SmartAdvisorProps & { rules?: unknown[] }) {
|
||
const router = useRouter();
|
||
const { isLoggedIn } = useUserStore();
|
||
const setLoginModalOpen = useUIStore(state => state.setLoginModalOpen);
|
||
const { addPet, setActivePet } = usePetStore();
|
||
const getText = useSettingsStore(state => state.getText);
|
||
|
||
const [advisorRules, setAdvisorRules] = useState<unknown[]>(rules);
|
||
|
||
useEffect(() => {
|
||
if (rules && rules.length > 0) {
|
||
Promise.resolve().then(() => setAdvisorRules(rules));
|
||
} else {
|
||
api.get('/smart-advisor/rules')
|
||
.then(res => {
|
||
if (Array.isArray(res.data)) {
|
||
setAdvisorRules(res.data);
|
||
}
|
||
})
|
||
.catch(err => console.error("[SmartAdvisor] Failed to fetch rules:", err));
|
||
}
|
||
}, [rules]);
|
||
|
||
const [step, setStep] = useState(1);
|
||
|
||
// Step 1 Data
|
||
const [name, setName] = useState("");
|
||
const [type, setType] = useState<"سگ" | "گربه">("سگ");
|
||
const [breed, setBreed] = useState("");
|
||
const [showError, setShowError] = useState(false);
|
||
const [shake, setShake] = useState(false);
|
||
|
||
// Step 2 Data
|
||
const [age, setAge] = useState<number>(3);
|
||
const [weight, setWeight] = useState<number>(10);
|
||
const [activityLevel, setActivityLevel] = useState<"کم" | "متوسط" | "زیاد">("متوسط");
|
||
|
||
// Step 3 Data (Symptoms) & Step 4 Data (History)
|
||
const [currentSymptoms, setCurrentSymptoms] = useState<string[]>([]);
|
||
const [medicalConditions, setMedicalConditions] = useState<string[]>([]);
|
||
|
||
const handleNext = () => {
|
||
if (step === 1) {
|
||
if (!name.trim() || !breed.trim()) {
|
||
setShowError(true);
|
||
setShake(true);
|
||
setTimeout(() => setShake(false), 500);
|
||
return;
|
||
}
|
||
}
|
||
setStep(s => s + 1);
|
||
};
|
||
const handleBack = () => setStep(s => s - 1);
|
||
|
||
const calculateRecommendation = async () => {
|
||
const combinedConditions = Array.from(new Set([...currentSymptoms, ...medicalConditions]));
|
||
|
||
let matchedProd: Record<string, unknown> | null = null;
|
||
|
||
// First, check fetched advisor rules
|
||
if (advisorRules && advisorRules.length > 0) {
|
||
const foundRule = advisorRules.find((r) => {
|
||
const ruleObj = r as Record<string, unknown>;
|
||
const targetPetType = ruleObj.targetPetType as string | undefined;
|
||
const condition = ruleObj.condition as string | undefined;
|
||
const petMatch = !targetPetType || targetPetType === "هر دو" || targetPetType === type;
|
||
const conditionMatch = combinedConditions.some(c => condition?.includes(c) || c.includes(condition || ''));
|
||
return petMatch && conditionMatch;
|
||
}) as Record<string, unknown> | undefined;
|
||
if (foundRule && foundRule.product) {
|
||
matchedProd = foundRule.product as Record<string, unknown>;
|
||
}
|
||
}
|
||
|
||
// Second, query dynamic products API
|
||
if (!matchedProd) {
|
||
try {
|
||
const liveRes = await productService.getProducts({ petType: type, limit: 50 });
|
||
if (liveRes.data && liveRes.data.length > 0) {
|
||
const found = liveRes.data.find(p => {
|
||
const matchPet = p.suitableFor === "هر دو" || p.suitableFor === type;
|
||
const matchSymptom = p.symptoms?.some((s: string) => combinedConditions.some(c => s.includes(c) || c.includes(s)));
|
||
return matchPet && matchSymptom;
|
||
});
|
||
matchedProd = (found || liveRes.data[0]) as unknown as Record<string, unknown>;
|
||
}
|
||
} catch (e) {
|
||
console.error("[SmartAdvisor] Dynamic product fetch failed, using fallback", e);
|
||
}
|
||
}
|
||
|
||
if (!matchedProd) {
|
||
const fallback = PRODUCTS.find(p => p.suitableFor === "هر دو" || p.suitableFor === type) || PRODUCTS[0];
|
||
matchedProd = fallback as unknown as Record<string, unknown>;
|
||
}
|
||
};
|
||
|
||
const handleSubmit = () => {
|
||
const combinedConditions = Array.from(new Set([...currentSymptoms, ...medicalConditions]));
|
||
const data = {
|
||
name, type, breed, age, weight, activityLevel, medicalConditions: combinedConditions
|
||
};
|
||
|
||
calculateRecommendation();
|
||
|
||
if (onComplete) {
|
||
onComplete(data);
|
||
} else {
|
||
if (!isLoggedIn) {
|
||
setLoginModalOpen(true, data);
|
||
} else {
|
||
const newPet = { ...data, id: Date.now().toString(), reminders: [], logs: [], consumptions: [] };
|
||
addPet(newPet);
|
||
setActivePet(newPet.id);
|
||
router.push('/profile');
|
||
}
|
||
}
|
||
};
|
||
|
||
return (
|
||
<section id="canino-advisor" className="py-16 bg-gradient-to-b from-white via-medical-gray-50/50 to-white">
|
||
<div className="max-w-5xl mx-auto px-4">
|
||
{/* Header Title */}
|
||
<div className="text-center mb-10">
|
||
<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-4 border border-canina-blue/10"
|
||
>
|
||
<Sparkle className="w-4 h-4 text-canina-blue animate-spin" />
|
||
<span suppressHydrationWarning className="text-canina-blue text-xs font-black uppercase tracking-widest font-vazir">{getText('advisor_badge', "سیستم پایش و تجویز هوشمند")}</span>
|
||
</motion.div>
|
||
<h2 suppressHydrationWarning className="text-4xl lg:text-5xl font-black text-medical-gray-900 mb-4 font-lalezar tracking-wide">{getText('advisor_title', "دستیار سلامت و الگوریتم پایش کنینا")}</h2>
|
||
<p suppressHydrationWarning className="text-medical-gray-600 font-medium text-base font-shabnam max-w-2xl mx-auto leading-relaxed">
|
||
{getText('advisor_subtitle', "با ثبت اطلاعات همدم خود در ۴ قدم ساده، از الگوریتم علمی کنینا برای تجویز دقیق مکمل و پایش سلامت بهرهمند شوید.")}
|
||
</p>
|
||
|
||
<div className="mt-4 flex justify-center">
|
||
<Link
|
||
href="/catalog"
|
||
className="inline-flex items-center gap-2 px-4 py-2 bg-medical-gray-50 border border-medical-gray-200 rounded-xl text-medical-gray-600 text-xs font-bold hover:bg-canina-blue hover:text-white transition-all shadow-sm"
|
||
>
|
||
<Download className="w-4 h-4" />
|
||
<span>مشاهده کاتالوگ و جدول دوز آلمان</span>
|
||
</Link>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Value Proposition Cards (Explaining Why & How) */}
|
||
<motion.div
|
||
initial={{ opacity: 0, y: 20 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
className="grid grid-cols-1 md:grid-cols-3 gap-4 sm:gap-6 mb-8 sm:mb-12"
|
||
>
|
||
<div className="bg-white p-4 sm:p-6 rounded-2xl sm:rounded-[2.5rem] border border-medical-gray-200/80 shadow-md sm:shadow-lg hover:shadow-xl transition-all group flex flex-col justify-between">
|
||
<div>
|
||
<div className="flex items-center gap-3 sm:block mb-2 sm:mb-0">
|
||
<div className="w-9 h-9 sm:w-12 sm:h-12 bg-canina-blue/10 rounded-xl sm:rounded-2xl flex items-center justify-center text-canina-blue sm:mb-4 group-hover:bg-canina-blue group-hover:text-white transition-all shadow-sm flex-shrink-0">
|
||
<Pill className="w-5 h-5 sm:w-6 sm:h-6" />
|
||
</div>
|
||
<h3 className="text-base sm:text-lg font-black text-medical-gray-900 sm:mb-2 font-vazir">چرا ثبت شناسنامه؟</h3>
|
||
</div>
|
||
<p className="text-xs text-medical-gray-500 font-medium leading-relaxed font-vazir">
|
||
مکملهای دارویی کنینا غلیظ و تخصصی هستند؛ الگوریتم هوشمند ما بر اساس سن، وزن و جثه پت شما، دوز دقیق مصرفی روزانه را محاسبه میکند.
|
||
</p>
|
||
</div>
|
||
<div className="mt-3 sm:mt-4 pt-3 sm:pt-4 border-t border-medical-gray-100 text-[10px] font-black text-canina-blue flex items-center gap-1 font-vazir">
|
||
<Check className="w-3.5 h-3.5" />
|
||
تضمین ایمنی و عدم اوردوز
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-white p-4 sm:p-6 rounded-2xl sm:rounded-[2.5rem] border border-medical-gray-200/80 shadow-md sm:shadow-lg hover:shadow-xl transition-all group flex flex-col justify-between">
|
||
<div>
|
||
<div className="flex items-center gap-3 sm:block mb-2 sm:mb-0">
|
||
<div className="w-9 h-9 sm:w-12 sm:h-12 bg-canina-blue/10 rounded-xl sm:rounded-2xl flex items-center justify-center text-canina-blue sm:mb-4 group-hover:bg-canina-blue group-hover:text-white transition-all shadow-sm flex-shrink-0">
|
||
<Activity className="w-5 h-5 sm:w-6 sm:h-6" />
|
||
</div>
|
||
<h3 className="text-base sm:text-lg font-black text-medical-gray-900 sm:mb-2 font-vazir">تطبیق علائم بالینی</h3>
|
||
</div>
|
||
<p className="text-xs text-medical-gray-500 font-medium leading-relaxed font-vazir">
|
||
با انتخاب علائمی مثل لنگیدن یا ریزش مو، دقیقترین راهکار مکمل آلمانی به همراه دلایل علمی به شما پیشنهاد میشود.
|
||
</p>
|
||
</div>
|
||
<div className="mt-3 sm:mt-4 pt-3 sm:pt-4 border-t border-medical-gray-100 text-[10px] font-black text-canina-blue flex items-center gap-1 font-vazir">
|
||
<Check className="w-3.5 h-3.5" />
|
||
پیشنهاد هدفمند و تخصصی
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-white p-4 sm:p-6 rounded-2xl sm:rounded-[2.5rem] border border-medical-gray-200/80 shadow-md sm:shadow-lg hover:shadow-xl transition-all group flex flex-col justify-between">
|
||
<div>
|
||
<div className="flex items-center gap-3 sm:block mb-2 sm:mb-0">
|
||
<div className="w-9 h-9 sm:w-12 sm:h-12 bg-canina-blue/10 rounded-xl sm:rounded-2xl flex items-center justify-center text-canina-blue sm:mb-4 group-hover:bg-canina-blue group-hover:text-white transition-all shadow-sm flex-shrink-0">
|
||
<ShieldCheck className="w-5 h-5 sm:w-6 sm:h-6" />
|
||
</div>
|
||
<h3 className="text-base sm:text-lg font-black text-medical-gray-900 sm:mb-2 font-vazir">پایش موجودی و یادآور</h3>
|
||
</div>
|
||
<p className="text-xs text-medical-gray-500 font-medium leading-relaxed font-vazir">
|
||
زمانهای مصرف مکملها تنظیم شده و سیستم قبل از اتمام دوره درمانی، هشدار شارژ مجدد ارسال خواهد کرد.
|
||
</p>
|
||
</div>
|
||
<div className="mt-3 sm:mt-4 pt-3 sm:pt-4 border-t border-medical-gray-100 text-[10px] font-black text-canina-blue flex items-center gap-1 font-vazir">
|
||
<Check className="w-3.5 h-3.5" />
|
||
تکمیل منظم دوره درمان
|
||
</div>
|
||
</div>
|
||
</motion.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 / 4) * 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">{getText('advisor_step1_title', "گام اول: هویت بصری")}</h3>
|
||
<p className="text-medical-gray-600 font-bold font-vazir">{getText('advisor_step1_desc', "همدم شما رو با چه اسمی صدا میزنید؟")}</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 focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none cursor-pointer ${type === "سگ" ? 'border-canina-blue bg-white shadow-xl shadow-canina-blue/10 scale-105 text-canina-blue' : 'border-medical-gray-250 bg-white text-medical-gray-500 hover:border-canina-blue/30 hover:text-canina-blue/70'}`}
|
||
>
|
||
<Dog className="w-12 h-12" />
|
||
<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 focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none cursor-pointer ${type === "گربه" ? 'border-canina-blue bg-white shadow-xl shadow-canina-blue/10 scale-105 text-canina-blue' : 'border-medical-gray-250 bg-white text-medical-gray-500 hover:border-canina-blue/30 hover:text-canina-blue/70'}`}
|
||
>
|
||
<Cat className="w-12 h-12" />
|
||
<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-600 pr-2">{getText('advisor_pet_name_label', "نام پت")}</label>
|
||
<input
|
||
type="text"
|
||
value={name}
|
||
onChange={e => {
|
||
setName(e.target.value);
|
||
if (e.target.value.trim() && breed.trim()) setShowError(false);
|
||
}}
|
||
placeholder={getText('advisor_pet_name_placeholder', "لوسی، تدی...")}
|
||
className={cn(
|
||
"w-full bg-white border rounded-2xl py-4 px-6 focus:ring-2 outline-none font-black text-lg font-vazir placeholder-medical-gray-500 text-medical-gray-900 transition-all",
|
||
showError && !name.trim()
|
||
? "border-red-500 focus:ring-red-500/30"
|
||
: "border-medical-gray-300 focus:ring-canina-blue/30 focus-visible:ring-4 focus-visible:ring-canina-blue/40"
|
||
)}
|
||
/>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<label className="text-xs font-black text-medical-gray-600 pr-2">{getText('advisor_pet_breed_label', "نژاد")}</label>
|
||
<input
|
||
type="text"
|
||
value={breed}
|
||
onChange={e => {
|
||
setBreed(e.target.value);
|
||
if (name.trim() && e.target.value.trim()) setShowError(false);
|
||
}}
|
||
placeholder={getText('advisor_pet_breed_placeholder', "ژرمن، پرشین...")}
|
||
className={cn(
|
||
"w-full bg-white border rounded-2xl py-4 px-6 focus:ring-2 outline-none font-bold font-vazir placeholder-medical-gray-500 text-medical-gray-900 transition-all",
|
||
showError && !breed.trim()
|
||
? "border-red-500 focus:ring-red-500/30"
|
||
: "border-medical-gray-300 focus:ring-canina-blue/30 focus-visible:ring-4 focus-visible:ring-canina-blue/40"
|
||
)}
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<motion.button
|
||
onClick={handleNext}
|
||
animate={shake ? { x: [-10, 10, -10, 10, -5, 5, -2, 2, 0] } : { x: 0 }}
|
||
transition={{ duration: 0.4 }}
|
||
className={cn(
|
||
"w-full py-6 rounded-3xl font-black text-lg flex items-center justify-center gap-3 font-vazir min-h-[48px] transition-all focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none bg-medical-gray-900 text-white hover:bg-canina-blue shadow-xl shadow-medical-gray-900/10",
|
||
(!name.trim() || !breed.trim()) ? "opacity-60 cursor-pointer" : ""
|
||
)}
|
||
>
|
||
{getText('advisor_submit_btn', "ثبت هویت و ادامه")}
|
||
<ChevronLeft className="w-5 h-5" />
|
||
</motion.button>
|
||
{showError && (!name.trim() || !breed.trim()) && (
|
||
<p className="text-center text-xs font-black text-red-500 mt-2 font-vazir animate-pulse">
|
||
{getText('advisor_validation_warning', "⚠️ لطفاً ابتدا نام و نژاد پت را در بالا وارد کنید.")}
|
||
</p>
|
||
)}
|
||
</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">{getText('advisor_step2_title', "گام دوم: پایش فیزیکی")}</h3>
|
||
<p className="text-medical-gray-600 font-bold font-vazir">{getText('advisor_step2_desc', "اطلاعات فیزیکی دقیق به دوزبندی صحیح مکملها کمک میکند")}</p>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-2 gap-6">
|
||
<div className="space-y-4">
|
||
<label className="block text-center text-sm font-black text-medical-gray-800 font-vazir">{getText('advisor_age_label', "سن حیوان (سال)")}</label>
|
||
<div className="flex items-center gap-2 bg-white rounded-3xl p-2 border-2 border-medical-gray-200">
|
||
<button type="button" onClick={() => setAge(Math.max(0, age - 1))} className="w-10 h-10 shrink-0 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 font-black text-lg">-</button>
|
||
<div className="flex-1 flex items-center justify-center gap-1">
|
||
<input
|
||
type="number"
|
||
min="0"
|
||
max="30"
|
||
value={age === 0 ? '' : age}
|
||
onChange={(e) => setAge(Math.max(0, parseInt(e.target.value) || 0))}
|
||
className="w-12 text-center text-xl font-black text-canina-blue font-vazir outline-none bg-transparent"
|
||
/>
|
||
<span className="text-xs font-bold text-medical-gray-400">سال</span>
|
||
</div>
|
||
<button type="button" onClick={() => setAge(age + 1)} className="w-10 h-10 shrink-0 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 font-black text-lg">+</button>
|
||
</div>
|
||
</div>
|
||
<div className="space-y-4">
|
||
<label className="block text-center text-sm font-black text-medical-gray-800 font-vazir">{getText('advisor_weight_label', "وزن حیوان (کیلوگرم)")}</label>
|
||
<div className="flex items-center gap-2 bg-white rounded-3xl p-2 border-2 border-medical-gray-200">
|
||
<button type="button" onClick={() => setWeight(Math.max(1, weight - 1))} className="w-10 h-10 shrink-0 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 font-black text-lg">-</button>
|
||
<div className="flex-1 flex items-center justify-center gap-1">
|
||
<input
|
||
type="number"
|
||
min="1"
|
||
max="120"
|
||
value={weight === 0 ? '' : weight}
|
||
onChange={(e) => setWeight(Math.max(1, parseInt(e.target.value) || 1))}
|
||
className="w-16 text-center text-xl font-black text-canina-blue font-vazir outline-none bg-transparent"
|
||
/>
|
||
<span className="text-xs font-bold text-medical-gray-400">kg</span>
|
||
</div>
|
||
<button type="button" onClick={() => setWeight(weight + 1)} className="w-10 h-10 shrink-0 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 font-black text-lg">+</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="space-y-4">
|
||
<label className="block text-center text-xs font-black text-medical-gray-600 uppercase tracking-widest font-vazir">{getText('advisor_activity_label', "سطح فعالیت روزانه")}</label>
|
||
<div className="grid grid-cols-3 gap-3">
|
||
{["کم", "متوسط", "زیاد"].map(level => (
|
||
<button
|
||
key={level}
|
||
onClick={() => setActivityLevel(level as "کم" | "متوسط" | "زیاد")}
|
||
className={`py-4 rounded-2xl border-2 transition-all font-black italic focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none min-h-[48px] ${activityLevel === level ? 'border-canina-blue bg-white text-canina-blue shadow-lg' : 'border-transparent bg-white/50 text-medical-gray-500 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-600 rounded-3xl font-black text-lg hover:bg-medical-gray-200 focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none transition-all flex items-center justify-center gap-3 font-vazir min-h-[48px]"
|
||
>
|
||
<ChevronRight className="w-5 h-5" />
|
||
{getText('advisor_back', "قبلی")}
|
||
</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 focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none transition-all shadow-xl shadow-medical-gray-900/10 flex items-center justify-center gap-3 font-vazir min-h-[48px]"
|
||
>
|
||
{getText('advisor_next_symptoms', "بررسی علائم بالینی فعلی")}
|
||
<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">{getText('advisor_step3_symptoms_title', "گام سوم: علائم بالینی فعلی")}</h3>
|
||
<p className="text-medical-gray-600 font-bold font-vazir">{getText('advisor_step3_symptoms_desc', "آیا همدم شما در حال حاضر هیچکدام از علائم زیر را تجربه میکند؟")}</p>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
{CURRENT_SYMPTOMS.map((opt) => (
|
||
<button
|
||
key={opt.id}
|
||
onClick={() => {
|
||
setCurrentSymptoms(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 focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none min-h-[48px] ${currentSymptoms.includes(opt.condition) ? 'border-canina-blue bg-white shadow-md' : 'border-transparent bg-white/50 text-medical-gray-600 hover:bg-white'}`}
|
||
>
|
||
<div className={`w-8 h-8 rounded-lg flex items-center justify-center border ${currentSymptoms.includes(opt.condition) ? 'bg-canina-blue border-canina-blue text-white' : 'border-medical-gray-300 text-transparent'}`}>
|
||
<ShieldCheck className="w-5 h-5" />
|
||
</div>
|
||
<span className={`text-sm font-black font-vazir ${currentSymptoms.includes(opt.condition) ? 'text-canina-blue' : 'text-medical-gray-900'}`}>{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-600 rounded-3xl font-black text-lg hover:bg-medical-gray-200 focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none transition-all flex items-center justify-center gap-3 font-vazir min-h-[48px]"
|
||
>
|
||
<ChevronRight className="w-5 h-5" />
|
||
{getText('advisor_back', "قبلی")}
|
||
</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 focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none transition-all shadow-xl shadow-medical-gray-900/10 flex items-center justify-center gap-3 font-vazir min-h-[48px]"
|
||
>
|
||
{getText('advisor_next_history', "سوابق پزشکی و جراحی")}
|
||
<ChevronLeft className="w-5 h-5" />
|
||
</button>
|
||
</div>
|
||
</motion.div>
|
||
)}
|
||
|
||
{step === 4 && (
|
||
<motion.div
|
||
key="step4"
|
||
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">{getText('advisor_step4_history_title', "گام چهارم: سوابق پزشکی و جراحی")}</h3>
|
||
<p className="text-medical-gray-600 font-bold font-vazir">{getText('advisor_step4_history_desc', "در صورت وجود سابقه جراحی، بارداری یا نارسایی، آن را مشخص کنید")}</p>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
{MEDICAL_HISTORIES.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 focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none min-h-[48px] ${medicalConditions.includes(opt.condition) ? 'border-canina-blue bg-white shadow-md' : 'border-transparent bg-white/50 text-medical-gray-600 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-300 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-900'}`}>{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-600 rounded-3xl font-black text-lg hover:bg-medical-gray-200 focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none transition-all flex items-center justify-center gap-3 font-vazir min-h-[48px]"
|
||
>
|
||
<ChevronRight className="w-5 h-5" />
|
||
{getText('advisor_back', "قبلی")}
|
||
</button>
|
||
<button
|
||
onClick={handleSubmit}
|
||
className="flex-[2] py-6 bg-canina-blue text-white rounded-3xl font-black text-lg hover:bg-canina-blue/90 focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none transition-all shadow-xl shadow-canina-blue/30 flex items-center justify-center gap-3 font-vazir min-h-[48px]"
|
||
>
|
||
{getText('advisor_complete', "تکمیل و صدور شناسنامه")}
|
||
<ChevronLeft className="w-5 h-5" />
|
||
</button>
|
||
</div>
|
||
</motion.div>
|
||
)}
|
||
</AnimatePresence>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|