"use client"; import { useState } from "react"; import { useQuery, useMutation } from "@tanstack/react-query"; import { toast } from "sonner"; import { Building2, CreditCard, Map, Wifi, Users, ChevronRight, } from "lucide-react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/Card"; import { Button } from "@/components/ui/Button"; import { Input } from "@/components/ui/Input"; import { Table, TableHead, TableBody, TableRow, Th, Td, EmptyState } from "@/components/ui/Table"; import { Badge } from "@/components/ui/Badge"; import { Modal } from "@/components/ui/Modal"; import { formatCurrency } from "@/lib/utils"; import api from "@/lib/api"; // ─── Types ───────────────────────────────────────────────────────────────────── interface Tenant { id: string; name: string; slug: string; address?: string; phone?: string; email?: string; settings?: TenantBillingSettings | null; } interface TenantBillingSettings { billingDay?: number; lateFeeAmount?: number; lateFeeGraceDays?: number; } interface Plan { id: string; name: string; description?: string; type: string; speedDownMbps: number; speedUpMbps: number; monthlyPrice: number | string; isActive: boolean; } // ─── Sub-page: Tenant ───────────────────────────────────────────────────────── function TenantSettings() { const [name, setName] = useState(""); const [address, setAddress] = useState(""); const [email, setEmail] = useState(""); const [phone, setPhone] = useState(""); const [loaded, setLoaded] = useState(false); const { isLoading } = useQuery({ queryKey: ["tenant-me"], queryFn: async () => { const res = await api.get("/api/v1/tenants/me"); return res.data; }, select: (data) => { if (!loaded) { setName(data.name ?? ""); setAddress(data.address ?? ""); setEmail(data.email ?? ""); setPhone(data.phone ?? ""); setLoaded(true); } return data; }, }); const saveMutation = useMutation({ mutationFn: async () => { await api.patch("/api/v1/tenants/me", { name, address, email, phone }); }, onSuccess: () => toast.success("Tenant settings saved"), onError: () => toast.error("Failed to save. Endpoint may not be available yet."), }); if (isLoading) { return
; } return ( Business Information
{ e.preventDefault(); saveMutation.mutate(); }} className="space-y-4 max-w-lg" > setName(e.target.value)} /> setAddress(e.target.value)} /> setEmail(e.target.value)} /> setPhone(e.target.value)} /> {saveMutation.isError && (

Save endpoint not available yet — changes not persisted.

)}
); } // ─── Sub-page: Billing Settings ─────────────────────────────────────────────── function BillingSettings() { const [billingDay, setBillingDay] = useState("1"); const [lateFeeAmount, setLateFeeAmount] = useState("0"); const [graceDays, setGraceDays] = useState("0"); const [loaded, setLoaded] = useState(false); const { isLoading } = useQuery({ queryKey: ["tenant-billing-settings"], queryFn: async () => { try { const res = await api.get("/api/v1/tenants/me/settings"); return res.data ?? {}; } catch { return {}; } }, select: (data) => { if (!loaded && data) { setBillingDay(String(data.billingDay ?? 1)); setLateFeeAmount(String(data.lateFeeAmount ?? 0)); setGraceDays(String(data.lateFeeGraceDays ?? 0)); setLoaded(true); } return data; }, }); const saveMutation = useMutation({ mutationFn: async () => { await api.patch("/api/v1/tenants/me/settings", { billingDay: parseInt(billingDay), lateFeeAmount: parseFloat(lateFeeAmount), lateFeeGraceDays: parseInt(graceDays), }); }, onSuccess: () => toast.success("Billing settings saved"), onError: () => toast.error("Failed to save. Endpoint may not be available yet."), }); if (isLoading) { return
; } return ( Billing Configuration
{ e.preventDefault(); saveMutation.mutate(); }} className="space-y-4 max-w-lg" > setBillingDay(e.target.value)} hint="Day of month invoices are generated" /> setLateFeeAmount(e.target.value)} /> setGraceDays(e.target.value)} hint="Days after due date before late fee applies" /> {saveMutation.isError && (

Save endpoint not available yet — changes not persisted.

)}
); } // ─── Sub-page: Areas & Zones ────────────────────────────────────────────────── interface Area { id: string; name: string; zones?: Zone[]; } interface Zone { id: string; name: string; areaId: string; } function AreasSettings() { const [showAddArea, setShowAddArea] = useState(false); const [showAddZone, setShowAddZone] = useState(false); const [areaName, setAreaName] = useState(""); const [zoneName, setZoneName] = useState(""); const [zoneAreaId, setZoneAreaId] = useState(""); const { data: areas, isLoading, refetch } = useQuery({ queryKey: ["areas"], queryFn: async () => { try { const res = await api.get("/api/v1/areas"); const d = res.data; return Array.isArray(d) ? d : (d as { data: Area[] }).data ?? []; } catch { return []; } }, }); const addAreaMutation = useMutation({ mutationFn: async () => { await api.post("/api/v1/areas", { name: areaName }); }, onSuccess: () => { toast.success("Area added"); setAreaName(""); setShowAddArea(false); refetch(); }, onError: () => toast.error("Failed to add area"), }); const addZoneMutation = useMutation({ mutationFn: async () => { await api.post("/api/v1/zones", { name: zoneName, areaId: zoneAreaId }); }, onSuccess: () => { toast.success("Zone added"); setZoneName(""); setZoneAreaId(""); setShowAddZone(false); refetch(); }, onError: () => toast.error("Failed to add zone"), }); const areaList = areas ?? []; return (
Areas & Zones
{isLoading ? ( Array.from({ length: 3 }).map((_, i) => ( )) ) : areaList.length === 0 ? ( ) : ( areaList.map((a) => ( )) )}
Area Name Zones
{a.name} {a.zones?.length ? a.zones.map((z) => z.name).join(", ") : No zones}
{/* Add Area Modal */} setShowAddArea(false)} title="Add Area">
{ e.preventDefault(); addAreaMutation.mutate(); }} className="space-y-4"> setAreaName(e.target.value)} placeholder="e.g. North Sector" />
{/* Add Zone Modal */} setShowAddZone(false)} title="Add Zone">
{ e.preventDefault(); addZoneMutation.mutate(); }} className="space-y-4">
setZoneName(e.target.value)} placeholder="e.g. Zone 1" />
); } // ─── Sub-page: Plans ────────────────────────────────────────────────────────── function PlansSettings() { const [showAdd, setShowAdd] = useState(false); const [planName, setPlanName] = useState(""); const [planType, setPlanType] = useState("POSTPAID"); const [speedDown, setSpeedDown] = useState(""); const [speedUp, setSpeedUp] = useState(""); const [price, setPrice] = useState(""); const [description, setDescription] = useState(""); const { data, isLoading, refetch } = useQuery({ queryKey: ["plans"], queryFn: async () => { try { const res = await api.get("/api/v1/plans"); const d = res.data; return Array.isArray(d) ? d : (d as { data: Plan[] }).data ?? []; } catch { return []; } }, }); const addPlanMutation = useMutation({ mutationFn: async () => { await api.post("/api/v1/plans", { name: planName, type: planType, speedDownMbps: parseInt(speedDown), speedUpMbps: parseInt(speedUp), monthlyPrice: parseFloat(price), description: description || undefined, }); }, onSuccess: () => { toast.success("Plan created"); resetAdd(); setShowAdd(false); refetch(); }, onError: () => toast.error("Failed to create plan"), }); const toggleActiveMutation = useMutation({ mutationFn: async ({ id, isActive }: { id: string; isActive: boolean }) => { await api.patch(`/api/v1/plans/${id}`, { isActive: !isActive }); }, onSuccess: () => { toast.success("Plan updated"); refetch(); }, onError: () => toast.error("Failed to update plan"), }); function resetAdd() { setPlanName(""); setPlanType("POSTPAID"); setSpeedDown(""); setSpeedUp(""); setPrice(""); setDescription(""); } const plans = data ?? []; return (
Plans
{isLoading ? ( Array.from({ length: 3 }).map((_, i) => ( {[1,2,3,4,5,6].map((j) => ( ))} )) ) : plans.length === 0 ? ( ) : ( plans.map((p) => ( )) )}
Name Type Speed Price Status Actions
{p.name} {p.type} {p.speedDownMbps}/{p.speedUpMbps} Mbps {formatCurrency(Number(p.monthlyPrice))} {p.isActive ? "Active" : "Inactive"}
{/* Add Plan Modal */} { setShowAdd(false); resetAdd(); }} title="Add Plan" className="max-w-lg">
{ e.preventDefault(); addPlanMutation.mutate(); }} className="space-y-4"> setPlanName(e.target.value)} placeholder="e.g. Basic 10 Mbps" />
setSpeedDown(e.target.value)} /> setSpeedUp(e.target.value)} />
setPrice(e.target.value)} /> setDescription(e.target.value)} />
); } // ─── Sub-page: Users ────────────────────────────────────────────────────────── interface UserItem { id: string; firstName: string; lastName: string; email: string; isActive: boolean; } function UsersSettings() { const { data, isLoading } = useQuery({ queryKey: ["users-settings"], queryFn: async () => { const res = await api.get("/api/v1/users"); return Array.isArray(res.data) ? res.data : []; }, }); const users = data ?? []; return ( Users {isLoading ? ( Array.from({ length: 3 }).map((_, i) => ( {[1,2,3].map((j) => )} )) ) : users.length === 0 ? ( ) : ( users.map((u) => ( )) )}
Name Email Status
{u.firstName} {u.lastName} {u.email} {u.isActive ? "Active" : "Inactive"}
); } // ─── Main Page ───────────────────────────────────────────────────────────────── const SUB_NAV = [ { key: "tenant", label: "Tenant", icon: Building2 }, { key: "billing", label: "Billing", icon: CreditCard }, { key: "areas", label: "Areas & Zones", icon: Map }, { key: "plans", label: "Plans", icon: Wifi }, { key: "users", label: "Users", icon: Users }, ] as const; type SubPage = typeof SUB_NAV[number]["key"]; export default function SettingsPage() { const [active, setActive] = useState("tenant"); return (

Settings

Manage your ISP configuration

{/* Left sub-nav */} {/* Content */}
{active === "tenant" && } {active === "billing" && } {active === "areas" && } {active === "plans" && } {active === "users" && }
); }