"use client"; import React, { useState } from "react"; import { motion } from "motion/react"; import { User, ShoppingBag, Wallet, MapPin, LogOut, ChevronRight, Package, Calendar, UserCircle, ShoppingCart, Trash2, Edit2, Phone, Hash, CheckCircle2, ArrowUpCircle, ArrowDownCircle, Info, Clock, CheckCircle, Heart, Sparkles, MessageSquare, Send, Stethoscope, FileText, Lock, Key, Eye, EyeOff, ShieldCheck } from "lucide-react"; import { OrderRowSkeleton } from "./Skeleton"; import { useUserStore, Address } from "../lib/store/userStore"; import { useCartStore } from "../lib/store/cartStore"; import { toPersian, toEnglishDigits, cn } from "../lib/utils"; import { toast } from "sonner"; import OrderDetailsModal from "./OrderDetailsModal"; import AddressModal from "./AddressModal"; import DeleteConfirmModal from "./DeleteConfirmModal"; import TopUpModal from "./TopUpModal"; import { ticketService, Ticket, TicketMessage } from "../lib/services/ticketService"; import { usePetStore } from "../lib/store/usePetStore"; import { useRouter } from 'next/navigation'; export default function UserDashboard() { const router = useRouter(); const { profile, isLoggedIn, logout, updateProfile, addAddress, updateAddress, deleteAddress, setDefaultAddress, topUpWallet } = useUserStore(); const { pets } = usePetStore(); React.useEffect(() => { if (!isLoggedIn && typeof window !== "undefined") { router.push("/"); toast.error("برای دسترسی به داشبورد لطفاً ابتدا وارد حساب کاربری خود شوید."); } }, [isLoggedIn, router]); const { orders } = useCartStore(); const [activeTab, setActiveTab] = React.useState<"profile" | "orders" | "wallet" | "addresses" | "tickets" | "overview">("profile"); const [isLoadingOrders, setIsLoadingOrders] = useState(false); // Tickets State const [tickets, setTickets] = useState([]); const [isLoadingTickets, setIsLoadingTickets] = useState(false); const [isNewTicketModalOpen, setIsNewTicketModalOpen] = useState(false); const [selectedTicket, setSelectedTicket] = useState(null); const [newTicketSubject, setNewTicketSubject] = useState(""); const [newTicketMessage, setNewTicketMessage] = useState(""); const [newTicketCategory, setNewTicketCategory] = useState("VET_CONSULTATION"); const [newTicketPriority, setNewTicketPriority] = useState("MEDIUM"); const [newTicketPetId, setNewTicketPetId] = useState(""); const [isSubmittingTicket, setIsSubmittingTicket] = useState(false); const [replyMessage, setReplyMessage] = useState(""); const [isSendingReply, setIsSendingReply] = useState(false); const fetchTickets = React.useCallback(async () => { try { setIsLoadingTickets(true); const data = await ticketService.getMyTickets(); setTickets(data); } catch { toast.error("خطا در دریافت لیست تیکت‌ها"); } finally { setIsLoadingTickets(false); } }, []); React.useEffect(() => { if (activeTab === "tickets") { fetchTickets(); } }, [activeTab, fetchTickets]); const handleCreateTicket = async (e: React.FormEvent) => { e.preventDefault(); if (!newTicketSubject.trim() || !newTicketMessage.trim()) { toast.error("لطفاً موضوع و متن پیام را وارد کنید."); return; } try { setIsSubmittingTicket(true); await ticketService.createTicket({ subject: newTicketSubject.trim(), message: newTicketMessage.trim(), category: newTicketCategory, priority: newTicketPriority, petId: newTicketPetId || undefined, }); toast.success("تیکت شما با موفقیت ثبت شد!"); setIsNewTicketModalOpen(false); setNewTicketSubject(""); setNewTicketMessage(""); setNewTicketPetId(""); fetchTickets(); } catch (err: unknown) { const gErr = err as { response?: { data?: { message?: string } }; message?: string }; toast.error(gErr.response?.data?.message || gErr.message || "خطا در ثبت تیکت"); } finally { setIsSubmittingTicket(false); } }; const handleSendReply = async (e: React.FormEvent) => { e.preventDefault(); if (!selectedTicket || !replyMessage.trim()) return; try { setIsSendingReply(true); const newMsg = await ticketService.replyTicket(selectedTicket.id, replyMessage.trim()); setSelectedTicket((prev) => prev ? { ...prev, messages: [...prev.messages, newMsg] } : null, ); setReplyMessage(""); toast.success("پاسخ شما با موفقیت ارسال شد"); fetchTickets(); } catch (err: unknown) { const gErr = err as { response?: { data?: { message?: string } }; message?: string }; toast.error(gErr.response?.data?.message || gErr.message || "خطا در ارسال پاسخ"); } finally { setIsSendingReply(false); } }; // Fetch fresh profile data (which includes orders) whenever orders tab is opened React.useEffect(() => { if (activeTab === "orders") { let isSubscribed = true; Promise.resolve().then(() => { if (isSubscribed) setIsLoadingOrders(true); }); useUserStore.getState().fetchProfile().finally(() => { if (isSubscribed) setIsLoadingOrders(false); }); return () => { isSubscribed = false; }; } }, [activeTab]); const [selectedOrder, setSelectedOrder] = useState | null>(null); // Address State const [isAddressModalOpen, setIsAddressModalOpen] = useState(false); const [editingAddress, setEditingAddress] = useState
(null); const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const [addressToDelete, setAddressToDelete] = useState
(null); // Wallet State const [isTopUpModalOpen, setIsTopUpModalOpen] = useState(false); // Profile State const [isEditing, setIsEditing] = useState(false); const [isSavingProfile, setIsSavingProfile] = useState(false); const [profileForm, setProfileForm] = useState(() => ({ firstName: profile.firstName || "", lastName: profile.lastName || "", email: profile.email || "", mobile: profile.mobile || "" })); // Sync profile data on change React.useEffect(() => { let isSubscribed = true; Promise.resolve().then(() => { if (isSubscribed) { setProfileForm({ firstName: profile.firstName || "", lastName: profile.lastName || "", email: profile.email || "", mobile: profile.mobile || "" }); } }); return () => { isSubscribed = false; }; }, [profile]); if (!isLoggedIn) { return null; } const handleSaveProfile = async (e: React.FormEvent) => { e.preventDefault(); setIsSavingProfile(true); try { const cleanPhone = toEnglishDigits(profileForm.mobile); await updateProfile({ firstName: profileForm.firstName, lastName: profileForm.lastName, email: profileForm.email, mobile: cleanPhone }); setIsEditing(false); toast.success("اطلاعات کاربری با موفقیت ویرایش شد"); } catch { toast.error("خطا در ویرایش اطلاعات"); } finally { setIsSavingProfile(false); } }; // Password Management State const [currentPassword, setCurrentPassword] = useState(""); const [newPassword, setNewPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState(""); const [showCurrentPass, setShowCurrentPass] = useState(false); const [showNewPass, setShowNewPass] = useState(false); const [showConfirmPass, setShowConfirmPass] = useState(false); const [isSavingPassword, setIsSavingPassword] = useState(false); const handleSavePassword = async (e: React.FormEvent) => { e.preventDefault(); if (!newPassword || newPassword.length < 6) { toast.error("رمز عبور جدید باید حداقل ۶ کاراکتر باشد"); return; } if (newPassword !== confirmPassword) { toast.error("رمز عبور جدید و تکرار آن یکسان نیستند"); return; } if (profile.hasPassword && !currentPassword) { toast.error("لطفاً رمز عبور فعلی خود را وارد کنید"); return; } setIsSavingPassword(true); try { await updateProfile({ password: newPassword.trim(), currentPassword: profile.hasPassword ? currentPassword.trim() : undefined, }); toast.success(profile.hasPassword ? "رمز عبور با موفقیت تغییر کرد" : "رمز عبور با موفقیت برای حساب شما ثبت شد"); setCurrentPassword(""); setNewPassword(""); setConfirmPassword(""); } catch (err: unknown) { const errObj = err as { message?: string }; toast.error(errObj.message || "خطا در ثبت رمز عبور"); } finally { setIsSavingPassword(false); } }; const handleLogout = () => { logout(); router.push('/'); }; const handleSaveAddress = async (addr: Address) => { try { if (editingAddress) { await updateAddress(addr.id, addr); } else { await addAddress(addr); } } catch { toast.error("خطا در ثبت آدرس"); } setEditingAddress(null); }; const handleDeleteClick = (addr: Address) => { setAddressToDelete(addr); setIsDeleteModalOpen(true); }; const handleConfirmDelete = async () => { if (addressToDelete) { try { await deleteAddress(addressToDelete.id); toast.success("آدرس با موفقیت حذف شد"); } catch { toast.error("خطا در حذف آدرس"); } setAddressToDelete(null); } }; const handleEditClick = (addr: Address) => { setEditingAddress(addr); setIsAddressModalOpen(true); }; const handleAddClick = () => { setEditingAddress(null); setIsAddressModalOpen(true); }; const tabs = [ { id: "profile", label: "اطلاعات فردی", icon: }, { id: "orders", label: "سفارشات من", icon: }, { id: "wallet", label: "کیف پول", icon: }, { id: "addresses", label: "آدرس‌ها", icon: }, { id: "tickets", label: "پشتیبانی و مشاوره", icon: }, ]; return (
{/* Breadcrumb */}
router.push('/')}>خانه پنل کاربری
{/* Top User Info & Navigation Tabs */}

{profile.firstName} {profile.lastName}

{profile.email}

{/* Tab Navigation */}
{tabs.map(tab => ( ))}
{/* Main Content Area */}
{activeTab === "profile" && (

اطلاعات فردی

setProfileForm({ ...profileForm, firstName: e.target.value })} className={cn( "w-full border p-4 rounded-2xl font-bold outline-none transition-all", isEditing ? "bg-white border-canina-blue/30 focus:ring-2 focus:ring-canina-blue/20" : "bg-medical-gray-50 border-medical-gray-100" )} />
setProfileForm({ ...profileForm, lastName: e.target.value })} className={cn( "w-full border p-4 rounded-2xl font-bold outline-none transition-all", isEditing ? "bg-white border-canina-blue/30 focus:ring-2 focus:ring-canina-blue/20" : "bg-medical-gray-50 border-medical-gray-100" )} />
setProfileForm({ ...profileForm, email: e.target.value })} className={cn( "w-full border p-4 rounded-2xl font-bold outline-none transition-all text-left", isEditing ? "bg-white border-canina-blue/30 focus:ring-2 focus:ring-canina-blue/20" : "bg-medical-gray-50 border-medical-gray-100" )} dir="ltr" />
setProfileForm({ ...profileForm, mobile: e.target.value.replace(/[^0-9]/g, '') })} className={cn( "w-full border p-4 rounded-2xl font-bold outline-none transition-all text-left", isEditing ? "bg-white border-canina-blue/30 focus:ring-2 focus:ring-canina-blue/20" : "bg-medical-gray-50 border-medical-gray-100 opacity-75 cursor-not-allowed" )} dir="ltr" />
{isEditing ? ( <> ) : ( )}
{/* Password & Security Section */}

{profile.hasPassword ? "تغییر رمز عبور حساب" : "تعریف رمز عبور برای حساب کاربری"}

{profile.hasPassword ? "برای افزایش امنیت می‌توانید رمز عبور ورود خود را تغییر دهید." : "با تعیین رمز عبور می‌توانید در دفعات بعدی علاوه بر پیامک، با رمز عبور نیز وارد شوید."}

{profile.hasPassword ? "رمز عبور فعال است" : "ورود فقط با OTP"}
{profile.hasPassword && (
setCurrentPassword(e.target.value)} className="w-full bg-white border border-medical-gray-200 rounded-xl py-3 pr-3 pl-10 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm text-left font-mono" dir="ltr" />
)}
setNewPassword(e.target.value)} className="w-full bg-white border border-medical-gray-200 rounded-xl py-3 pr-3 pl-10 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm text-left font-mono" dir="ltr" />
setConfirmPassword(e.target.value)} className="w-full bg-white border border-medical-gray-200 rounded-xl py-3 pr-3 pl-10 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm text-left font-mono" dir="ltr" />
)} {activeTab === "orders" && (

تاریخچه سفارشات

تعداد کل: {toPersian((orders || []).length.toString())} مورد
{(orders || []).length === 0 ? (

هنوز سفارشی ثبت نکرده‌اید.

) : (
{isLoadingOrders ? ( [1, 2, 3].map(i => ) ) : ( (orders || []).map(order => ( setSelectedOrder(order as unknown as Record)} className="p-6 border border-medical-gray-100 rounded-[2.5rem] hover:border-canina-blue transition-all group cursor-pointer bg-white overflow-hidden relative" >
سفارش {toPersian(order.trackingNumber || order.id?.substring(0, 8))}
{toPersian(new Date(order.date || (order as unknown as Record).createdAt || 0).toLocaleDateString("fa-IR"))} - ساعت {toPersian(new Date(order.date || (order as unknown as Record).createdAt || 0).toLocaleTimeString("fa-IR", { hour: '2-digit', minute: '2-digit' }))}
{toPersian(order.total.toLocaleString())} تومان
{order.isRefill && ( تمدید خودکار ۵٪- )} {order.status === 'delivered' ? 'تحویل شده' : order.status === 'shipped' ? 'ارسال شده' : 'در حال پردازش'}
)) )}
)}
)} {activeTab === "addresses" && (

آدرس‌های من

{(profile?.addresses || []).length === 0 ? (

هنوز آدرسی ثبت نکرده‌اید.

) : ( (profile?.addresses || []).map((addr) => (

{addr.title}

{addr.isDefault && ( پیش‌فرض )}

تحویل گیرنده: {addr.receptorName}

{toPersian(addr.province)}، {toPersian(addr.city)}، {toPersian(addr.detail)}

{toPersian(addr.phone)}
{toPersian(addr.zipCode)}
{!addr.isDefault && ( )} {addr.isDefault && (
آدرس اصلی ارسال
)}
)) )}
)} {activeTab === "wallet" && (

مدیریت کیف پول

حساب تایید شده
{/* Main Balance Card */}
موجودی زنده و قابل استفاده
{toPersian((profile.walletBalance || 0).toLocaleString())}
تومان
{/* Transaction History */}

تاریخچه تراکنش‌ها

{(profile?.transactions || []).length === 0 ? (

تراکنشی یافت نشد.

) : ( (profile?.transactions || []).map((trx) => (
{trx.type === 'top_up' ? : }
{trx.type === 'top_up' ? "افزایش موجودی (شارژ)" : "پرداخت سفارش"}
کد: {trx.id} {toPersian(new Date(trx.date).toLocaleDateString("fa-IR"))}
{trx.type === 'top_up' ? "+" : ""}{toPersian(trx.amount.toLocaleString())} تومان
تایید شده
)) )}
)} {activeTab === "tickets" && (

پشتیبانی و مشاوره آنلاین دامپزشک

پاسخگویی تخصصی به سوالات دارویی، مکمل‌ها و پیگیری سفارشات توسط تیم پزشکی کنینا

{isLoadingTickets ? (
در حال بارگذاری تیکت‌ها...
) : tickets.length === 0 ? (

هنوز تیکتی ثبت نکرده‌اید

شما می‌توانید سوالات پزشکی و تغذیه‌ای حیوان خانگی خود را از دامپزشکان متخصص کنینا بپرسید یا وضعیت سفارشات خود را پیگیری کنید.

) : (
{tickets.map((t) => { const isVet = t.category === 'VET_CONSULTATION'; return (
setSelectedTicket(t)} className="p-5 sm:p-6 bg-medical-gray-50 hover:bg-white border border-medical-gray-200 hover:border-canina-blue/30 rounded-[1.5rem] sm:rounded-[2rem] flex flex-col md:flex-row items-start md:items-center justify-between gap-4 transition-all hover:shadow-lg hover:shadow-canina-blue/5 cursor-pointer group" >
{isVet ? : }

{t.subject}

{t.pet && ( 🐾 {t.pet.name} ({t.pet.breed || t.pet.type}) )}

شناسه: {t.ticketNumber} • {t.messages?.length || 1} پیام • {new Date(t.updatedAt).toLocaleDateString('fa-IR')}

{t.status === 'ANSWERED' ? 'پاسخ داده شد' : t.status === 'OPEN' ? 'در انتظار پاسخ' : t.status === 'IN_PROGRESS' ? 'در حال بررسی' : 'بسته شده'}
); })}
)}
)}
{/* Sidebar Widgets (Wallet & Charity) */}
موجودی کیف پول
{toPersian((profile.walletBalance ?? 0).toLocaleString())}
تومان

ردپای مهربانی

مجموع کمک‌های اهدایی

{toPersian((Math.max(profile.charityDonationTotal || 0, orders.reduce((sum, o) => sum + Number(o.charityDonation || 0), 0))).toLocaleString())} تومان

همکاری شما باعث تامین هزینه {toPersian(Math.floor((profile.charityDonationTotal || 0) / 15000).toString())} وعده غذا برای حیوانات بی‌پناه شده است.

setSelectedOrder(null)} order={selectedOrder} /> setIsAddressModalOpen(false)} onSave={handleSaveAddress} editingAddress={editingAddress} /> setIsDeleteModalOpen(false)} onConfirm={handleConfirmDelete} title="حذف آدرس" message={`آیا از حذف آدرس "${addressToDelete?.title}" اطمینان دارید؟ این عمل غیرقابل بازگشت است.`} /> setIsTopUpModalOpen(false)} onConfirm={topUpWallet} /> {/* New Ticket Modal */} {isNewTicketModalOpen && (

ثبت تیکت و مشاوره دامپزشک

پاسخگویی توسط دامپزشکان و کارشناسان کنینا

setNewTicketSubject(e.target.value)} className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl text-sm font-medium focus:border-canina-blue outline-none" />