"use client"; import React, { useState, useEffect } from "react"; import { motion, AnimatePresence } 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, Dog, X, Plus, CreditCard, Activity, RotateCcw } from "lucide-react"; import { OrderRowSkeleton } from "./Skeleton"; import { useUserStore, Address } from "../lib/store/userStore"; import { useCartStore } from "../lib/store/cartStore"; import { useSettingsStore } from "../lib/store/settingsStore"; 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 { useCatalogMode } from "../lib/useCatalogMode"; import PetProfile from "./PetProfile"; import api, { BASE_DOMAIN } from "../lib/services/api"; import { useRouter, useSearchParams } from 'next/navigation'; export default function UserDashboard() { const router = useRouter(); const searchParams = useSearchParams(); const { profile, isLoggedIn, logout, updateProfile, addAddress, updateAddress, deleteAddress, setDefaultAddress, topUpWallet, fetchProfile } = useUserStore(); const { pets, activePetId, setActivePet, removePet } = usePetStore(); const isWithdrawalEnabled = useSettingsStore((s) => s.getBoolean('wallet_withdrawal_enabled', false)); const isRechargeEnabled = useSettingsStore((s) => s.getBoolean('wallet_recharge_enabled', true)); const { isCatalogOnly, showOrders, showWallet, showCharity } = useCatalogMode(); const isWalletRechargeAllowed = !isCatalogOnly && isRechargeEnabled; React.useEffect(() => { if (!isLoggedIn && typeof window !== "undefined") { router.push("/"); toast.error("برای دسترسی به داشبورد لطفاً ابتدا وارد حساب کاربری خود شوید."); } }, [isLoggedIn, router]); const { orders } = useCartStore(); const validTabs = React.useMemo(() => { const list = ['profile', 'pets', 'addresses', 'tickets', 'prescriptions']; if (showOrders) list.push('orders'); if (showWallet) list.push('wallet'); return list; }, [showOrders, showWallet]); // Initialize tab directly from URL or default to profile const initialTab = (searchParams.get('tab') as "profile" | "orders" | "wallet" | "addresses" | "tickets" | "pets" | "prescriptions") || "profile"; const [activeTab, setActiveTab] = React.useState<"profile" | "orders" | "wallet" | "addresses" | "tickets" | "pets" | "prescriptions" | "overview">( validTabs.includes(initialTab) ? initialTab : "profile" ); // Handle tab click and update browser URL without full reloads const handleTabChange = React.useCallback((tabId: "profile" | "orders" | "wallet" | "addresses" | "tickets" | "pets" | "prescriptions") => { setActiveTab(tabId); const newUrl = tabId === 'profile' ? '/dashboard' : `/dashboard?tab=${tabId}`; window.history.replaceState(null, '', newUrl); }, []); // Sync tab if user navigates back/forward in browser history or searchParams change useEffect(() => { const tabParam = searchParams.get('tab'); if (tabParam && validTabs.includes(tabParam)) { setActiveTab(tabParam as "profile" | "orders" | "wallet" | "addresses" | "tickets" | "pets" | "prescriptions"); } else if (!tabParam || !validTabs.includes(activeTab)) { setActiveTab("profile"); } }, [searchParams, validTabs, activeTab]); const [isLoadingOrders, setIsLoadingOrders] = useState(false); const [prescriptions, setPrescriptions] = useState([]); const [isLoadingPrescriptions, setIsLoadingPrescriptions] = useState(false); const fetchPrescriptions = React.useCallback(async () => { try { setIsLoadingPrescriptions(true); const res = await api.get('/prescriptions'); setPrescriptions(Array.isArray(res.data) ? res.data : (res.data?.data || [])); } catch { // ignore } finally { setIsLoadingPrescriptions(false); } }, []); useEffect(() => { if (activeTab === 'prescriptions') { fetchPrescriptions(); } }, [activeTab, fetchPrescriptions]); // 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 (err: unknown) { if (!(err as Record)?._toastShown) { 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) { if (!(err as Record)?._toastShown) { 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) { if (!(err as Record)?._toastShown) { 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); const [isWithdrawModalOpen, setIsWithdrawModalOpen] = useState(false); const [withdrawAmount, setWithdrawAmount] = useState(""); const [withdrawIban, setWithdrawIban] = useState(""); const [isSubmittingWithdrawal, setIsSubmittingWithdrawal] = useState(false); // Pet Delete State const [petToDelete, setPetToDelete] = useState<{ id: string; name: string } | null>(null); // 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]); // 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); 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); } }; 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 handleWithdrawSubmit = async (e: React.FormEvent) => { e.preventDefault(); const numAmount = parseInt(toEnglishDigits(withdrawAmount)); if (!numAmount || numAmount < 50000) { toast.error("حداقل مبلغ قابل برداشت ۵۰,۰۰۰ تومان است."); return; } if (numAmount > (profile.walletBalance || 0)) { toast.error("مبلغ درخواستی از موجودی کیف پول شما بیشتر است."); return; } if (!withdrawIban.trim() || withdrawIban.trim().length < 10) { toast.error("لطفاً شماره شبا یا شماره کارت معتبر وارد نمایید."); return; } try { setIsSubmittingWithdrawal(true); await ticketService.createTicket({ subject: `درخواست تسویه و برداشت وجه (${toPersian(numAmount.toLocaleString())} تومان)`, message: `درخواست برداشت وجه از کیف پول کاربری:\nمبلغ: ${numAmount.toLocaleString('fa-IR')} تومان\nشماره شبا/کارت: ${withdrawIban}\nشماره همراه کاربر: ${profile.mobile || ''}`, category: 'FINANCIAL', priority: 'HIGH' }); toast.success(`درخواست برداشت ${toPersian(numAmount.toLocaleString())} تومان با موفقیت ثبت شد و پس از بررسی واحد مالی واریز خواهد شد.`); setIsWithdrawModalOpen(false); setWithdrawAmount(""); setWithdrawIban(""); fetchTickets(); } catch { toast.error("خطا در ثبت درخواست برداشت. لطفاً با پشتیبانی تماس بگیرید."); } finally { setIsSubmittingWithdrawal(false); } }; const allTabs = [ { id: "profile", label: "اطلاعات فردی", icon: }, { id: "pets", label: "پت‌های من", icon: }, { id: "orders", label: "سفارشات من", icon: }, { id: "prescriptions", label: "نسخه‌های من", icon: }, { id: "wallet", label: "کیف پول", icon: }, { id: "addresses", label: "آدرس‌ها", icon: }, { id: "tickets", label: "پشتیبانی و مشاوره", icon: }, ]; const tabs = allTabs.filter(tab => { if (tab.id === 'orders' && !showOrders) return false; if (tab.id === 'wallet' && !showWallet) return false; return true; }); 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 === "pets" && (
0 ? "index" : "add"} />
)} {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 && ( تمدید خودکار ۵٪- )} {Number(order.charityDonation) > 0 && ( مهربانی: {toPersian(Number(order.charityDonation).toLocaleString())} ت )} {(() => { const st = order.status; let badgeLabel = 'در حال پردازش'; let badgeColor = 'bg-amber-50 text-amber-700 border-amber-200'; if (st === 'delivered') { badgeLabel = 'تحویل شده'; badgeColor = 'bg-green-50 text-green-700 border-green-200'; } else if (st === 'shipped') { badgeLabel = 'ارسال شده'; badgeColor = 'bg-blue-50 text-blue-700 border-blue-200'; } else if (st === 'packaged' || st === 'ready_to_ship') { badgeLabel = 'بسته‌بندی شده (آماده ارسال)'; badgeColor = 'bg-purple-50 text-purple-700 border-purple-200'; } else if (st === 'pending_payment') { badgeLabel = 'در انتظار پرداخت / ناموفق'; badgeColor = 'bg-rose-50 text-rose-700 border-rose-200'; } else if (st === 'cancelled') { badgeLabel = 'لغو شده'; badgeColor = 'bg-red-50 text-red-700 border-red-200'; } else if (st === 'refunded') { badgeLabel = 'مرجوع شده'; badgeColor = 'bg-gray-100 text-gray-700 border-gray-200'; } return ( {badgeLabel} ); })()}
)) )}
)}
)} {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())}
تومان
{isWithdrawalEnabled && ( )} {isWalletRechargeAllowed ? ( ) : ( شارژ در حالت کاتالوگ غیرفعال است )}
{/* Transaction History */}

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

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

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

) : ( (profile?.transactions || []).map((trx) => { const desc = trx.description || ""; const isRefund = trx.type === 'refund' || desc.includes('استرداد') || desc.includes('لغو') || desc.includes('عودت'); const isAdmin = desc.includes('مدیر') || desc.includes('دستی'); const isOnlineTopUp = trx.type === 'top_up' && !isRefund && !isAdmin; const isPurchase = trx.type === 'purchase' || desc.includes('پرداخت سفارش'); let title = "تراکنش کیف پول"; let badgeText = "تراکنش"; let badgeClass = "bg-medical-gray-100 text-medical-gray-600 border-medical-gray-200"; let iconBgClass = "bg-medical-gray-50 text-medical-gray-600 group-hover:bg-medical-gray-700 group-hover:text-white"; let IconComponent = ArrowUpCircle; let isPositive = trx.type === 'top_up' || trx.type === 'refund'; if (isRefund) { title = "استرداد وجه سفارش"; badgeText = "استرداد وجه"; badgeClass = "bg-blue-50 text-blue-700 border-blue-200/60"; iconBgClass = "bg-blue-50 text-blue-600 group-hover:bg-blue-600 group-hover:text-white"; IconComponent = RotateCcw; isPositive = true; } else if (isAdmin) { title = isPositive ? "شارژ توسط مدیریت" : "کسر توسط مدیریت"; badgeText = "تغییر توسط پشتیبانی"; badgeClass = "bg-amber-50 text-amber-700 border-amber-200/60"; iconBgClass = "bg-amber-50 text-amber-600 group-hover:bg-amber-600 group-hover:text-white"; IconComponent = ShieldCheck; } else if (isOnlineTopUp) { title = "افزایش اعتبار آنلاین (درگاه پرداخت)"; badgeText = "شارژ درگاه"; badgeClass = "bg-emerald-50 text-emerald-700 border-emerald-200/60"; iconBgClass = "bg-emerald-50 text-emerald-600 group-hover:bg-emerald-600 group-hover:text-white"; IconComponent = ArrowUpCircle; isPositive = true; } else if (isPurchase) { title = "پرداخت سفارش از کیف پول"; badgeText = "پرداخت سفارش"; badgeClass = "bg-rose-50 text-rose-700 border-rose-200/60"; iconBgClass = "bg-rose-50 text-rose-600 group-hover:bg-rose-600 group-hover:text-white"; IconComponent = ShoppingCart; isPositive = false; } return (
{title} {badgeText}
{desc && (

{desc}

)}
کد: {trx.id} {trx.transactionReference && ( <> مرجع/پیگیری: {trx.transactionReference} )} {toPersian(new Date(trx.date).toLocaleDateString("fa-IR"))}
{isPositive ? "+" : "-"}{toPersian(trx.amount.toLocaleString())} تومان
{trx.status === 'failed' ? "ناموفق" : trx.status === 'pending' ? "در انتظار تایید" : "موفق"}
); }) )}
)} {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' ? 'در حال بررسی' : 'بسته شده'}
); })}
)}
)} {activeTab === "prescriptions" && (

نسخه‌های پزشکی من

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

{isLoadingPrescriptions ? (
در حال بارگذاری نسخه‌ها...
) : prescriptions.length === 0 ? (

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

) : (
{prescriptions.map((rx: any) => (
نسخه #{rx.id.slice(0, 8)} {rx.pet && ( 🐾 {rx.pet.name} ({rx.pet.breed || rx.pet.type}) )}
{rx.status === 'APPROVED' ? 'تایید شده' : rx.status === 'REJECTED' ? 'عدم تایید' : 'در حال بررسی'} {toPersian(new Date(rx.createdAt).toLocaleDateString('fa-IR'))}
{rx.notes && (

توضیحات شما: {rx.notes}

)} {rx.adminNotes && (
پاسخ دامپزشک کنینا: {rx.adminNotes.split('__PRESCRIBED_PRODUCTS__:')[0]}
)}
))}
)}
)}
{/* Sidebar Widgets (Wallet & Charity) */} {(showWallet || showCharity) && (
{showWallet && (
موجودی کیف پول
{toPersian((profile.walletBalance ?? 0).toLocaleString())}
تومان
{isWalletRechargeAllowed ? ( ) : (
شارژ آنلاین موقتاً غیرفعال است
)}
)} {showCharity && (

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

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

{toPersian((Number(profile.charityDonationTotal) || 0).toLocaleString())} تومان

همکاری شما باعث تامین هزینه {toPersian(Math.floor((Number(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" />