From dea0e1ad881a9b1827a0b64af5fce6544e4cf246 Mon Sep 17 00:00:00 2001 From: gitea Date: Fri, 21 Aug 2026 21:54:14 +0000 Subject: [PATCH] feat(ui): add debug mode toggle, delete hotel button, and copy hotel to trip modal --- src/app/page.tsx | 316 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 305 insertions(+), 11 deletions(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index 4abe146..baee5d2 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -130,6 +130,32 @@ export default function TravelDashboard() { const [isSubmitting, setIsSubmitting] = useState(false); const [showParkedModal, setShowParkedModal] = useState(false); + // Scraper Debug-Modus Toggle + const [isDebugMode, setIsDebugMode] = useState(false); + + // Initialisiere Debug-Modus aus localStorage im Client + useEffect(() => { + if (typeof window !== 'undefined') { + setIsDebugMode(localStorage.getItem('scraper_debug_mode') === 'true'); + } + }, []); + + const toggleDebugMode = () => { + const next = !isDebugMode; + setIsDebugMode(next); + if (typeof window !== 'undefined') { + localStorage.setItem('scraper_debug_mode', String(next)); + } + }; + + // Hotel Löschen Status + const [deletingHotelId, setDeletingHotelId] = useState(null); + + // Hotel in Trip kopieren Modal & Status + const [copyHotelTarget, setCopyHotelTarget] = useState(null); + const [selectedTargetTripId, setSelectedTargetTripId] = useState(''); + const [isCopyingHotel, setIsCopyingHotel] = useState(false); + // Mitreisende Modal const [showMembersModal, setShowMembersModal] = useState(false); const [newMemberName, setNewMemberName] = useState(''); @@ -284,13 +310,14 @@ export default function TravelDashboard() { const scanSingleHotel = async (hotel: Hotel) => { const reviewCountDisplay = hotel.review_summary_count || hotel.review_count || 80; const isConfirmed = confirm( - `Möchtest du die KI-Review-Analyse für "${hotel.name}" wirklich neu starten?\n\nHinweis: Dabei werden bis zu ${reviewCountDisplay} Gästebewertungen gescrapt und per n8n/Ollama neu zusammengefasst.` + `Möchtest du die KI-Review-Analyse für "${hotel.name}" wirklich neu starten?\n\nHinweis: Dabei werden bis zu ${reviewCountDisplay} Gästebewertungen gescrapt und per n8n/Ollama neu zusammengefasst.${isDebugMode ? '\n\n🐛 Scraper-Debug-Modus ist AKTIV (HTML & Screenshots werden gespeichert).' : ''}` ); if (!isConfirmed) return; setScanningHotelId(hotel.id); try { - const res = await fetch(`/api/hotels/${hotel.id}/scan`, { method: 'POST' }); + const debugQuery = isDebugMode ? '?debug=true' : ''; + const res = await fetch(`/api/hotels/${hotel.id}/scan${debugQuery}`, { method: 'POST' }); const data = await res.json(); alert(data.message || (data.success ? 'Scrape-Job erfolgreich gestartet!' : 'Fehler beim Starten')); setTimeout(loadData, 5000); @@ -304,7 +331,8 @@ export default function TravelDashboard() { const refreshHotelMetadata = async (hotel: Hotel) => { setRefreshingMetadataHotelId(hotel.id); try { - const res = await fetch(`/api/hotels/${hotel.id}/scan?mode=stammdaten`, { method: 'POST' }); + const debugQuery = isDebugMode ? '&debug=true' : ''; + const res = await fetch(`/api/hotels/${hotel.id}/scan?mode=stammdaten${debugQuery}`, { method: 'POST' }); const data = await res.json(); alert(data.message || (data.success ? 'Stammdaten-Scan gestartet!' : 'Fehler beim Starten')); setTimeout(loadData, 4000); @@ -315,6 +343,62 @@ export default function TravelDashboard() { } }; + const handleDeleteHotel = async (hotel: Hotel) => { + const isConfirmed = confirm(`Möchtest du das Hotel "${hotel.name}" wirklich unwiderruflich löschen?`); + if (!isConfirmed) return; + + setDeletingHotelId(hotel.id); + try { + const res = await fetch(`/api/hotels/${hotel.id}`, { method: 'DELETE' }); + const data = await res.json(); + if (data.success) { + if (selectedDetailHotel?.id === hotel.id) { + setSelectedDetailHotel(null); + } + setHotels(prev => prev.filter(h => h.id !== hotel.id)); + alert(`Hotel "${hotel.name}" wurde erfolgreich gelöscht.`); + } else { + alert('Fehler beim Löschen: ' + data.error); + } + } catch (err: any) { + alert('Fehler beim Löschen: ' + err.message); + } finally { + setDeletingHotelId(null); + } + }; + + const handleCopyHotelToTrip = async (e: React.FormEvent) => { + e.preventDefault(); + if (!copyHotelTarget || !selectedTargetTripId) return; + + setIsCopyingHotel(true); + try { + const res = await fetch('/api/hotels', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + name: copyHotelTarget.name, + booking_url: copyHotelTarget.booking_url, + trip_id: selectedTargetTripId + }) + }); + const data = await res.json(); + if (data.success) { + const targetTripName = trips.find(t => t.id === selectedTargetTripId)?.name || 'dem Trip'; + alert(`Hotel "${copyHotelTarget.name}" wurde erfolgreich zu "${targetTripName}" hinzugefügt!`); + setCopyHotelTarget(null); + setSelectedTargetTripId(''); + loadData(); + } else { + alert('Fehler beim Hinzufügen: ' + data.error); + } + } catch (err: any) { + alert('Fehler beim Hinzufügen: ' + err.message); + } finally { + setIsCopyingHotel(false); + } + }; + // ========================================== // HANDLERS: MANUELLE BAD-BILDER // ========================================== @@ -743,6 +827,61 @@ export default function TravelDashboard() { > {scanningAllPrices ? '🔄 Scanne...' : '📊 Preise aktualisieren'} + + {/* DEBUG SLIDER SWITCH */} +