Compare commits

..

No commits in common. "20a1b225c88f03ef473ebbcaecc699796e6fa23d" and "27ef5979aab276cbca4718587f1dd9cba6c6b774" have entirely different histories.

5 changed files with 230 additions and 564 deletions

View File

@ -9,155 +9,151 @@ import Link from 'next/link';
import { toPersian } from "../lib/utils";
import { useSettingsStore } from "../lib/store/settingsStore";
import api from "../lib/services/api";
import Image from "next/image";
export default function HomeClient() {
const getText = useSettingsStore(state => state.getText);
const [data, setData] = useState<any>(null);
const getText = useSettingsStore(state => state.getText);
const [data, setData] = useState<any>(null);
useEffect(() => {
api.get('/home')
.then(res => setData(res.data))
.catch(err => console.error('Home data fetch error:', err));
}, []);
useEffect(() => {
api.get('/home')
.then(res => setData(res.data))
.catch(err => console.error('Home data fetch error:', err));
}, []);
const banners = data?.heroBanners || [];
const testimonials = data?.vetTestimonials || [];
const rules = data?.smartAdvisorRules || [];
const banners = data?.heroBanners || [];
const testimonials = data?.vetTestimonials || [];
const rules = data?.smartAdvisorRules || [];
return (
<>
<Hero banners={banners} />
<SmartAdvisor rules={rules} />
<FeaturedProducts />
<VetGallery testimonials={testimonials} />
return (
<>
<Hero banners={banners} />
<SmartAdvisor rules={rules} />
<FeaturedProducts />
<VetGallery testimonials={testimonials} />
{/* Navigation to Sections */}
<section className="py-12 bg-medical-gray-50 border-y border-medical-gray-100 font-sans">
<div className="max-w-7xl mx-auto px-4 grid md:grid-cols-2 gap-8">
{/* Specialty Shop Card */}
<Link
href="/shop"
className="bg-gradient-to-br from-blue-50/60 to-canina-blue/5 p-12 rounded-[2.5rem] border border-blue-100/60 shadow-md hover:shadow-2xl hover:-translate-y-2 transition-all cursor-pointer group block relative overflow-hidden"
>
<div className="absolute -top-12 -left-12 w-32 h-32 bg-canina-blue/5 rounded-full blur-2xl group-hover:scale-150 transition-transform duration-700" />
<h3 className="text-3xl font-black text-medical-gray-900 mb-4 group-hover:text-canina-blue transition-colors font-lalezar tracking-wide">
{getText('nav_card_shop_title', "فروشگاه تخصصی")}
</h3>
<p className="text-medical-gray-600 mb-8 leading-relaxed font-shabnam">
{getText('nav_card_shop_desc', "دسترسی به تمامی محصولات بر اساس علائم بالینی و نیازهای پت شما.")}
</p>
<div className="flex items-center gap-2 text-canina-blue font-black tracking-widest uppercase text-xs font-vazir">
{getText('nav_card_shop_cta', "کاتالوگ کامل محصولات")}
<span className="text-lg group-hover:-translate-x-1 transition-transform">←</span>
</div>
</Link>
{/* Navigation to Sections */}
<section className="py-12 bg-medical-gray-50 border-y border-medical-gray-100 font-sans">
<div className="max-w-7xl mx-auto px-4 grid md:grid-cols-2 gap-8">
{/* Specialty Shop Card */}
<Link
href="/shop"
className="bg-gradient-to-br from-blue-50/60 to-canina-blue/5 p-12 rounded-[2.5rem] border border-blue-100/60 shadow-md hover:shadow-2xl hover:-translate-y-2 transition-all cursor-pointer group block relative overflow-hidden"
>
<div className="absolute -top-12 -left-12 w-32 h-32 bg-canina-blue/5 rounded-full blur-2xl group-hover:scale-150 transition-transform duration-700" />
<h3 className="text-3xl font-black text-medical-gray-900 mb-4 group-hover:text-canina-blue transition-colors font-lalezar tracking-wide">
{getText('nav_card_shop_title', "فروشگاه تخصصی")}
</h3>
<p className="text-medical-gray-600 mb-8 leading-relaxed font-shabnam">
{getText('nav_card_shop_desc', "دسترسی به تمامی محصولات بر اساس علائم بالینی و نیازهای پت شما.")}
</p>
<div className="flex items-center gap-2 text-canina-blue font-black tracking-widest uppercase text-xs font-vazir">
{getText('nav_card_shop_cta', "کاتالوگ کامل محصولات")}
<span className="text-lg group-hover:-translate-x-1 transition-transform">←</span>
</div>
</Link>
{/* Scientific Wiki Card */}
<Link
href="/wiki"
className="bg-gradient-to-br from-emerald-50/60 to-green-50/20 p-12 rounded-[2.5rem] border border-emerald-100/60 shadow-md hover:shadow-2xl hover:-translate-y-2 transition-all cursor-pointer group block relative overflow-hidden"
>
<div className="absolute -top-12 -left-12 w-32 h-32 bg-emerald-500/5 rounded-full blur-2xl group-hover:scale-150 transition-transform duration-700" />
<h3 className="text-3xl font-black text-medical-gray-900 mb-4 group-hover:text-emerald-700 transition-colors font-lalezar tracking-wide">
{getText('nav_card_wiki_title', "دانشنامه علمی")}
</h3>
<p className="text-medical-gray-600 mb-8 leading-relaxed font-shabnam">
{getText('nav_card_wiki_desc', "آشنایی با ترکیبات نایاب و استانداردهای دارویی محصولات آلمانی کانینا.")}
</p>
<div className="flex items-center gap-2 text-emerald-700 font-black tracking-widest uppercase text-xs font-vazir">
{getText('nav_card_wiki_cta', "مطالعه مقالات تخصصی")}
<span className="text-lg group-hover:-translate-x-1 transition-transform">←</span>
</div>
</Link>
</div>
</section>
{/* Scientific Wiki Card */}
<Link
href="/wiki"
className="bg-gradient-to-br from-emerald-50/60 to-green-50/20 p-12 rounded-[2.5rem] border border-emerald-100/60 shadow-md hover:shadow-2xl hover:-translate-y-2 transition-all cursor-pointer group block relative overflow-hidden"
>
<div className="absolute -top-12 -left-12 w-32 h-32 bg-emerald-500/5 rounded-full blur-2xl group-hover:scale-150 transition-transform duration-700" />
<h3 className="text-3xl font-black text-medical-gray-900 mb-4 group-hover:text-emerald-700 transition-colors font-lalezar tracking-wide">
{getText('nav_card_wiki_title', "دانشنامه علمی")}
</h3>
<p className="text-medical-gray-600 mb-8 leading-relaxed font-shabnam">
{getText('nav_card_wiki_desc', "آشنایی با ترکیبات نایاب و استانداردهای دارویی محصولات آلمانی کانینا.")}
</p>
<div className="flex items-center gap-2 text-emerald-700 font-black tracking-widest uppercase text-xs font-vazir">
{getText('nav_card_wiki_cta', "مطالعه مقالات تخصصی")}
<span className="text-lg group-hover:-translate-x-1 transition-transform">←</span>
</div>
</Link>
</div>
</section>
{/* About Section Teaser */}
<section className="py-16 bg-white overflow-hidden font-sans">
<div className="max-w-7xl mx-auto px-4 grid lg:grid-cols-2 gap-16 lg:gap-24 items-center">
<div className="relative overflow-hidden rounded-[3rem]">
<div className="absolute -top-10 -right-10 w-40 h-40 bg-canina-blue/5 rounded-full blur-3xl" />
<img
src="/assets/images/regenerated_image_1779109861747.png"
alt="آزمایشگاه تخصصی تولید مکمل‌های دارویی کانینا آلمان"
className="rounded-[3rem] shadow-2xl relative z-10 grayscale-[10%] w-full"
/>
<div className="absolute bottom-4 left-4 sm:bottom-6 sm:left-6 bg-canina-blue text-white p-4 sm:p-6 md:p-8 rounded-[1.5rem] sm:rounded-[2rem] shadow-2xl z-20 max-w-[80%] border border-white/10">
<div className="text-2xl sm:text-4xl font-black mb-1 font-lalezar tracking-wide">{getText('quality', "کیفیت")}</div>
<div className="text-[10px] sm:text-xs uppercase tracking-widest font-bold opacity-90 font-vazir">{getText('ultra_pharmaceutical', "استانداردهای فوق‌دارویی")}</div>
</div>
</div>
<div>
<div className="text-canina-blue text-xs font-bold uppercase tracking-[0.2em] mb-4 font-vazir">
{getText('about_label', "میراث ما از آلمان")}
</div>
<h2 className="text-3xl lg:text-5xl font-black text-medical-gray-900 leading-[1.2] mb-12 font-lalezar tracking-wide">
{getText('about_title', "چرا برند آلمانی Canina مرجع دامپزشکان است؟")}
</h2>
<div className="space-y-6">
{[
{ title: getText('about_item1_title', "مواد اولیه نایاب"), desc: getText('about_item1_desc', "استفاده از پودر صدف لب‌سبز اصل نیوزیلند و مواد ارگانیک با گرید دارویی.") },
{ title: getText('about_item2_title', "فاقد مواد نگهدارنده"), desc: getText('about_item2_desc', "تمامی محصولات ۱۰۰٪ طبیعی و فاقد رنگ‌های مصنوعی و طعم‌دهنده‌های شیمیایی هستند.") },
{ title: getText('about_item3_title', "تاییدیه اروپا"), desc: getText('about_item3_desc', "مطابق با سخت‌گیرانه‌ترین استانداردهای ایمنی مواد غذایی و دارویی در اتحادیه اروپا.") }
].map((item, idx) => (
<div key={idx} className="flex gap-4 items-center">
<div className="w-12 h-12 rounded-2xl bg-medical-gray-50 flex items-center justify-center flex-shrink-0 text-canina-blue font-black font-lalezar text-xl shadow-sm">
{toPersian(idx + 1)}
</div>
<div>
<h4 className="text-lg font-bold text-medical-gray-900 mb-1 font-shabnam">{item.title}</h4>
<p className="text-sm text-medical-gray-500 leading-relaxed font-medium font-sans">{item.desc}</p>
{idx === 0 && (
<span className="text-[10px] text-slate-400 mt-1 block">
* تهیه شده با بالاترین استانداردهای دارویی انسان - کاملاً بی‌خطر در صورت لیسیده شدن توسط حیوان.
</span>
)}
{idx === 2 && (
<span className="text-[10px] text-slate-400 mt-1 block">
* تاییدیه رسمی ایمنی مواد غذایی و دارویی (HACCP & IFS) از اتحادیه اروپا.
</span>
)}
</div>
</div>
))}
</div>
</div>
</div>
</section>
{/* About Section Teaser */}
<section className="py-16 bg-white overflow-hidden font-sans">
<div className="max-w-7xl mx-auto px-4 grid lg:grid-cols-2 gap-16 lg:gap-24 items-center">
<div className="relative overflow-hidden rounded-[3rem]">
<div className="absolute -top-10 -right-10 w-40 h-40 bg-canina-blue/5 rounded-full blur-3xl" />
<Image
src="/assets/images/regenerated_image_1779109861747.png"
alt="آزمایشگاه تخصصی تولید مکمل‌های دارویی کانینا آلمان"
className="rounded-[3rem] shadow-2xl relative z-10 grayscale-[10%] w-full"
width={640}
height={425}
loading="lazy"
/>
<div className="absolute bottom-4 left-4 sm:bottom-6 sm:left-6 bg-canina-blue text-white p-4 sm:p-6 md:p-8 rounded-[1.5rem] sm:rounded-[2rem] shadow-2xl z-20 max-w-[80%] border border-white/10">
<div className="text-2xl sm:text-4xl font-black mb-1 font-lalezar tracking-wide">{getText('quality', "کیفیت")}</div>
<div className="text-[10px] sm:text-xs uppercase tracking-widest font-bold opacity-90 font-vazir">{getText('ultra_pharmaceutical', "استانداردهای فوق‌دارویی")}</div>
</div>
</div>
<div>
<div className="text-canina-blue text-xs font-bold uppercase tracking-[0.2em] mb-4 font-vazir">
{getText('about_label', "میراث ما از آلمان")}
</div>
<h2 className="text-3xl lg:text-5xl font-black text-medical-gray-900 leading-[1.2] mb-12 font-lalezar tracking-wide">
{getText('about_title', "چرا برند آلمانی Canina مرجع دامپزشکان است؟")}
</h2>
<div className="space-y-6">
{[
{ title: getText('about_item1_title', "مواد اولیه نایاب"), desc: getText('about_item1_desc', "استفاده از پودر صدف لب‌سبز اصل نیوزیلند و مواد ارگانیک با گرید دارویی.") },
{ title: getText('about_item2_title', "فاقد مواد نگهدارنده"), desc: getText('about_item2_desc', "تمامی محصولات ۱۰۰٪ طبیعی و فاقد رنگ‌های مصنوعی و طعم‌دهنده‌های شیمیایی هستند.") },
{ title: getText('about_item3_title', "تاییدیه اروپا"), desc: getText('about_item3_desc', "مطابق با سخت‌گیرانه‌ترین استانداردهای ایمنی مواد غذایی و دارویی در اتحادیه اروپا.") }
].map((item, idx) => (
<div key={idx} className="flex gap-4 items-center">
<div className="w-12 h-12 rounded-2xl bg-medical-gray-50 flex items-center justify-center flex-shrink-0 text-canina-blue font-black font-lalezar text-xl shadow-sm">
{toPersian(idx + 1)}
</div>
<div>
<h4 className="text-lg font-bold text-medical-gray-900 mb-1 font-shabnam">{item.title}</h4>
<p className="text-sm text-medical-gray-500 leading-relaxed font-medium font-sans">{item.desc}</p>
{idx === 0 && (
<span className="text-[10px] text-slate-400 mt-1 block">
* تهیه شده با بالاترین استانداردهای دارویی انسان - کاملاً بی‌خطر در صورت لیسیده شدن توسط حیوان.
</span>
)}
{idx === 2 && (
<span className="text-[10px] text-slate-400 mt-1 block">
* تاییدیه رسمی ایمنی مواد غذایی و دارویی (HACCP & IFS) از اتحادیه اروپا.
</span>
)}
</div>
</div>
))}
</div>
</div>
</div>
</section>
{/* Call to Action */}
<section className="bg-canina-blue py-12 relative overflow-hidden font-sans">
<div className="absolute inset-0 opacity-10">
<div className="grid grid-cols-10 h-full w-full">
{Array.from({ length: 100 }).map((_, i) => (
<div key={i} className="border border-white/20" />
))}
</div>
</div>
<div className="max-w-4xl mx-auto px-4 text-center relative z-10">
<h2 className="text-3xl lg:text-5xl font-black text-white mb-8 leading-tight font-lalezar tracking-wide">
{getText('cta_title', "می‌خواهید بدانید کدام محصول برای پت شما مناسب‌تر است؟")}
</h2>
<p className="text-white/80 text-lg mb-10 leading-relaxed font-shabnam">
{getText('cta_desc', "تیم متخصص دامپزشکی کانینا ایران آماده پاسخگویی به سوالات شماست.")}
</p>
<div className="flex flex-wrap justify-center gap-4">
<button className="bg-white text-canina-blue px-12 py-5 rounded-full font-black text-xl hover:scale-105 transition-transform shadow-2xl shadow-black/20 font-shabnam">
{getText('cta_btn_primary', "دریافت رژیم مکمل رایگان")}
</button>
<Link
href="/shop"
className="bg-transparent border-2 border-white text-white px-12 py-5 rounded-full font-black text-xl hover:bg-white hover:text-canina-blue transition-all font-shabnam"
>
{getText('cta_btn_secondary', "پروانه دامپزشکی و اصالت کالا")}
</Link>
</div>
</div>
</section>
</>
);
{/* Call to Action */}
<section className="bg-canina-blue py-12 relative overflow-hidden font-sans">
<div className="absolute inset-0 opacity-10">
<div className="grid grid-cols-10 h-full w-full">
{Array.from({length: 100}).map((_, i) => (
<div key={i} className="border border-white/20" />
))}
</div>
</div>
<div className="max-w-4xl mx-auto px-4 text-center relative z-10">
<h2 className="text-3xl lg:text-5xl font-black text-white mb-8 leading-tight font-lalezar tracking-wide">
{getText('cta_title', "می‌خواهید بدانید کدام محصول برای پت شما مناسب‌تر است؟")}
</h2>
<p className="text-white/80 text-lg mb-10 leading-relaxed font-shabnam">
{getText('cta_desc', "تیم متخصص دامپزشکی کانینا ایران آماده پاسخگویی به سوالات شماست.")}
</p>
<div className="flex flex-wrap justify-center gap-4">
<button className="bg-white text-canina-blue px-12 py-5 rounded-full font-black text-xl hover:scale-105 transition-transform shadow-2xl shadow-black/20 font-shabnam">
{getText('cta_btn_primary', "دریافت رژیم مکمل رایگان")}
</button>
<Link
href="/shop"
className="bg-transparent border-2 border-white text-white px-12 py-5 rounded-full font-black text-xl hover:bg-white hover:text-canina-blue transition-all font-shabnam"
>
{getText('cta_btn_secondary', "پروانه دامپزشکی و اصالت کالا")}
</Link>
</div>
</div>
</section>
</>
);
}

View File

@ -366,37 +366,35 @@ export default function ArchivePage({
<div className="max-w-7xl mx-auto">
{/* Standard Breadcrumb Navigation & Integrated Back Button */}
<div className="flex items-center justify-between mb-8 pb-4 border-b border-medical-gray-200/60 font-vazir gap-2">
{/* Back Button - always visible */}
<button
onClick={() => router.push('/')}
className="flex-shrink-0 px-3 py-1.5 bg-white border border-medical-gray-200 rounded-lg text-medical-gray-600 shadow-xs flex items-center gap-1.5 font-bold text-xs hover:border-canina-blue hover:text-canina-blue transition-all"
>
<ChevronRight className="w-3.5 h-3.5" />
<span>بازگشت</span>
</button>
{/* Breadcrumb - truncates on mobile */}
<div className="flex items-center gap-1.5 text-xs font-bold text-medical-gray-400 overflow-hidden min-w-0">
<span className="hidden sm:inline cursor-pointer hover:text-canina-blue transition-colors shrink-0" onClick={() => router.push('/')}>خانه</span>
<ChevronLeft className="hidden sm:block w-3 h-3 text-medical-gray-300 shrink-0" />
<span className="cursor-pointer hover:text-canina-blue transition-colors shrink-0" onClick={() => { setSelectedCategory("all"); }}>کاتالوگ</span>
<div className="flex items-center justify-between mb-8 pb-4 border-b border-medical-gray-200/60 font-vazir">
<div className="flex items-center gap-2 text-xs font-bold text-medical-gray-400 whitespace-nowrap">
<span className="cursor-pointer hover:text-canina-blue transition-colors" onClick={() => router.push('/')}>خانه</span>
<ChevronLeft className="w-3 h-3 text-medical-gray-300" />
<span className="cursor-pointer hover:text-canina-blue transition-colors" onClick={() => { setSelectedCategory("all"); }}>کاتالوگ</span>
{selectedCategory !== "all" && (
<>
<ChevronLeft className="w-3 h-3 text-medical-gray-300 shrink-0" />
<span className="text-canina-blue font-black truncate">{CATEGORY_LABELS[selectedCategory] || selectedCategory}</span>
<ChevronLeft className="w-3 h-3 text-medical-gray-300" />
<span className="text-canina-blue font-black">{CATEGORY_LABELS[selectedCategory] || selectedCategory}</span>
</>
)}
</div>
{/* Mobile Filter Button */}
<button
onClick={() => setIsMobileFilterOpen(!isMobileFilterOpen)}
className="lg:hidden flex-shrink-0 flex items-center gap-1.5 bg-canina-blue text-white px-3 py-1.5 rounded-lg text-xs font-bold shadow-xs"
>
<Filter className="w-3.5 h-3.5" />
<span>فیلترها {activeSymptoms.length > 0 || selectedPet !== "all" || selectedCategory !== "all" ? "•" : ""}</span>
</button>
<div className="flex items-center gap-2">
<button
onClick={() => setIsMobileFilterOpen(!isMobileFilterOpen)}
className="lg:hidden flex items-center gap-1.5 bg-canina-blue text-white px-3 py-1.5 rounded-lg text-xs font-bold shadow-xs"
>
<Filter className="w-3.5 h-3.5" />
<span>فیلترها {activeSymptoms.length > 0 || selectedPet !== "all" || selectedCategory !== "all" ? "•" : ""}</span>
</button>
<button
onClick={() => router.push('/')}
className="px-3.5 py-1.5 bg-white border border-medical-gray-200 rounded-lg text-medical-gray-600 shadow-xs flex items-center gap-1.5 font-bold text-xs hover:border-canina-blue hover:text-canina-blue transition-all whitespace-nowrap"
>
<ChevronRight className="w-3.5 h-3.5" />
<span>بازگشت</span>
</button>
</div>
</div>
<div className="flex flex-col lg:flex-row gap-8">

View File

@ -1,7 +1,7 @@
"use client";
import React, { useState, useEffect, useRef } from "react";
import { motion, AnimatePresence } from "motion/react";
import { X, User, Building2, Heart, ShieldCheck, Phone, Key, LogIn, ArrowRight, RefreshCw, Lock } from "lucide-react";
import { X, User, Building2, Heart, ShieldCheck, Phone, Key, LogIn, ArrowRight, RefreshCw } from "lucide-react";
import { useUserStore, UserRole } from "../lib/store/userStore";
import { authService } from "../lib/services/authService";
import { toast } from "sonner";
@ -14,10 +14,9 @@ interface AuthModalProps {
export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
const { setRole, setLoggedIn, fetchProfile } = useUserStore();
const [view, setView] = useState<"login" | "register" | "otp-phone" | "otp" | "forgot-password" | "forgot-otp" | "reset-password">("login");
const [view, setView] = useState<"login" | "register" | "otp-phone" | "otp">("login");
const [phoneNumber, setPhoneNumber] = useState("");
const [password, setPassword] = useState("");
const [newPassword, setNewPassword] = useState("");
const [email, setEmail] = useState("");
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
@ -41,7 +40,6 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
setView("login");
setPhoneNumber("");
setPassword("");
setNewPassword("");
setEmail("");
setFirstName("");
setLastName("");
@ -161,67 +159,6 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
}
};
const handleForgotSendOtp = async (e: React.FormEvent) => {
e.preventDefault();
const cleanPhone = phoneNumber.trim();
if (!/^09\d{9}$/.test(cleanPhone)) {
toast.error("شماره موبایل نامعتبر است");
return;
}
setIsLoading(true);
try {
const res = await authService.sendOtp(cleanPhone);
if ((res as any).code) {
toast.info(`کد تایید (تست): ${(res as any).code}`, { duration: 10000 });
} else {
toast.success("کد بازیابی رمز پیامک شد");
}
setView("forgot-otp");
setCountdown(120);
} catch (err: any) {
toast.error(err.message || "خطا در ارسال کد");
} finally {
setIsLoading(false);
}
};
const handleForgotVerifyOtp = async (e: React.FormEvent) => {
e.preventDefault();
if (otpCode.trim().length !== 5) {
toast.error("کد باید ۵ رقم باشد");
return;
}
setIsLoading(true);
try {
// Verify OTP - if success, allow password reset
await authService.verifyOtp(phoneNumber.trim(), otpCode.trim());
setView("reset-password");
} catch (err: any) {
toast.error(err.message || "کد تایید نامعتبر است");
} finally {
setIsLoading(false);
}
};
const handleResetPassword = async (e: React.FormEvent) => {
e.preventDefault();
if (newPassword.length < 6) {
toast.error("رمز جدید باید حداقل ۶ کاراکتر باشد");
return;
}
setIsLoading(true);
try {
// Use authService to update password after OTP verification
await authService.updateProfile({ password: newPassword } as any);
toast.success("رمز عبور با موفقیت تغییر یافت");
setView("login");
} catch (err: any) {
toast.error(err.message || "خطا در تغییر رمز");
} finally {
setIsLoading(false);
}
};
return (
<AnimatePresence>
{isOpen && (
@ -282,13 +219,10 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
className="space-y-4"
>
<div className="space-y-2">
<label htmlFor="login-phone" className="text-xs font-bold text-medical-gray-500">شماره موبایل</label>
<label className="text-xs font-bold text-medical-gray-500">شماره موبایل</label>
<input
id="login-phone"
name="username"
type="tel"
maxLength={11}
autoComplete="username"
placeholder="مثال: ۰۹۱۲۳۴۵۶۷۸۹"
value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value.replace(/[^0-9]/g, ''))}
@ -297,12 +231,9 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
/>
</div>
<div className="space-y-2">
<label htmlFor="login-password" className="text-xs font-bold text-medical-gray-500">رمز عبور</label>
<label className="text-xs font-bold text-medical-gray-500">رمز عبور</label>
<input
id="login-password"
name="password"
type="password"
autoComplete="current-password"
placeholder="رمز عبور خود را وارد کنید"
value={password}
onChange={(e) => setPassword(e.target.value)}
@ -311,7 +242,7 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
/>
</div>
<div className="flex items-center justify-between pt-2 flex-wrap gap-2">
<div className="flex items-center justify-between pt-2">
<button
type="button"
onClick={() => setView("otp-phone")}
@ -319,22 +250,13 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
>
ورود با کد یکبار مصرف (OTP)
</button>
<div className="flex items-center gap-3">
<button
type="button"
onClick={() => { setPhoneNumber(""); setView("forgot-password"); }}
className="text-xs font-bold text-medical-gray-400 hover:text-canina-blue hover:underline"
>
فراموشی رمز
</button>
<button
type="button"
onClick={() => setView("register")}
className="text-xs font-bold text-canina-blue hover:underline"
>
حساب ندارید؟ ثبت‌نام
</button>
</div>
<button
type="button"
onClick={() => setView("register")}
className="text-xs font-bold text-canina-blue hover:underline"
>
حساب ندارید؟ ثبت‌نام
</button>
</div>
<button
@ -358,12 +280,9 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
>
<div className="grid grid-cols-2 gap-3">
<div className="space-y-1">
<label htmlFor="reg-fname" className="text-xs font-bold text-medical-gray-500">نام</label>
<label className="text-xs font-bold text-medical-gray-500">نام</label>
<input
id="reg-fname"
name="given-name"
type="text"
autoComplete="given-name"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
disabled={isLoading}
@ -371,12 +290,9 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
/>
</div>
<div className="space-y-1">
<label htmlFor="reg-lname" className="text-xs font-bold text-medical-gray-500">نام خانوادگی</label>
<label className="text-xs font-bold text-medical-gray-500">نام خانوادگی</label>
<input
id="reg-lname"
name="family-name"
type="text"
autoComplete="family-name"
value={lastName}
onChange={(e) => setLastName(e.target.value)}
disabled={isLoading}
@ -386,13 +302,10 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
</div>
<div className="space-y-1">
<label htmlFor="reg-phone" className="text-xs font-bold text-medical-gray-500">شماره موبایل *</label>
<label className="text-xs font-bold text-medical-gray-500">شماره موبایل *</label>
<input
id="reg-phone"
name="username"
type="tel"
maxLength={11}
autoComplete="username"
value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value.replace(/[^0-9]/g, ''))}
disabled={isLoading}
@ -401,12 +314,9 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
</div>
<div className="space-y-1">
<label htmlFor="reg-email" className="text-xs font-bold text-medical-gray-500">ایمیل (اختیاری)</label>
<label className="text-xs font-bold text-medical-gray-500">ایمیل (اختیاری)</label>
<input
id="reg-email"
name="email"
type="email"
autoComplete="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
disabled={isLoading}
@ -415,12 +325,9 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
</div>
<div className="space-y-1">
<label htmlFor="reg-password" className="text-xs font-bold text-medical-gray-500">رمز عبور *</label>
<label className="text-xs font-bold text-medical-gray-500">رمز عبور *</label>
<input
id="reg-password"
name="new-password"
type="password"
autoComplete="new-password"
value={password}
onChange={(e) => setPassword(e.target.value)}
disabled={isLoading}
@ -570,119 +477,6 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
</button>
</motion.form>
)}
{/* Forgot Password - Enter Phone */}
{view === "forgot-password" && (
<motion.form
key="forgot-password-view"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
onSubmit={handleForgotSendOtp}
className="space-y-4"
>
<div className="bg-amber-50 border border-amber-200 rounded-2xl p-4 text-center">
<Lock className="w-8 h-8 text-amber-500 mx-auto mb-2" />
<p className="text-sm font-bold text-amber-700">برای بازیابی رمز عبور، کد تایید به شمارهتان ارسال می‌شود</p>
</div>
<div className="space-y-2">
<label htmlFor="forgot-phone" className="text-xs font-bold text-medical-gray-500">شماره موبایل</label>
<input
id="forgot-phone"
name="username"
type="tel"
maxLength={11}
autoComplete="username"
placeholder="مثال: ۰۹۱۲۳۴۵۶۷۸۹"
value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value.replace(/[^0-9]/g, ''))}
disabled={isLoading}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-3 px-4 focus:ring-2 focus:ring-canina-blue/20 outline-none font-bold text-left tracking-widest"
/>
</div>
<button
type="submit"
disabled={isLoading || phoneNumber.length < 11}
className="w-full py-4 bg-canina-blue text-white rounded-xl font-black hover:bg-indigo-700 disabled:opacity-50 transition-all shadow-lg shadow-canina-blue/20 flex items-center justify-center gap-2"
>
{isLoading ? <RefreshCw className="w-5 h-5 animate-spin" /> : <>ارسال کد بازیابی</>}
</button>
<button type="button" onClick={() => setView("login")} className="w-full text-xs font-bold text-medical-gray-400 hover:text-canina-blue">بازگشت به ورود</button>
</motion.form>
)}
{/* Forgot Password - Enter OTP */}
{view === "forgot-otp" && (
<motion.form
key="forgot-otp-view"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
onSubmit={handleForgotVerifyOtp}
className="space-y-5"
>
<p className="text-sm font-bold text-medical-gray-600 text-center">کد ارسال شده به {toPersian(phoneNumber)} را وارد کنید</p>
<div className="relative">
<input
ref={otpInputRef}
id="forgot-otp-code"
name="one-time-code"
type="text"
autoComplete="one-time-code"
inputMode="numeric"
maxLength={5}
value={otpCode}
onChange={(e) => setOtpCode(e.target.value.replace(/[^0-9]/g, ''))}
disabled={isLoading}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-4 pl-4 pr-4 focus:ring-2 focus:ring-canina-blue/20 outline-none font-black text-2xl text-center tracking-[0.5em]"
/>
</div>
{countdown > 0 ? (
<p className="text-xs font-bold text-medical-gray-400 text-center">ارسال مجدد پس از {toPersian(countdown)} ثانیه</p>
) : (
<button type="button" onClick={handleForgotSendOtp as any} disabled={isLoading} className="text-xs font-black text-canina-blue hover:underline flex items-center gap-1.5 justify-center mx-auto">
<RefreshCw className="w-3.5 h-3.5" />ارسال مجدد
</button>
)}
<button type="submit" disabled={isLoading || otpCode.length < 5} className="w-full py-4 bg-canina-blue text-white rounded-xl font-black disabled:opacity-50 transition-all shadow-lg shadow-canina-blue/20 flex items-center justify-center gap-2">
{isLoading ? <RefreshCw className="w-5 h-5 animate-spin" /> : <>تایید کد</>}
</button>
</motion.form>
)}
{/* Reset Password */}
{view === "reset-password" && (
<motion.form
key="reset-password-view"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
onSubmit={handleResetPassword}
className="space-y-4"
>
<div className="bg-green-50 border border-green-200 rounded-2xl p-4 text-center">
<p className="text-sm font-bold text-green-700">هویت تایید شد. رمز جدید خود را وارد کنید</p>
</div>
<div className="space-y-2">
<label htmlFor="new-password" className="text-xs font-bold text-medical-gray-500">رمز جدید</label>
<input
id="new-password"
name="new-password"
type="password"
autoComplete="new-password"
placeholder="حداقل ۶ کاراکتر"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
disabled={isLoading}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-3 px-4 focus:ring-2 focus:ring-canina-blue/20 outline-none text-left"
/>
</div>
<button type="submit" disabled={isLoading || newPassword.length < 6} className="w-full py-4 bg-canina-blue text-white rounded-xl font-black disabled:opacity-50 transition-all shadow-lg shadow-canina-blue/20 flex items-center justify-center gap-2">
{isLoading ? <RefreshCw className="w-5 h-5 animate-spin" /> : <>تغییر رمز عبور</>}
</button>
</motion.form>
)}
</AnimatePresence>
</div>
</motion.div>

View File

@ -36,27 +36,9 @@ export default function CheckoutPage() {
const [dbProducts, setDbProducts] = useState<Product[]>([]);
const [paymentMethod, setPaymentMethod] = useState<string>('online');
const [selectedAddressId, setSelectedAddressId] = useState<string>('');
const [showNewAddressForm, setShowNewAddressForm] = useState(false);
// Custom manual address state if guest or not using stored addresses
const [manualName, setManualName] = useState('');
const [manualPhone, setManualPhone] = useState('');
const [manualAddress, setManualAddress] = useState('');
useEffect(() => {
productService.getProducts({ limit: 999 }).then(res => setDbProducts(res.data));
// Set default selected address if logged in
if (profile.addresses && profile.addresses.length > 0) {
const def = profile.addresses.find(a => a.isDefault) || profile.addresses[0];
setSelectedAddressId(def.id);
} else if (profile.firstName) {
// Pre-fill profile info for manual fields
setManualName(`${profile.firstName} ${profile.lastName}`.trim());
setManualPhone(profile.mobile || '');
}
}, [profile]);
}, []);
const subtotal = getSubtotal();
const roundedAmount = Math.ceil(subtotal / 10000) * 10000;
@ -73,49 +55,26 @@ export default function CheckoutPage() {
};
const handleFinalize = async () => {
const totalAmount = getTotal();
if (paymentMethod === 'wallet' && profile.walletBalance < totalAmount) {
toast.error("اعتبار کیف پول شما کافی نیست. لطفاً ابتدا آن را شارژ کنید یا روش پرداخت دیگری انتخاب کنید.");
return;
}
setLoading(true);
const activePet = getActivePet();
try {
// 1. If using wallet, deduct balance and add purchase transaction
if (paymentMethod === 'wallet') {
const newBalance = profile.walletBalance - totalAmount;
const newTransaction = {
id: `TR-${Math.floor(Math.random() * 90000) + 10000}`,
type: 'purchase' as const,
amount: totalAmount,
date: new Date().toISOString(),
status: 'success' as const
};
await updateProfile({
walletBalance: newBalance,
transactions: [newTransaction, ...(profile.transactions || [])]
});
}
// 2. Register the order in the backend
// 1. Register the order in the backend
const orderId = await addOrder({
items: [...items],
total: totalAmount,
total: getTotal(),
charityDonation: charityDonation,
petId: activePet?.id
});
// 3. Update user charity total
// 2. Update user charity total
if (charityDonation > 0) {
await updateProfile({
updateProfile({
charityDonationTotal: (profile.charityDonationTotal || 0) + charityDonation
});
}
// 4. Update pet consumptions for the refill logic
// 3. Update pet consumptions for the refill logic
if (activePet) {
const newConsumptions = [...(activePet.consumptions || [])];
@ -199,99 +158,20 @@ export default function CheckoutPage() {
<h2 className="text-xl sm:text-2xl font-black text-medical-gray-900 italic">اطلاعات ارسال</h2>
</div>
{profile.addresses && profile.addresses.length > 0 && !showNewAddressForm ? (
<div className="space-y-4">
<p className="text-xs font-bold text-medical-gray-500 mb-2">انتخاب از بین آدرس‌های ذخیره‌شده شما:</p>
<div className="grid gap-3">
{profile.addresses.map((addr) => (
<div
key={addr.id}
onClick={() => setSelectedAddressId(addr.id)}
className={cn(
"p-4 rounded-2xl border-2 cursor-pointer transition-all flex items-start gap-3",
selectedAddressId === addr.id ? "border-canina-blue bg-canina-blue/5" : "border-medical-gray-100 hover:border-medical-gray-200"
)}
>
<input
type="radio"
name="selectedAddress"
checked={selectedAddressId === addr.id}
onChange={() => setSelectedAddressId(addr.id)}
className="mt-1"
/>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
<span className="font-black text-sm text-medical-gray-900">{addr.title}</span>
{addr.isDefault && (
<span className="bg-canina-blue/10 text-canina-blue text-[9px] font-black px-2 py-0.5 rounded-full">پیش‌فرض</span>
)}
</div>
<p className="text-xs text-medical-gray-600 leading-relaxed truncate">{addr.province}، {addr.city}، {addr.detail}</p>
<p className="text-[10px] text-medical-gray-400 font-bold mt-1">گیرنده: {addr.receptorName} | همراه: {toPersian(addr.phone)}</p>
</div>
</div>
))}
</div>
<button
onClick={() => setShowNewAddressForm(true)}
className="mt-4 flex items-center gap-2 text-canina-blue font-black text-xs hover:underline"
>
<PlusCircle className="w-4 h-4" />
<span>استفاده از آدرس جدید</span>
</button>
</div>
) : (
<div className="space-y-4">
{profile.addresses && profile.addresses.length > 0 && (
<button
onClick={() => setShowNewAddressForm(false)}
className="mb-4 text-xs font-bold text-canina-blue hover:underline"
>
← بازگشت به آدرس‌های ذخیره‌شده
</button>
)}
<div className="grid md:grid-cols-2 gap-4 sm:gap-6">
<div className="space-y-2">
<label htmlFor="chk-fullname" className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest block">نام و نام خانوادگی</label>
<input
id="chk-fullname"
name="name"
type="text"
autoComplete="name"
value={manualName}
onChange={(e) => setManualName(e.target.value)}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-3 sm:py-4 px-4 sm:px-6 text-xs sm:text-sm focus:outline-none focus:ring-2 focus:ring-canina-blue/20"
placeholder="مثلاً: پارسا آقایی"
/>
</div>
<div className="space-y-2">
<label htmlFor="chk-tel" className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest block">شماره تماس</label>
<input
id="chk-tel"
name="tel"
type="tel"
autoComplete="tel"
value={manualPhone}
onChange={(e) => setManualPhone(e.target.value.replace(/[^0-9]/g, ''))}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-3 sm:py-4 px-4 sm:px-6 text-xs sm:text-sm focus:outline-none focus:ring-2 focus:ring-canina-blue/20"
placeholder="۰۹۱۲XXXXXXX"
/>
</div>
<div className="md:col-span-2 space-y-2">
<label htmlFor="chk-street" className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest block">آدرس دقیق پستی</label>
<textarea
id="chk-street"
name="street-address"
autoComplete="street-address"
value={manualAddress}
onChange={(e) => setManualAddress(e.target.value)}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-3 sm:py-4 px-4 sm:px-6 text-xs sm:text-sm focus:outline-none focus:ring-2 focus:ring-canina-blue/20 h-28 sm:h-32"
placeholder="استان، شهر، خیابان..."
/>
</div>
</div>
</div>
)}
<div className="grid md:grid-cols-2 gap-4 sm:gap-6">
<div className="space-y-2">
<label className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest block">نام و نام خانوادگی</label>
<input type="text" className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-3 sm:py-4 px-4 sm:px-6 text-xs sm:text-sm focus:outline-none focus:ring-2 focus:ring-canina-blue/20" placeholder="مثلاً: پارسا آقایی" />
</div>
<div className="space-y-2">
<label className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest block">شماره تماس</label>
<input type="text" className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-3 sm:py-4 px-4 sm:px-6 text-xs sm:text-sm focus:outline-none focus:ring-2 focus:ring-canina-blue/20" placeholder="۰۹۱۲XXXXXXX" />
</div>
<div className="md:col-span-2 space-y-2">
<label className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest block">آدرس دقیق پستی</label>
<textarea className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-3 sm:py-4 px-4 sm:px-6 text-xs sm:text-sm focus:outline-none focus:ring-2 focus:ring-canina-blue/20 h-28 sm:h-32" placeholder="استان، شهر، خیابان..." />
</div>
</div>
</section>
{/* Step 2: Payment */}
@ -378,46 +258,46 @@ export default function CheckoutPage() {
{/* Sidebar Summary */}
<div className="lg:col-span-4 space-y-8">
<div className="bg-medical-gray-900 rounded-[3rem] p-10 text-white shadow-2xl sticky top-32">
<h3 className="text-2xl font-black italic mb-8 border-b border-white/20 pb-4 text-white">خلاصه سفارش</h3>
<h3 className="text-2xl font-black italic mb-8 border-b border-white/10 pb-4">خلاصه سفارش</h3>
<div className="space-y-6 mb-10 overflow-y-auto max-h-60 pr-2">
{items.map((item) => (
<div key={item.product.id} className="flex justify-between items-start gap-4">
<div className="flex-1">
<p className="text-sm font-black text-white leading-tight">{item.product.name}</p>
<p className="text-[10px] text-white/60 font-bold mt-1">تعداد: {toPersian(item.quantity)} بسته</p>
<p className="text-sm font-black text-white/90 leading-tight">{item.product.name}</p>
<p className="text-[10px] text-white/40 font-bold mt-1">تعداد: {toPersian(item.quantity)} بسته</p>
</div>
<span className="text-xs font-mono font-bold font-vazir text-white">{toPersian((item.product.priceValue * item.quantity).toLocaleString())} تومان</span>
<span className="text-xs font-mono font-bold font-vazir">{toPersian((item.product.priceValue * item.quantity).toLocaleString())}</span>
</div>
))}
</div>
<div className="space-y-4 pt-8 border-t border-white/20">
<div className="flex justify-between text-sm font-bold text-white/80">
<div className="space-y-4 pt-8 border-t border-white/10">
<div className="flex justify-between text-sm font-bold text-white/60">
<span>مجموع اقلام</span>
<span className="font-vazir text-white">{toPersian(getSubtotal().toLocaleString())} تومان</span>
<span className="font-vazir">{toPersian(getSubtotal().toLocaleString())} تومان</span>
</div>
{isSubscribed && (
<div className="flex justify-between text-sm font-bold text-green-400">
<span>تخفیف اشتراک (۵٪-)</span>
<span className="font-vazir text-green-400">{toPersian(getDiscount().toLocaleString())} تومان</span>
<span className="font-vazir">{toPersian(getDiscount().toLocaleString())} تومان</span>
</div>
)}
{charityDonation > 0 && (
<div className="flex justify-between text-sm font-bold text-pink-400">
<span>ردپای مهربانی</span>
<span className="font-vazir text-pink-400">{toPersian(charityDonation.toLocaleString())} تومان+</span>
<span className="font-vazir">{toPersian(charityDonation.toLocaleString())} تومان+</span>
</div>
)}
<div className="flex justify-between text-sm font-bold text-white/80">
<div className="flex justify-between text-sm font-bold text-white/60">
<span>هزینه ارسال</span>
<span className="text-green-400 uppercase tracking-widest text-[10px] font-black">رایگان</span>
</div>
<div className="flex justify-between items-end pt-4">
<span className="text-xl font-black italic text-white">مبلغ نهایی</span>
<span className="text-xl font-black italic">مبلغ نهایی</span>
<div className="text-left">
<p className="text-3xl font-black text-canina-gold leading-none font-vazir">{toPersian(getTotal().toLocaleString())}</p>
<p className="text-[10px] text-white/50 font-bold mt-1">تومان</p>
<p className="text-3xl font-black text-canina-blue leading-none font-vazir">{toPersian(getTotal().toLocaleString())}</p>
<p className="text-[10px] text-white/30 font-bold mt-1">تومان</p>
</div>
</div>
</div>
@ -437,14 +317,12 @@ export default function CheckoutPage() {
}
}}
>
<input
id="chk-coupon"
name="coupon"
type="text"
autoComplete="off"
placeholder="کد تخفیف (مثلاً: CANINO2024)"
className="flex-1 bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-xs focus:ring-1 focus:ring-canina-blue outline-none"
/>
<input
name="coupon"
type="text"
placeholder="کد تخفیف (مثلاً: CANINO2024)"
className="flex-1 bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-xs focus:ring-1 focus:ring-canina-blue outline-none"
/>
<button type="submit" className="bg-white/10 px-4 py-3 rounded-xl text-[10px] font-black uppercase tracking-widest hover:bg-white/20 transition-all">اعمال</button>
</form>
</div>

View File

@ -300,39 +300,39 @@ export default function Header({
<div className="flex gap-1.5 sm:gap-2 items-center h-12">
{isLoggedIn ? (
<div className="flex items-center h-full gap-1 sm:gap-2 p-1 bg-medical-gray-50/50 border border-medical-gray-100 rounded-3xl">
{/* User Icon/Name (Direct link to dashboard) */}
<div className="flex items-center h-full gap-1.5 sm:gap-2 p-1 bg-medical-gray-50/50 border border-medical-gray-100 rounded-3xl">
{/* User Name (Direct link to dashboard) */}
<div
className="flex items-center gap-1 sm:gap-2.5 px-2 sm:px-4 py-2 hover:bg-white rounded-2xl cursor-pointer transition-all border border-transparent hover:border-medical-gray-200 min-w-0"
className="flex items-center gap-1.5 sm:gap-2.5 px-2.5 sm:px-4 py-2 hover:bg-white rounded-2xl cursor-pointer transition-all border border-transparent hover:border-medical-gray-200"
onClick={() => onNavigate("user-dashboard")}
>
<User className="w-4 h-4 sm:w-5 sm:h-5 text-canina-blue flex-shrink-0" />
<span className="hidden sm:inline text-[14px] font-black font-vazir text-medical-gray-900 leading-none truncate max-w-[80px]">
<User className="w-4 h-4 sm:w-5 sm:h-5 text-canina-blue" />
<span className="text-[12px] sm:text-[14px] font-black font-vazir text-medical-gray-900 leading-none">
{profile.firstName}
</span>
</div>
{/* Separator Line */}
<div className="w-[1.5px] h-4 sm:h-5 bg-medical-gray-200 flex-shrink-0" />
<div className="w-[1.5px] h-4 sm:h-5 bg-medical-gray-200" />
{/* Pet Icon & Switcher */}
{/* Pet Name & Switcher */}
<div className="relative h-full flex items-center" ref={petMenuRef}>
<div
className={cn(
"flex items-center gap-1 sm:gap-2.5 px-2 sm:px-4 py-2 rounded-2xl transition-all cursor-pointer min-w-0",
"flex items-center gap-1.5 sm:gap-2.5 px-2.5 sm:px-4 py-2 rounded-2xl transition-all cursor-pointer",
isPetSwitcherOpen ? "bg-canina-blue text-white shadow-lg shadow-canina-blue/20" : "text-canina-blue hover:bg-white"
)}
onClick={() => setIsPetSwitcherOpen(!isPetSwitcherOpen)}
>
{activePet?.type === "گربه" ? (
<Cat className={cn("w-4 h-4 sm:w-5 sm:h-5 flex-shrink-0", isPetSwitcherOpen ? "text-white" : "text-canina-blue/60")} />
<Cat className={cn("w-4 h-4 sm:w-5 sm:h-5", isPetSwitcherOpen ? "text-white" : "text-canina-blue/60")} />
) : (
<Dog className={cn("w-4 h-4 sm:w-5 sm:h-5 flex-shrink-0", isPetSwitcherOpen ? "text-white" : "text-canina-blue/60")} />
<Dog className={cn("w-4 h-4 sm:w-5 sm:h-5", isPetSwitcherOpen ? "text-white" : "text-canina-blue/60")} />
)}
<span className={cn("hidden sm:inline text-[14px] font-black font-vazir leading-none truncate max-w-[80px]", isPetSwitcherOpen ? "text-white" : "text-canina-blue")}>
<span className={cn("text-[12px] sm:text-[14px] font-black font-vazir leading-none", isPetSwitcherOpen ? "text-white" : "text-canina-blue")}>
{activePet?.name || "بدون پت"}
</span>
<ChevronDown className={cn("w-3 h-3 sm:w-4 sm:h-4 transition-transform flex-shrink-0", isPetSwitcherOpen ? "rotate-180" : "")} />
<ChevronDown className={cn("w-3.5 h-3.5 sm:w-4 sm:h-4 transition-transform", isPetSwitcherOpen ? "rotate-180" : "")} />
</div>
<AnimatePresence>