import { useEffect, useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Badge } from "@/components/ui/badge"; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Plus, Trash2, Copy, Loader2 } from "lucide-react"; import AiTipBanner from "@/components/ai/AiTipBanner"; import { useToast } from "@/hooks/use-toast"; import { platformSettingsService, type GradingConfig, type PlatformPackage, type RegistrationCode, } from "@/services/platformSettings.service"; export default function SettingsPage() { const { toast } = useToast(); const qc = useQueryClient(); // ── Codes ────────────────────────────────────────────────────────── const codesQ = useQuery({ queryKey: ["codes"], queryFn: () => platformSettingsService.listCodes(), }); const codes: RegistrationCode[] = codesQ.data?.items ?? []; const genMut = useMutation({ mutationFn: platformSettingsService.generateCodes, onSuccess: (r) => { qc.invalidateQueries({ queryKey: ["codes"] }); toast({ title: `Generated ${r.count} code(s)` }); }, onError: () => toast({ title: "Failed to generate codes", variant: "destructive" }), }); const delCodeMut = useMutation({ mutationFn: platformSettingsService.deleteCode, onSuccess: () => { qc.invalidateQueries({ queryKey: ["codes"] }); toast({ title: "Code deleted" }); }, }); // ── Packages ─────────────────────────────────────────────────────── const pkgQ = useQuery({ queryKey: ["packages"], queryFn: () => platformSettingsService.listPackages(), }); const packages: PlatformPackage[] = pkgQ.data?.items ?? []; const [pkgOpen, setPkgOpen] = useState(false); const [pkgForm, setPkgForm] = useState>({ name: "", price: 0, duration: "1 month", discount: 0, }); const createPkgMut = useMutation({ mutationFn: platformSettingsService.createPackage, onSuccess: () => { qc.invalidateQueries({ queryKey: ["packages"] }); setPkgOpen(false); setPkgForm({ name: "", price: 0, duration: "1 month", discount: 0 }); toast({ title: "Package created" }); }, }); const delPkgMut = useMutation({ mutationFn: platformSettingsService.deletePackage, onSuccess: () => { qc.invalidateQueries({ queryKey: ["packages"] }); toast({ title: "Package deleted" }); }, }); // ── Grading config ───────────────────────────────────────────────── const gradingQ = useQuery({ queryKey: ["grading-config"], queryFn: () => platformSettingsService.getGrading(), }); const [grading, setGrading] = useState({ min_score: 0, max_score: 9, increment: 0.5, }); useEffect(() => { if (gradingQ.data) { setGrading({ min_score: gradingQ.data.min_score, max_score: gradingQ.data.max_score, increment: gradingQ.data.increment, }); } }, [gradingQ.data]); const saveGradingMut = useMutation({ mutationFn: platformSettingsService.setGrading, onSuccess: () => { qc.invalidateQueries({ queryKey: ["grading-config"] }); toast({ title: "Grading configuration saved" }); }, onError: (err: unknown) => { const msg = err && typeof err === "object" && "message" in err ? String((err as { message: unknown }).message) : "Failed to save"; toast({ title: msg, variant: "destructive" }); }, }); return (

Settings

Manage registration codes, packages, and the grading scale.

Codes Packages & Discounts Grading System {/* CODES */}
Code Type User Uses State Created {codesQ.isLoading && ( )} {!codesQ.isLoading && codes.length === 0 && ( No codes yet. Click Generate Single to create one. )} {codes.map((c) => ( {c.code} {c.code_type} {c.user_type} {c.uses}/{c.max_uses} {c.used ? "Used" : "Available"} {c.created ? new Date(c.created).toLocaleDateString() : "—"} ))}
{/* PACKAGES */}
{pkgQ.isLoading && } {packages.map((p) => ( {p.name}

${p.price}

{p.duration}

{p.discount > 0 && ( {p.discount}% OFF )}
))}
New Package
setPkgForm((f) => ({ ...f, name: e.target.value })) } />
setPkgForm((f) => ({ ...f, price: Number(e.target.value), })) } />
setPkgForm((f) => ({ ...f, discount: Number(e.target.value), })) } />
{/* GRADING */} Scoring Scale
setGrading((g) => ({ ...g, min_score: Number(e.target.value), })) } />
setGrading((g) => ({ ...g, max_score: Number(e.target.value), })) } />
setGrading((g) => ({ ...g, increment: Number(e.target.value), })) } />
); }