'use client' import { useState, useRef } from 'react' import { useSortable } from '@dnd-kit/sortable' import { CSS } from '@dnd-kit/utilities' import { useUpdateSite, useDeleteSite, useAdaptSite, useSiteSelectors } from '@/hooks/useSites' import { useQueryClient } from '@tanstack/react-query' import type { TargetSite } from '@/lib/types' import { cn } from '@/lib/utils' function HealthBadge({ site }: { site: TargetSite }) { const inCooldown = site.cooldown_until && new Date(site.cooldown_until) > new Date() if (inCooldown) return Cooldown if (site.consecutive_failures > 2) return {site.error_count} errors return OK } function ConfidenceBadge({ siteId }: { siteId: number }) { const { data: sel } = useSiteSelectors(siteId) if (!sel) return — const cls = sel.confidence >= 70 ? 'g-badge-green' : sel.confidence >= 40 ? 'g-badge-amber' : 'g-badge-red' return ( {sel.confidence}%{sel.stale ? ' ⚠' : ''} ) } const ADAPT_POLL_DELAY = 45_000 export default function SiteRow({ site }: { site: TargetSite }) { const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: site.id }) const updateSiteMut = useUpdateSite() const deleteSiteMut = useDeleteSite() const adaptSiteMut = useAdaptSite() const qc = useQueryClient() const [adapting, setAdapting] = useState(false) const timerRef = useRef | null>(null) const handleAdapt = () => { adaptSiteMut.mutate(site.id, { onSuccess: () => { setAdapting(true) if (timerRef.current) clearTimeout(timerRef.current) timerRef.current = setTimeout(() => { setAdapting(false) qc.invalidateQueries({ queryKey: ['selectors', site.id] }) }, ADAPT_POLL_DELAY) }, }) } const style = { transform: CSS.Transform.toString(transform), transition } const isAdapting = adaptSiteMut.isPending || adapting return ( ⋮⋮ {site.name} {site.url_template} updateSiteMut.mutate({ id: site.id, data: { enabled: site.enabled ? 0 : 1 } })} className={cn( 'relative w-8 h-4 rounded-full transition-colors duration-200 cursor-pointer flex-shrink-0', site.enabled ? 'bg-g-green/30 border border-g-green/40' : 'bg-g-raised border border-g-border' )} > {site.enabled ? 'On' : 'Off'} {isAdapting ? 'Adapting…' : 'Adapt AI'} { if (confirm(`Delete "${site.name}"?`)) deleteSiteMut.mutate(site.id) }} className="text-g-faint hover:text-g-red transition-colors text-xs opacity-0 group-hover:opacity-100" > ✕ ) }