diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx index dbdbd94..2e87630 100644 --- a/app/dashboard/page.tsx +++ b/app/dashboard/page.tsx @@ -1,736 +1,20 @@ -'use client'; - -import React, { useState } from 'react'; -import { - User, Crown, Home, ShoppingBag, Wallet, MapPin, - Headphones, Heart, IdCard, LogOut, TrendingUp, - Plus, Hash, Clock, AlertCircle, Eye, Reply, - CreditCard, Gift, Edit, Trash2, CircleCheck, Info, Box, Camera, Check, Shield, ShieldAlert, Mail, CheckCircle, ShieldX -} from 'lucide-react'; -import { useSearchParams } from "next/navigation"; -import { useEffect } from "react"; -import { useRouter } from "next/navigation"; - - - - -// === رابط‌ها و کامپوننت‌های کمکی === -interface PriceDisplayProps { - amount: number | string | null; - currency?: string; - unit?: string; -} - -const PriceDisplay: React.FC = ({ amount, currency = 'تومان', unit }) => { - if (amount === null || amount === undefined || amount === 0) { - return استعلام; - } - const formattedAmount = typeof amount === 'number' - ? amount.toLocaleString('fa-IR') - : Number(amount.toString().replace(/,/g, '')).toLocaleString('fa-IR'); +import { Suspense } from "react"; +import { DashboardClient } from "@/components/dashboard/dashboard-client"; +function DashboardFallback() { return ( - <> - {formattedAmount} {unit ? unit : currency} - - ); -}; - -// === داده‌های نمونه === -const sampleOrders = [ - { - id: "PR-10452", - status: "در حال پردازش", - statusColor: "amber", - items: [ - { name: "پمپ آب صنعتی مدل PX-200", qty: 2, price: "۳۰۰,۰۰۰" }, - { name: "فیلتر هوای کابین خودرو", qty: 1, price: "۲۰۰,۰۰۰" }, - ], - total: "۵۰۰,۰۰۰", - regDate: "۱۵ دی ۱۴۰۴", - deliveryDate: "۲۰ دی ۱۴۰۴", - delivered: false, - }, - { - id: "PR-10451", - status: "تحویل شده", - statusColor: "green", - items: [ - { name: "روغن موتور ۵W-30 کاسترول", qty: 4, price: "۱۲۰,۰۰۰" }, - ], - total: "۱۲۰,۰۰۰", - regDate: "۱۰ دی ۱۴۰۴", - deliveryDate: "۱۲ دی ۱۴۰۴", - delivered: true, - }, -]; - -const sampleTickets = [ - { - id: "54321", - title: "عدم تطابق قطعه ارسالی با فاکتور", - date: "۱۴ دی ۱۴۰۴", - department: "بخش فروش", - priority: "اولویت بالا", - status: "در حال بررسی", - statusColor: "amber", - }, -]; - -const sampleAddresses = [ - { - id: "addr1", - text: "شیراز، بلوار معالی آباد، خیابان پزشکان، ساختمان پارس، واحد ۴", - receiver: "علی محمدی", - phone: "۰۹۱۲۳۴۵۶۷۸۹", - isDefault: true, - }, - { - id: "addr2", - text: "تهران، خیابان سعدی جنوبی، کوچه ناظم‌الاطباء، پلاک ۲۰", - receiver: "دفتر تهران (شرکت پترو صدف)", - phone: "۰۲۱-۳۳۹۰۰۰۰", - isDefault: false, - }, -]; - -export default function Dashboard() { - - const [activeTab, setActiveTab] = useState('dash'); - const searchParams = useSearchParams(); - const success = searchParams.get("success"); - const [authorized, setAuthorized] = useState(null); - React.useEffect(() => { - const token = localStorage.getItem("accessToken"); - const role = localStorage.getItem("role"); - - if (token && (role === "user" || role === "admin")) { - setAuthorized(true); - } else { - setAuthorized(false); - } - }, []); - - - const menuItems = [ - { id: 'dash', label: 'پیشخوان', icon: Home }, - { id: 'orders', label: 'سفارش‌های من', icon: ShoppingBag }, - { id: 'wallet', label: 'کیف پول', icon: Wallet }, - { id: 'address', label: 'آدرس‌ها', icon: MapPin }, - { id: 'tickets', label: 'تیکت پشتیبانی', icon: Headphones }, - { id: 'profile', label: 'مشخصات حساب', icon: IdCard }, - ]; - - - // ✅ حالت در حال بررسی - if (authorized === null) { - return ( -
-

در حال بررسی دسترسی...

-
- ); - } - - // ❌ دسترسی ندارد - if (!authorized) { - const router = useRouter(); - return ( -
-
- -
-
- -
-
- -

- دسترسی غیرمجاز -

- -

- شما اجازه دسترسی به این صفحه را ندارید. - برای مشاهده داشبورد باید ابتدا وارد حساب کاربری خود شوید. -

- -
- -
- -
-
- ); - } - - - - return ( -
-
- - {success === "login" && ( -
- - ورود با موفقیت انجام شد -
- )} - - {success === "register" && ( -
- - ثبت نام با موفقیت انجام شد -
- )} - -
- - - -
- - {/* -------------------- تب پیشخوان -------------------- */} - {activeTab === 'dash' && ( -
-

خوش آمدید، علی عزیز

-
-
-
-
-

موجودی کیف پول

- -
-
-
-
-
-

سفارشات جاری

- ۲ مورد -
-
-
-
-
-

تیکت‌های باز

- ۱ مورد -
-
-
-
-
-

امتیاز وفاداری

- ۲,۳۴۰ امتیاز -
-
-
- -
-
-
-

آخرین سفارشات

- -
-
- {/* پیش‌نمایش کوچکی از سفارشات */} -
- #PR-10452 - در حال پردازش -
-
-
-
-
- )} - - {/* -------------------- تب تیکت‌ها -------------------- */} - {activeTab === 'tickets' && ( -
-
-

تیکت‌های پشتیبانی

- -
- -
-
- ۵ -
کل تیکت‌ها
-
-
- ۱ -
در حال بررسی
-
-
- ۳ -
پاسخ داده شده
-
-
- ۱ -
بسته شده
-
-
- -
-
- - - - -
-
- -
- {sampleTickets.map(ticket => ( -
-
-

{ticket.title}

-
- {ticket.id} - {ticket.date} - {ticket.department} - {ticket.priority} -
-
-
- - {ticket.status} - -
- - -
-
-
- ))} -
-
- )} - - {/* -------------------- تب آدرس‌ها -------------------- */} - {activeTab === 'address' && ( -
-
-

آدرس‌های من

- -
- -
- {sampleAddresses.map((address) => ( -
- {address.isDefault && ( - - پیش‌فرض - - )} -
- {address.text} -
-
-
گیرنده: {address.receiver}
-
تماس: {address.phone}
-
-
- {!address.isDefault && ( - - )} - - -
-
- ))} -
-
- )} - - {/* -------------------- تب کیف پول -------------------- */} - {activeTab === 'wallet' && ( -
-

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

- -
-
- موجودی فعلی حساب شما: -
- ۱,۵۰۰,۰۰۰ تومان -
-
- آخرین بروزرسانی: امروز ۱۴:۳۰ -
-
-
- - -
-
- -
-
-
-
-

کل واریزی‌ها

- ۵,۲۰۰,۰۰۰ تومان -
-
-
-
-
-

کل برداشت‌ها

- ۳,۷۰۰,۰۰۰ تومان -
-
-
-
-
-

امتیاز وفاداری

- ۲,۳۴۰ امتیاز -
-
-
- -
-
-
- تاریخچه تراکنش‌ها -
- -
- -
-
-
-
- -
-
-
پرداخت بابت سفارش #PR-10452
-
۱۵ دی ۱۴۰۴ - ساعت ۱4:۳۰
-
کد پیگیری: TXN-789456123
-
-
-
- - ۵۰۰,۰۰۰ تومان -
-
- -
-
-
- -
-
-
افزایش آنلاین موجودی (درگاه بانکی)
-
۱۴ دی ۱۴۰۴ - ساعت ۱۰:۱۵
-
کد پیگیری: TXN-789456122
-
-
-
- + ۲,۰۰۰,۰۰۰ تومان -
-
- -
-
-
- -
-
-
استفاده از امتیاز وفاداری
-
۱۲ دی ۱۴۰۴ - ساعت ۱۶:۲۰
-
تبدیل ۵۰۰ امتیاز به تومان
-
-
-
- + ۵۰,۰۰۰ تومان -
-
-
-
-
- )} - - {/* -------------------- تب سفارش‌ها -------------------- */} - {activeTab === 'orders' && ( -
-

سفارش‌های من

- -
-
- - - - - -
-
- -
- {sampleOrders.map((order) => ( -
-
-
- {order.id} -
- - {order.status} - -
-
-
-
- {order.items.map((item, idx) => ( -
- {item.name} - تعداد: {item.qty} - {item.price} تومان -
- ))} -
-
-
تاریخ ثبت: {order.regDate}
-
- {order.delivered ? : } - {order.delivered ? 'تحویل شده:' : 'تحویل تا:'} {order.deliveryDate} -
-
- مبلغ کل: {order.total} تومان -
-
-
-
- - {!order.delivered && ( - - )} - {order.delivered ? ( - <> - - - - ) : ( - - )} -
-
-
- ))} -
-
- )} - - {/* -------------------- تب پروفایل -------------------- */} - {activeTab === 'profile' && ( -
-

پروفایل کاربری

- -
- {/* اطلاعات شخصی */} -
-

اطلاعات شخصی

- -
- {/* Avatar Section */} -
-
- User Avatar -
-
- -

فرمت‌های مجاز: JPG, PNG. حداکثر حجم: ۲ مگابایت

-
-
- - {/* Form Fields Grid */} -
-
- - -
-
- - -

برای تغییر شماره موبایل با پشتیبانی تماس بگیرید.

-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - {/* Submit Button */} -
- -
-
-
- - {/* تنظیمات امنیتی */} -
-

تنظیمات امنیتی

- -
- {/* Change Password */} -
-
-
- -
-
-

تغییر رمز عبور

-

آخرین تغییر: ۲ ماه پیش

-
-
- -
- - {/* Two-Factor Authentication */} -
-
-
- -
-
-

احراز هویت دو مرحله‌ای

-

- وضعیت: غیرفعال -

-
-
- -
- - {/* Email Notifications */} -
-
-
- -
-
-

اطلاع‌رسانی ایمیلی

-

دریافت ایمیل برای ورودهای جدید و هشدارهای امنیتی

-
-
- {/* Toggle Switch */} - -
-
-
-
-
- )} - -
-
+
+
+ در حال بارگذاری پیشخوان...
); } - +export default function DashboardPage() { + return ( + }> + + + ); +} diff --git a/components/dashboard/dashboard-client.tsx b/components/dashboard/dashboard-client.tsx new file mode 100644 index 0000000..3bc0239 --- /dev/null +++ b/components/dashboard/dashboard-client.tsx @@ -0,0 +1,826 @@ +"use client"; + +import React, { useDeferredValue, useEffect, useState } from "react"; +import { + AlertCircle, + ArrowRight, + CheckCircle2, + ChevronLeft, + Clock3, + Filter, + Headphones, + Home, + IdCard, + LoaderCircle, + LogOut, + MapPin, + MessageSquareMore, + Plus, + Search, + Send, + ShoppingBag, + Sparkles, + User, + Wallet, + XCircle, +} from "lucide-react"; +import { useRouter, useSearchParams } from "next/navigation"; +import { logoutUser } from "@/public/src/services/auth/api"; +import { + closeUserTicket, + createUserTicket, + getAdminTicketById, + getAdminTickets, + getUserTicketById, + getUserTickets, + sendAdminTicketMessage, + sendUserTicketMessage, + TicketDetail, + TicketListItem, + TicketListResponse, + TicketPriority, + TicketStatus, + updateAdminTicket, +} from "@/public/src/services/tickets/api"; + +type UserRole = "user" | "admin"; + +type SessionState = { + token: string; + role: UserRole; + fullName: string; + username: string; + phone: string; +}; + +type TicketSummary = { + total: number; + waiting_for_admin: number; + waiting_for_user: number; + closed: number; +}; + +const sampleOrders = [ + { id: "PR-10452", status: "در حال پردازش", total: "500,000", date: "15 دی 1404" }, + { id: "PR-10451", status: "تحویل شده", total: "120,000", date: "10 دی 1404" }, +]; + +const sampleAddresses = [ + { id: "addr1", title: "آدرس پیش‌فرض", text: "شیراز، بلوار معالی آباد، ساختمان پارس، واحد 4" }, + { id: "addr2", title: "دفتر تهران", text: "تهران، خیابان سعدی جنوبی، کوچه ناظم‌الاطباء، پلاک 20" }, +]; + +const statusLabels: Record = { + waiting_for_admin: "در انتظار پاسخ پشتیبانی", + waiting_for_user: "در انتظار پاسخ کاربر", + closed: "بسته شده", +}; + +const priorityLabels: Record = { + low: "کم", + normal: "عادی", + high: "زیاد", + urgent: "فوری", +}; + +const statusTone: Record = { + waiting_for_admin: "bg-amber-100 text-amber-800 border-amber-200", + waiting_for_user: "bg-sky-100 text-sky-800 border-sky-200", + closed: "bg-zinc-200 text-zinc-700 border-zinc-300", +}; + +const priorityTone: Record = { + low: "bg-emerald-100 text-emerald-700 border-emerald-200", + normal: "bg-zinc-100 text-zinc-700 border-zinc-200", + high: "bg-orange-100 text-orange-700 border-orange-200", + urgent: "bg-rose-100 text-rose-700 border-rose-200", +}; + +const userTicketStatusFilters: Array<{ label: string; value: "all" | TicketStatus }> = [ + { label: "همه", value: "all" }, + { label: statusLabels.waiting_for_admin, value: "waiting_for_admin" }, + { label: statusLabels.waiting_for_user, value: "waiting_for_user" }, + { label: statusLabels.closed, value: "closed" }, +]; + +const priorityOptions: TicketPriority[] = ["low", "normal", "high", "urgent"]; +const statusOptions: TicketStatus[] = ["waiting_for_admin", "waiting_for_user", "closed"]; + +function formatDateTime(value?: string | null) { + if (!value) return "ثبت نشده"; + return new Intl.DateTimeFormat("fa-IR", { dateStyle: "medium", timeStyle: "short" }).format(new Date(value)); +} + +function cn(...parts: Array) { + return parts.filter(Boolean).join(" "); +} + +function Badge({ className, children }: { className: string; children: React.ReactNode }) { + return {children}; +} + +function PanelCard({ children, className = "" }: { children: React.ReactNode; className?: string }) { + return
{children}
; +} + +function buildSummary( + allTickets: TicketListResponse, + waitingAdmin: TicketListResponse, + waitingUser: TicketListResponse, + closedTickets: TicketListResponse +): TicketSummary { + return { + total: allTickets.pagination.total, + waiting_for_admin: waitingAdmin.pagination.total, + waiting_for_user: waitingUser.pagination.total, + closed: closedTickets.pagination.total, + }; +} + +export function DashboardClient() { + const router = useRouter(); + const searchParams = useSearchParams(); + const success = searchParams.get("success"); + const currentTab = searchParams.get("tab"); + + const [session, setSession] = useState(null); + const [authorized, setAuthorized] = useState(null); + const [activeTab, setActiveTab] = useState("dash"); + const [ticketSummary, setTicketSummary] = useState({ + total: 0, + waiting_for_admin: 0, + waiting_for_user: 0, + closed: 0, + }); + + const [userTickets, setUserTickets] = useState([]); + const [userTicketsLoading, setUserTicketsLoading] = useState(false); + const [userTicketsError, setUserTicketsError] = useState(""); + const [userTicketsPagination, setUserTicketsPagination] = useState({ page: 1, totalPages: 1, total: 0 }); + const [userTicketStatusFilter, setUserTicketStatusFilter] = useState<"all" | TicketStatus>("all"); + const [userTicketPage, setUserTicketPage] = useState(1); + const [selectedUserTicketId, setSelectedUserTicketId] = useState(null); + const [selectedUserTicket, setSelectedUserTicket] = useState(null); + const [selectedUserTicketLoading, setSelectedUserTicketLoading] = useState(false); + const [userReplyMessage, setUserReplyMessage] = useState(""); + const [userReplySubmitting, setUserReplySubmitting] = useState(false); + const [newTicketOpen, setNewTicketOpen] = useState(false); + const [newTicketSubmitting, setNewTicketSubmitting] = useState(false); + const [newTicketError, setNewTicketError] = useState(""); + const [newTicketForm, setNewTicketForm] = useState({ subject: "", priority: "normal" as TicketPriority, message: "" }); + + const [adminTickets, setAdminTickets] = useState([]); + const [adminTicketsLoading, setAdminTicketsLoading] = useState(false); + const [adminTicketsError, setAdminTicketsError] = useState(""); + const [adminTicketsPagination, setAdminTicketsPagination] = useState({ page: 1, totalPages: 1, total: 0 }); + const [adminStatusFilter, setAdminStatusFilter] = useState<"" | TicketStatus>(""); + const [adminPriorityFilter, setAdminPriorityFilter] = useState<"" | TicketPriority>(""); + const [adminSearchInput, setAdminSearchInput] = useState(""); + const deferredAdminSearch = useDeferredValue(adminSearchInput); + const [adminTicketPage, setAdminTicketPage] = useState(1); + const [selectedAdminTicketId, setSelectedAdminTicketId] = useState(null); + const [selectedAdminTicket, setSelectedAdminTicket] = useState(null); + const [selectedAdminTicketLoading, setSelectedAdminTicketLoading] = useState(false); + const [adminReplyMessage, setAdminReplyMessage] = useState(""); + const [adminReplySubmitting, setAdminReplySubmitting] = useState(false); + const [adminUpdateSubmitting, setAdminUpdateSubmitting] = useState(false); + const [adminUpdateForm, setAdminUpdateForm] = useState({ + status: "waiting_for_user" as TicketStatus, + priority: "normal" as TicketPriority, + assignedAdminId: "", + }); + + useEffect(() => { + const token = localStorage.getItem("accessToken"); + const role = localStorage.getItem("role") as UserRole | null; + + if (!token || (role !== "user" && role !== "admin")) { + setAuthorized(false); + return; + } + + setSession({ + token, + role, + fullName: localStorage.getItem("fullName") || localStorage.getItem("username") || "کاربر پارس شاپ", + username: localStorage.getItem("username") || "user", + phone: localStorage.getItem("phone") || "شماره ثبت نشده", + }); + setAuthorized(true); + }, []); + + useEffect(() => { + const validTabs = new Set(["dash", "orders", "wallet", "address", "tickets", "profile"]); + setActiveTab(currentTab && validTabs.has(currentTab) ? currentTab : "dash"); + }, [currentTab]); + + useEffect(() => { + if (!session) return; + void refreshTicketSummary(session); + }, [session]); + + useEffect(() => { + if (!session || activeTab !== "tickets") return; + + if (session.role === "admin") { + void loadAdminTickets(session.token, { + status: adminStatusFilter || undefined, + priority: adminPriorityFilter || undefined, + search: deferredAdminSearch || undefined, + page: adminTicketPage, + limit: 12, + }); + return; + } + + void loadUserTickets(session.token, { + status: userTicketStatusFilter === "all" ? undefined : userTicketStatusFilter, + page: userTicketPage, + limit: 12, + }); + }, [activeTab, adminPriorityFilter, adminStatusFilter, adminTicketPage, deferredAdminSearch, session, userTicketPage, userTicketStatusFilter]); + + useEffect(() => { + if (!session || session.role !== "user" || !selectedUserTicketId) return; + void loadUserTicketDetail(session.token, selectedUserTicketId); + }, [selectedUserTicketId, session]); + + useEffect(() => { + if (!session || session.role !== "admin" || !selectedAdminTicketId) return; + void loadAdminTicketDetail(session.token, selectedAdminTicketId); + }, [selectedAdminTicketId, session]); + + async function refreshTicketSummary(currentSession: SessionState) { + try { + if (currentSession.role === "admin") { + const [allTickets, waitingAdmin, waitingUser, closedTickets] = await Promise.all([ + getAdminTickets(currentSession.token, { page: 1, limit: 1 }), + getAdminTickets(currentSession.token, { status: "waiting_for_admin", page: 1, limit: 1 }), + getAdminTickets(currentSession.token, { status: "waiting_for_user", page: 1, limit: 1 }), + getAdminTickets(currentSession.token, { status: "closed", page: 1, limit: 1 }), + ]); + + setTicketSummary(buildSummary(allTickets, waitingAdmin, waitingUser, closedTickets)); + return; + } + + const [allTickets, waitingAdmin, waitingUser, closedTickets] = await Promise.all([ + getUserTickets(currentSession.token, { page: 1, limit: 1 }), + getUserTickets(currentSession.token, { status: "waiting_for_admin", page: 1, limit: 1 }), + getUserTickets(currentSession.token, { status: "waiting_for_user", page: 1, limit: 1 }), + getUserTickets(currentSession.token, { status: "closed", page: 1, limit: 1 }), + ]); + + setTicketSummary(buildSummary(allTickets, waitingAdmin, waitingUser, closedTickets)); + } catch (error) { + console.error("Failed to load ticket summary", error); + } + } + + async function loadUserTickets(token: string, query: { status?: TicketStatus; page: number; limit: number }) { + setUserTicketsLoading(true); + setUserTicketsError(""); + try { + const response = await getUserTickets(token, query); + setUserTickets(response.items); + setUserTicketsPagination({ page: response.pagination.page, totalPages: response.pagination.totalPages, total: response.pagination.total }); + if (response.items.length > 0 && !selectedUserTicketId) setSelectedUserTicketId(response.items[0].id); + if (response.items.length === 0) { + setSelectedUserTicketId(null); + setSelectedUserTicket(null); + } + } catch (error: any) { + setUserTicketsError(error?.message || "دریافت تیکت‌ها با خطا مواجه شد."); + } finally { + setUserTicketsLoading(false); + } + } + + async function loadUserTicketDetail(token: string, ticketId: string) { + setSelectedUserTicketLoading(true); + try { + setSelectedUserTicket(await getUserTicketById(token, ticketId)); + } finally { + setSelectedUserTicketLoading(false); + } + } + + async function loadAdminTickets(token: string, query: { status?: TicketStatus; priority?: TicketPriority; search?: string; page: number; limit: number }) { + setAdminTicketsLoading(true); + setAdminTicketsError(""); + try { + const response = await getAdminTickets(token, query); + setAdminTickets(response.items); + setAdminTicketsPagination({ page: response.pagination.page, totalPages: response.pagination.totalPages, total: response.pagination.total }); + if (response.items.length > 0 && !selectedAdminTicketId) setSelectedAdminTicketId(response.items[0].id); + if (response.items.length === 0) { + setSelectedAdminTicketId(null); + setSelectedAdminTicket(null); + } + } catch (error: any) { + setAdminTicketsError(error?.message || "دریافت تیکت‌های ادمین با خطا مواجه شد."); + } finally { + setAdminTicketsLoading(false); + } + } + + async function loadAdminTicketDetail(token: string, ticketId: string) { + setSelectedAdminTicketLoading(true); + try { + const detail = await getAdminTicketById(token, ticketId); + setSelectedAdminTicket(detail); + setAdminUpdateForm({ + status: detail.status, + priority: detail.priority, + assignedAdminId: detail.assignedAdmin?.id || "", + }); + } finally { + setSelectedAdminTicketLoading(false); + } + } + + async function handleLogout() { + const token = localStorage.getItem("accessToken"); + try { + if (token) await logoutUser(token); + } catch (error) { + console.error("Logout failed:", error); + } finally { + localStorage.removeItem("accessToken"); + localStorage.removeItem("refreshToken"); + localStorage.removeItem("username"); + localStorage.removeItem("fullName"); + localStorage.removeItem("phone"); + localStorage.removeItem("role"); + setAuthorized(false); + router.push("/"); + } + } + + async function handleCreateTicket() { + if (!session) return; + setNewTicketError(""); + if (!newTicketForm.subject.trim() || !newTicketForm.message.trim()) { + setNewTicketError("موضوع و متن پیام را کامل کنید."); + return; + } + setNewTicketSubmitting(true); + try { + const detail = await createUserTicket(session.token, { + subject: newTicketForm.subject.trim(), + priority: newTicketForm.priority, + message: newTicketForm.message.trim(), + }); + setNewTicketOpen(false); + setNewTicketForm({ subject: "", priority: "normal", message: "" }); + setSelectedUserTicketId(detail.id); + setUserTicketPage(1); + await Promise.all([ + loadUserTickets(session.token, { status: userTicketStatusFilter === "all" ? undefined : userTicketStatusFilter, page: 1, limit: 12 }), + refreshTicketSummary(session), + ]); + } catch (error: any) { + setNewTicketError(error?.message || "ثبت تیکت انجام نشد."); + } finally { + setNewTicketSubmitting(false); + } + } + + async function handleSendUserReply() { + if (!session || !selectedUserTicketId || !userReplyMessage.trim()) return; + setUserReplySubmitting(true); + try { + setSelectedUserTicket(await sendUserTicketMessage(session.token, selectedUserTicketId, { message: userReplyMessage.trim() })); + setUserReplyMessage(""); + await Promise.all([ + loadUserTickets(session.token, { status: userTicketStatusFilter === "all" ? undefined : userTicketStatusFilter, page: userTicketPage, limit: 12 }), + refreshTicketSummary(session), + ]); + } finally { + setUserReplySubmitting(false); + } + } + + async function handleCloseUserTicket() { + if (!session || !selectedUserTicketId) return; + setSelectedUserTicket(await closeUserTicket(session.token, selectedUserTicketId)); + await Promise.all([ + loadUserTickets(session.token, { status: userTicketStatusFilter === "all" ? undefined : userTicketStatusFilter, page: userTicketPage, limit: 12 }), + refreshTicketSummary(session), + ]); + } + + async function handleAdminReply() { + if (!session || !selectedAdminTicketId || !adminReplyMessage.trim()) return; + setAdminReplySubmitting(true); + try { + setSelectedAdminTicket(await sendAdminTicketMessage(session.token, selectedAdminTicketId, { message: adminReplyMessage.trim() })); + setAdminReplyMessage(""); + await Promise.all([ + loadAdminTickets(session.token, { status: adminStatusFilter || undefined, priority: adminPriorityFilter || undefined, search: deferredAdminSearch || undefined, page: adminTicketPage, limit: 12 }), + refreshTicketSummary(session), + ]); + } finally { + setAdminReplySubmitting(false); + } + } + + async function handleAdminUpdateTicket() { + if (!session || !selectedAdminTicketId) return; + setAdminUpdateSubmitting(true); + try { + setSelectedAdminTicket(await updateAdminTicket(session.token, selectedAdminTicketId, { + status: adminUpdateForm.status, + priority: adminUpdateForm.priority, + assignedAdminId: adminUpdateForm.assignedAdminId.trim() || undefined, + })); + await Promise.all([ + loadAdminTickets(session.token, { status: adminStatusFilter || undefined, priority: adminPriorityFilter || undefined, search: deferredAdminSearch || undefined, page: adminTicketPage, limit: 12 }), + refreshTicketSummary(session), + ]); + } finally { + setAdminUpdateSubmitting(false); + } + } + + if (authorized === null) { + return ; + } + + if (!authorized || !session) { + return ( +
+ +
+ +
+

دسترسی غیرمجاز

+

برای مشاهده پیشخوان باید ابتدا وارد حساب کاربری شوید.

+ +
+
+ ); + } + + const menuItems = [ + { id: "dash", label: "پیشخوان", icon: Home }, + { id: "orders", label: "سفارش‌های من", icon: ShoppingBag }, + { id: "wallet", label: "کیف پول", icon: Wallet }, + { id: "address", label: "آدرس‌ها", icon: MapPin }, + { id: "tickets", label: session.role === "admin" ? "مدیریت تیکت‌ها" : "تیکت پشتیبانی", icon: Headphones }, + { id: "profile", label: "مشخصات حساب", icon: IdCard }, + ]; + + return ( +
+
+ {success === "login" && } + {success === "register" && } + +
+ +
+
+ +
+
{session.fullName}
+
{session.phone}
+
+ + {session.role === "admin" ? "ادمین پنل" : "حساب کاربری فعال"} +
+
+ +
+
+ {menuItems.map((item) => ( + + ))} +
+
+ +
+
+
+ +
+ {activeTab === "dash" && ( + <> + +

{session.role === "admin" ? "مرکز کنترل تیکت‌ها" : "پیشخوان شخصی شما"}

+

{session.role === "admin" ? `سلام ${session.fullName}، مدیریت پشتیبانی آماده است` : `سلام ${session.fullName}، خوش برگشتی`}

+

{session.role === "admin" ? "تیکت‌ها را اولویت‌بندی کن و پاسخ‌ها را از همین‌جا مدیریت کن." : "سفارش‌ها، تیکت‌ها و اطلاعات حساب را در یک فضای جمع‌وجور و مینیمال مدیریت کن."}

+
+ +
+ } /> + } /> + } /> + } /> +
+ + )} + + {activeTab === "tickets" && (session.role === "admin" ? renderAdminTicketsView() : renderUserTicketsView())} + + {activeTab === "orders" && `${order.id} • ${order.status} • ${order.total} تومان`)} />} + {activeTab === "wallet" && } + {activeTab === "address" && `${address.title} • ${address.text}`)} />} + {activeTab === "profile" && } +
+
+
+ + {newTicketOpen && ( +
+
setNewTicketOpen(false)}>
+ +

ثبت تیکت جدید

+
+ setNewTicketForm((prev) => ({ ...prev, subject: event.target.value }))} placeholder="موضوع تیکت" className="rounded-2xl border border-[#e7e3da] bg-[#fcfbf8] px-4 py-3 text-sm outline-none focus:border-[#d5b96b]" /> + +