"use client"; import { useState } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { useParams, useRouter } from "next/navigation"; import { ArrowLeft, Wifi, FileText, Ticket, CreditCard, RefreshCw } from "lucide-react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/Card"; import { Badge } from "@/components/ui/Badge"; import { Button } from "@/components/ui/Button"; import { Modal } from "@/components/ui/Modal"; import { Input } from "@/components/ui/Input"; import { Table, TableHead, TableBody, TableRow, Th, Td, EmptyState } from "@/components/ui/Table"; import { formatDate, formatCurrency } from "@/lib/utils"; import api from "@/lib/api"; import { toast } from "sonner"; import type { Client, Subscription, Invoice, Ticket as TicketType, Payment, LegacyPaginatedResponse } from "@/types"; type Tab = "profile" | "subscriptions" | "invoices" | "payments" | "tickets"; const statusColor: Record = { ACTIVE: "success", active: "success", SUSPENDED: "warning", suspended: "warning", CANCELLED: "danger", cancelled: "danger", DISCONNECTED: "danger", PENDING: "muted", pending: "muted", }; const invColor: Record = { PAID: "success", paid: "success", PARTIAL: "warning", partial: "warning", OVERDUE: "danger", overdue: "danger", SENT: "muted", DRAFT: "muted", VOID: "muted", }; const channelColors: Record = { CASH: "bg-green-100 text-green-700", GCASH: "bg-blue-100 text-blue-700", MAYA: "bg-purple-100 text-purple-700", BANK_TRANSFER: "bg-yellow-100 text-yellow-700", CHECK: "bg-gray-100 text-gray-700", }; export default function ClientDetailPage() { const { id } = useParams<{ id: string }>(); const router = useRouter(); const qc = useQueryClient(); const [activeTab, setActiveTab] = useState("profile"); const [payInvoice, setPayInvoice] = useState(null); const [payForm, setPayForm] = useState({ amount: "", channel: "CASH", referenceNumber: "", notes: "" }); const { data: client, isLoading, refetch: refetchClient } = useQuery({ queryKey: ["client", id], queryFn: async () => { const r = await api.get(`/api/v1/clients/${id}`); return r.data; }, }); const { data: subscriptions, isError: subsError } = useQuery({ queryKey: ["client-subscriptions", id], queryFn: async () => { const r = await api.get(`/api/v1/clients/${id}/subscriptions`); return r.data; }, enabled: activeTab === "subscriptions", }); const { data: invoicesData, refetch: refetchInvoices } = useQuery>({ queryKey: ["client-invoices", id], queryFn: async () => { const r = await api.get>(`/api/v1/invoices?clientId=${id}&page=1&limit=30`); return r.data; }, enabled: activeTab === "invoices", }); const { data: paymentsData } = useQuery>({ queryKey: ["client-payments", id], queryFn: async () => { const r = await api.get>(`/api/v1/payments?clientId=${id}&page=1&limit=30`); return r.data; }, enabled: activeTab === "payments", }); const { data: ticketsData } = useQuery>({ queryKey: ["client-tickets", id], queryFn: async () => { const r = await api.get>(`/api/v1/tickets?clientId=${id}&page=1&limit=20`); return r.data; }, enabled: activeTab === "tickets", }); const recordPayment = useMutation({ mutationFn: async () => { await api.post("/api/v1/payments", { clientId: id, invoiceId: payInvoice?.id, amount: Number(payForm.amount), channel: payForm.channel, referenceNumber: payForm.referenceNumber || undefined, notes: payForm.notes || undefined, paymentDate: new Date().toISOString(), }); }, onSuccess: () => { toast.success("Payment recorded!"); setPayInvoice(null); setPayForm({ amount: "", channel: "CASH", referenceNumber: "", notes: "" }); qc.invalidateQueries({ queryKey: ["client-invoices", id] }); qc.invalidateQueries({ queryKey: ["client-payments", id] }); refetchInvoices(); }, onError: (e: any) => toast.error(e.response?.data?.message ?? "Payment failed"), }); const tabs = [ { key: "profile" as Tab, label: "Profile" }, { key: "subscriptions" as Tab, label: "Subscriptions" }, { key: "invoices" as Tab, label: "Invoices" }, { key: "payments" as Tab, label: "Payments" }, { key: "tickets" as Tab, label: "Tickets" }, ]; if (isLoading) return (
); if (!client) return (

Client not found

); const sub = client.subscriptions?.[0]; const clientStatus = sub?.status ?? (client.isActive ? "ACTIVE" : "INACTIVE"); return (
{/* Header */}

{client.firstName} {client.lastName}

{client.accountNumber} · {client.area?.name ?? "No area"}

{clientStatus}
{/* Tabs */}
{tabs.map(tab => ( ))}
{/* Profile */} {activeTab === "profile" && ( Client Profile
{[ { label: "Account Number", value: client.accountNumber }, { label: "Full Name", value: `${client.firstName} ${client.lastName}` }, { label: "Email", value: client.email || "—" }, { label: "Phone", value: client.phone }, { label: "Address", value: client.address || "—" }, { label: "Area", value: client.area?.name || "—" }, { label: "Status", value: client.isActive ? "Active" : "Inactive" }, { label: "Joined", value: formatDate(client.createdAt) }, ].map(({ label, value }) => (
{label}
{value}
))}
)} {/* Subscriptions */} {activeTab === "subscriptions" && ( Subscriptions {!subscriptions || subscriptions.length === 0 ? : subscriptions.map(sub => ( )) }
PlanTypeStatusStart DateMonthly Rate {sub.plan?.name ?? "—"} {sub.type ?? sub.plan?.type ?? "—"} {sub.status} {formatDate(sub.startDate)} {formatCurrency(Number(sub.monthlyPrice ?? sub.plan?.monthlyPrice ?? 0))}
)} {/* Invoices */} {activeTab === "invoices" && ( Invoices {!invoicesData?.data || invoicesData.data.length === 0 ? : invoicesData.data.map((inv: any) => ( )) }
Invoice #TotalBalanceDue DateStatus {inv.invoiceNumber ?? inv.id.slice(0, 8)} {formatCurrency(Number(inv.total ?? inv.amount ?? 0))} 0 ? "text-red-600 font-medium" : "text-gray-500"}> {formatCurrency(Number(inv.balance ?? 0))} {inv.dueDate ? formatDate(inv.dueDate) : "—"} {inv.status} {["SENT", "PARTIAL", "OVERDUE"].includes(inv.status) && ( )}
)} {/* Payments */} {activeTab === "payments" && ( Payment History {!paymentsData?.data || paymentsData.data.length === 0 ? : paymentsData.data.map((p: any) => ( )) }
DateAmountChannelReferenceNotes {p.paymentDate ? formatDate(p.paymentDate) : formatDate(p.createdAt)} {formatCurrency(Number(p.amount))} {p.channel} {p.referenceNumber ?? p.orNumber ?? "—"} {p.notes ?? "—"}
)} {/* Tickets */} {activeTab === "tickets" && ( Tickets {!ticketsData?.data || ticketsData.data.length === 0 ? : ticketsData.data.map(t => ( )) }
SubjectTypePriorityStatusCreated {t.subject} {t.type} {t.priority} {t.status} {formatDate(t.createdAt)}
)} {/* Pay Invoice Modal */} setPayInvoice(null)} title={`Record Payment — ${payInvoice?.invoiceNumber ?? ""}`}> {payInvoice && (
Invoice Total{formatCurrency(Number((payInvoice as any).total ?? payInvoice.amount))}
Amount Paid{formatCurrency(Number((payInvoice as any).amountPaid ?? 0))}
Balance Due{formatCurrency(Number((payInvoice as any).balance ?? payInvoice.amount))}
setPayForm(f => ({ ...f, amount: e.target.value }))} />
setPayForm(f => ({ ...f, referenceNumber: e.target.value }))} /> setPayForm(f => ({ ...f, notes: e.target.value }))} />
)}
); }