"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]" />