"use client"; import { useState } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { RefreshCw, Package, Plus, Pencil, Trash2 } from "lucide-react"; import { Card, CardContent, CardHeader } from "@/components/ui/Card"; import { Table, TableHead, TableBody, TableRow, Th, Td, EmptyState } from "@/components/ui/Table"; import { Badge } from "@/components/ui/Badge"; import { Button } from "@/components/ui/Button"; import { Input } from "@/components/ui/Input"; import { Modal } from "@/components/ui/Modal"; import { formatCurrency } from "@/lib/utils"; import api from "@/lib/api"; import { toast } from "sonner"; interface Plan { id: string; name: string; type: "PREPAID" | "POSTPAID"; speedDownMbps: number; speedUpMbps: number; monthlyPrice: number; description?: string; isActive: boolean; } const typeVariant: Record = { PREPAID: "success", POSTPAID: "muted", }; const emptyForm = { name: "", type: "PREPAID" as "PREPAID" | "POSTPAID", speedDown: "", speedUp: "", monthlyPrice: "", description: "", }; export default function PlansPage() { const qc = useQueryClient(); const [search, setSearch] = useState(""); // Modals const [showCreate, setShowCreate] = useState(false); const [editPlan, setEditPlan] = useState(null); const [deletePlan, setDeletePlan] = useState(null); // Forms const [createForm, setCreateForm] = useState({ ...emptyForm }); const [editForm, setEditForm] = useState({ ...emptyForm }); // GET /api/v1/plans returns a plain array const { data: plans = [], isLoading, isError, refetch } = useQuery({ queryKey: ["plans", search], queryFn: async () => { const params = new URLSearchParams({ limit: "100" }); if (search) params.set("search", search); const res = await api.get(`/api/v1/plans?${params}`); return res.data; }, }); const createMutation = useMutation({ mutationFn: async () => { await api.post("/api/v1/plans", { name: createForm.name, type: createForm.type, speedDownMbps: Number(createForm.speedDown), speedUpMbps: Number(createForm.speedUp), monthlyPrice: Number(createForm.monthlyPrice), description: createForm.description || undefined, }); }, onSuccess: () => { toast.success("Plan created!"); setShowCreate(false); setCreateForm({ ...emptyForm }); qc.invalidateQueries({ queryKey: ["plans"] }); }, onError: (e: any) => toast.error( Array.isArray(e.response?.data?.message) ? e.response.data.message.join(", ") : (e.response?.data?.message ?? "Failed to create plan") ), }); const updateMutation = useMutation({ mutationFn: async () => { await api.patch(`/api/v1/plans/${editPlan!.id}`, { name: editForm.name, type: editForm.type, speedDownMbps: Number(editForm.speedDown), speedUpMbps: Number(editForm.speedUp), monthlyPrice: Number(editForm.monthlyPrice), description: editForm.description || undefined, }); }, onSuccess: () => { toast.success("Plan updated!"); setEditPlan(null); qc.invalidateQueries({ queryKey: ["plans"] }); }, onError: (e: any) => toast.error(e.response?.data?.message ?? "Failed to update plan"), }); const deleteMutation = useMutation({ mutationFn: async () => { await api.delete(`/api/v1/plans/${deletePlan!.id}`); }, onSuccess: () => { toast.success("Plan deleted!"); setDeletePlan(null); qc.invalidateQueries({ queryKey: ["plans"] }); }, onError: (e: any) => toast.error(e.response?.data?.message ?? "Failed to delete plan"), }); const filtered = search ? plans.filter(p => p.name.toLowerCase().includes(search.toLowerCase())) : plans; const openEdit = (plan: Plan) => { setEditForm({ name: plan.name, type: plan.type, speedDown: String(plan.speedDownMbps), speedUp: String(plan.speedUpMbps), monthlyPrice: String(plan.monthlyPrice), description: plan.description ?? "", }); setEditPlan(plan); }; return (
{/* Header */}

Plans

{filtered.length} total plans

setSearch(e.target.value)} data-testid="input-search" /> {isLoading ? ( Array.from({ length: 5 }).map((_, i) => ( )) ) : isError ? ( ) : filtered.length === 0 ? ( } /> ) : ( filtered.map(plan => ( )) )}
Name Type Speed (Down/Up) Monthly Price Description

Failed to load plans.{" "}

{plan.name} {plan.type} {plan.speedDownMbps}/{plan.speedUpMbps} Mbps {formatCurrency(plan.monthlyPrice)} {plan.description ?? "—"}
{/* Create Plan Modal */} { setShowCreate(false); setCreateForm({ ...emptyForm }); }} title="Add Plan" className="max-w-md">
setCreateForm(f => ({ ...f, name: e.target.value }))} placeholder="e.g. Basic 25Mbps" data-testid="input-plan-name" />
setCreateForm(f => ({ ...f, speedDown: e.target.value }))} placeholder="e.g. 25" data-testid="input-plan-speed-down" /> setCreateForm(f => ({ ...f, speedUp: e.target.value }))} placeholder="e.g. 10" data-testid="input-plan-speed-up" />
setCreateForm(f => ({ ...f, monthlyPrice: e.target.value }))} placeholder="e.g. 999" data-testid="input-plan-price" /> setCreateForm(f => ({ ...f, description: e.target.value }))} placeholder="e.g. Perfect for households" data-testid="input-plan-description" />
{/* Edit Plan Modal */} setEditPlan(null)} title={`Edit Plan: ${editPlan?.name ?? ""}`} className="max-w-md">
setEditForm(f => ({ ...f, name: e.target.value }))} data-testid="input-edit-name" />
setEditForm(f => ({ ...f, speedDown: e.target.value }))} data-testid="input-edit-speed-down" /> setEditForm(f => ({ ...f, speedUp: e.target.value }))} data-testid="input-edit-speed-up" />
setEditForm(f => ({ ...f, monthlyPrice: e.target.value }))} data-testid="input-edit-price" /> setEditForm(f => ({ ...f, description: e.target.value }))} data-testid="input-edit-description" />
{/* Delete Confirmation Modal */} setDeletePlan(null)} title="Delete Plan" className="max-w-sm">

Are you sure you want to delete {deletePlan?.name}? This action cannot be undone.

); }