'use client'; import { useState } from 'react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { Plus, Trash2, ChevronUp, ChevronDown, Save } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Switch } from '@/components/ui/switch'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { apiFetch } from '@/lib/api/client'; import { toastError } from '@/lib/api/toast-error'; import { cn } from '@/lib/utils'; interface CriterionRow { id: string; key: string; label: string; description: string | null; enabled: boolean; displayOrder: number; } interface ListResponse { data: CriterionRow[]; } /** * Per-port qualification-criteria admin. Lists current criteria, add via * the dialog, toggle enabled inline, drag-style reorder via up/down buttons * (keeps the UI simple for v1; can swap to a real DnD later if reps want it). */ export function QualificationCriteriaAdmin() { const queryClient = useQueryClient(); const [createOpen, setCreateOpen] = useState(false); const { data, isLoading } = useQuery({ queryKey: ['qualification-criteria'], queryFn: () => apiFetch('/api/v1/admin/qualification-criteria'), }); const criteria = data?.data ?? []; const toggleEnabled = useMutation({ mutationFn: async (vars: { id: string; enabled: boolean }) => apiFetch(`/api/v1/admin/qualification-criteria/${vars.id}`, { method: 'PATCH', body: { enabled: vars.enabled }, }), onSuccess: () => queryClient.invalidateQueries({ queryKey: ['qualification-criteria'] }), onError: (err) => toastError(err), }); const reorder = useMutation({ mutationFn: async (vars: { id: string; displayOrder: number }) => apiFetch(`/api/v1/admin/qualification-criteria/${vars.id}`, { method: 'PATCH', body: { displayOrder: vars.displayOrder }, }), onSuccess: () => queryClient.invalidateQueries({ queryKey: ['qualification-criteria'] }), onError: (err) => toastError(err), }); const deleteCriterion = useMutation({ mutationFn: async (id: string) => apiFetch(`/api/v1/admin/qualification-criteria/${id}`, { method: 'DELETE' }), onSuccess: () => queryClient.invalidateQueries({ queryKey: ['qualification-criteria'] }), onError: (err) => toastError(err), }); if (isLoading) { return
Loading criteria…
; } return (

{criteria.length} criteria configured · {criteria.filter((c) => c.enabled).length} enabled

{criteria.length === 0 ? (

No criteria configured yet.

Add the first criterion the rep needs to confirm before a deal can be qualified.

) : ( )}
); } function CriterionEditableRow({ criterion, onToggleEnabled, }: { criterion: CriterionRow; onToggleEnabled: (enabled: boolean) => void; }) { const queryClient = useQueryClient(); const [label, setLabel] = useState(criterion.label); const [description, setDescription] = useState(criterion.description ?? ''); const isDirty = label.trim() !== criterion.label || (description.trim() || null) !== criterion.description; const save = useMutation({ mutationFn: async () => apiFetch(`/api/v1/admin/qualification-criteria/${criterion.id}`, { method: 'PATCH', body: { label: label.trim(), description: description.trim() || null }, }), onSuccess: () => queryClient.invalidateQueries({ queryKey: ['qualification-criteria'] }), onError: (err) => toastError(err), }); return (
setLabel(e.target.value)} className="h-7 max-w-md text-sm font-medium" /> {criterion.key}
{isDirty ? ( ) : null}