"use client"; /** * TEE OFF ADMIN DASHBOARD v1.7 - RESPONSIVT MED AI-HVISKER & KILL SWITCH * --------------------------------------------------------------------------- * PLASSERING: frontend/src/app/admin/page.tsx * FUNKSJON: Live-oppdatering, massevalg, redigering og SAMMENTREKKBAR meny. * --------------------------------------------------------------------------- */ import { useState, useEffect } from 'react'; import { API_URL } from "@/config/constants"; import ScrapeMethodSelect from "@/components/ScrapeMethodSelect"; export default function AdminDashboard() { const [facilities, setFacilities] = useState([]); const [loading, setLoading] = useState(true); const [selectedFacilities, setSelectedFacilities] = useState([]); const [isScraping, setIsScraping] = useState(false); // NY: State for å sjekke om sidebaren er klappet sammen const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false); const [editingFacility, setEditingFacility] = useState(null); // OPPDATERT: Lagt til ai_instruction og courses for manuell overstyring const [editForm, setEditForm] = useState({ scrape_status_url: '', scrape_status_selector: '', scrape_method: '', ai_instruction: '', courses: [] as any[] }); const [isSaving, setIsSaving] = useState(false); const fetchFacilities = () => { fetch(`${API_URL}/facilities`) .then(res => res.json()) .then(data => { setFacilities(Array.isArray(data) ? data : []); setLoading(false); }) .catch(() => setLoading(false)); }; useEffect(() => { fetchFacilities(); }, []); useEffect(() => { let interval: NodeJS.Timeout; if (isScraping) { interval = setInterval(() => { fetchFacilities(); }, 10000); } return () => clearInterval(interval); }, [isScraping]); const handleSelectAll = (e: React.ChangeEvent) => { if (e.target.checked) { setSelectedFacilities(facilities.map(f => f.id)); } else { setSelectedFacilities([]); } }; const handleSelectOne = (id: number, checked: boolean) => { if (checked) { setSelectedFacilities([...selectedFacilities, id]); } else { setSelectedFacilities(selectedFacilities.filter(facilityId => facilityId !== id)); } }; const handleRunScrapers = async () => { // Hvis den allerede skraper, lar vi brukeren trykke på knappen for å avbryte UI-oppdateringen if (isScraping) { setIsScraping(false); return; } setIsScraping(true); try { const response = await fetch(`${API_URL}/admin/run-scraper`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ facility_ids: selectedFacilities }) }); if (!response.ok) throw new Error("Kunne ikke starte skraping"); // Beregn dynamisk timeout: 40 sekunder per valgte anlegg (Minimum 1 minutt) const timeoutMs = Math.max(selectedFacilities.length * 40 * 1000, 60000); setSelectedFacilities([]); setTimeout(() => setIsScraping(false), timeoutMs); } catch (error) { console.error(error); alert("Feil ved start av skraperen."); setIsScraping(false); } }; // OPPDATERT: Laster inn eksisterende banestatuser og ai_instruction const openEditModal = (facility: any) => { setEditingFacility(facility); setEditForm({ scrape_status_url: facility.scrape_status_url || '', scrape_status_selector: facility.scrape_status_selector || '', scrape_method: facility.scrape_method || 'css_selector', ai_instruction: facility.ai_instruction || '', courses: facility.course_statuses ? facility.course_statuses.map((c: any) => ({id: c.id, name: c.name, status: c.status})) : [] }); }; const handleSaveEdit = async () => { setIsSaving(true); try { const response = await fetch(`${API_URL}/admin/facilities/${editingFacility.id}/scrape-settings`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(editForm) }); if (!response.ok) throw new Error("Feil ved lagring"); setEditingFacility(null); fetchFacilities(); } catch (error) { alert("Kunne ikke lagre endringene."); console.error(error); } finally { setIsSaving(false); } }; if (loading) return
LASTER DASHBORD...
; return (
{/* REDIGER-MODAL */} {editingFacility && (

Rediger Konfigurasjon

{editingFacility.name}

setEditForm({...editForm, scrape_status_url: e.target.value})} className="w-full border-2 border-gray-100 rounded-xl p-3 text-sm focus:border-[#8bc34a] focus:outline-none transition-colors" placeholder="f.eks. https://golfklubb.no/banestatus" />
{/* Boks som vises hvis Gemini AI er valgt */} {editForm.scrape_method === 'llm_parse' && (