'use client' import { useState, useEffect, useRef, useCallback } from 'react' import dynamic from 'next/dynamic' import { MapPin, AlertTriangle, X, Save, Maximize2, Minimize2 } from 'lucide-react' import type { OutbreakReport } from '@/lib/outbreakReport' // Dynamically import Google Maps component to avoid SSR issues const GoogleMapComponent = dynamic(() => import('./GoogleMap'), { ssr: false, loading: () => (

Loading map...

), }) interface USOutbreakMapProps { reports?: OutbreakReport[] onReportSubmit?: (report: OutbreakReport) => void /** Reporter status for new submissions from this browser */ reporterVerified: boolean } function triggerMapResize(map: google.maps.Map | null) { if (!map || typeof google === 'undefined') return window.setTimeout(() => { google.maps.event.trigger(map, 'resize') }, 120) } function getFullscreenElement(): Element | null { const doc = document as Document & { webkitFullscreenElement?: Element | null } return document.fullscreenElement ?? doc.webkitFullscreenElement ?? null } async function requestElementFullscreen(el: HTMLElement): Promise { const anyEl = el as HTMLElement & { webkitRequestFullscreen?: () => void } try { if (el.requestFullscreen) { await el.requestFullscreen() return true } } catch { /* try webkit */ } try { if (anyEl.webkitRequestFullscreen) { anyEl.webkitRequestFullscreen() return true } } catch { /* fall through */ } return false } async function exitDocumentFullscreen(): Promise { const doc = document as Document & { webkitExitFullscreen?: () => void } try { if (document.fullscreenElement && document.exitFullscreen) { await document.exitFullscreen() return } } catch { /* try webkit */ } try { if (doc.webkitExitFullscreen) { doc.webkitExitFullscreen() } } catch { /* ignore */ } } export default function USOutbreakMap({ reports = [], onReportSubmit, reporterVerified }: USOutbreakMapProps) { const [selectedLocation, setSelectedLocation] = useState<{ lat: number; lng: number } | null>(null) const [showReportForm, setShowReportForm] = useState(false) const mapCardRef = useRef(null) const mapInstanceRef = useRef(null) const [browserFullscreen, setBrowserFullscreen] = useState(false) /** iOS / browsers without element fullscreen */ const [layoutFullscreen, setLayoutFullscreen] = useState(false) const expanded = browserFullscreen || layoutFullscreen const handleMapReady = useCallback((map: google.maps.Map) => { mapInstanceRef.current = map }, []) useEffect(() => { const syncFs = () => { const fsEl = getFullscreenElement() setBrowserFullscreen(fsEl === mapCardRef.current) } syncFs() document.addEventListener('fullscreenchange', syncFs) document.addEventListener('webkitfullscreenchange', syncFs) return () => { document.removeEventListener('fullscreenchange', syncFs) document.removeEventListener('webkitfullscreenchange', syncFs) } }, []) useEffect(() => { triggerMapResize(mapInstanceRef.current) }, [expanded]) useEffect(() => { const onResize = () => triggerMapResize(mapInstanceRef.current) window.addEventListener('orientationchange', onResize) window.addEventListener('resize', onResize) return () => { window.removeEventListener('orientationchange', onResize) window.removeEventListener('resize', onResize) } }, []) const exitAllFullscreen = useCallback(async () => { if (getFullscreenElement()) { await exitDocumentFullscreen() } setLayoutFullscreen(false) }, []) const toggleFullscreen = useCallback(async () => { const el = mapCardRef.current if (!el) return if (expanded) { await exitAllFullscreen() triggerMapResize(mapInstanceRef.current) return } const enteredFs = await requestElementFullscreen(el) if (enteredFs) { triggerMapResize(mapInstanceRef.current) return } setLayoutFullscreen(true) triggerMapResize(mapInstanceRef.current) }, [expanded, exitAllFullscreen]) useEffect(() => { if (!layoutFullscreen && !browserFullscreen) return const prev = document.body.style.overflow document.body.style.overflow = 'hidden' return () => { document.body.style.overflow = prev } }, [layoutFullscreen, browserFullscreen]) useEffect(() => { if (!expanded) return const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') void exitAllFullscreen() } window.addEventListener('keydown', onKey) return () => window.removeEventListener('keydown', onKey) }, [expanded, exitAllFullscreen]) useEffect(() => { return () => { document.body.style.removeProperty('overflow') void exitDocumentFullscreen() } }, []) // Ensure modal closes on mount/unmount to prevent stuck overlays useEffect(() => { return () => { setShowReportForm(false) setSelectedLocation(null) } }, []) const [formData, setFormData] = useState({ crop: '', disease: '', severity: 'medium' as 'low' | 'medium' | 'high', description: '', }) const handleMapClick = (lat: number, lng: number) => { // Restrict to US bounds const clampedLat = Math.max(24.39, Math.min(49.38, lat)) const clampedLng = Math.max(-125, Math.min(-66.93, lng)) setSelectedLocation({ lat: clampedLat, lng: clampedLng }) setShowReportForm(true) } const handleSubmitReport = () => { if (!selectedLocation || !formData.crop || !formData.disease) { alert('Please fill in all required fields') return } const newReport: OutbreakReport = { id: Date.now().toString(), lat: selectedLocation.lat, lng: selectedLocation.lng, crop: formData.crop, disease: formData.disease, severity: formData.severity, date: new Date().toISOString(), description: formData.description, reporterVerified, } if (onReportSubmit) { onReportSubmit(newReport) } setShowReportForm(false) setSelectedLocation(null) setFormData({ crop: '', disease: '', severity: 'medium', description: '', }) } return (
{/* Map card: explicit height so the map fills the area (no aspect-ratio gap) */}
{selectedLocation && !showReportForm && (

Selected location

{selectedLocation.lat.toFixed(4)}, {selectedLocation.lng.toFixed(4)}

)}

Tap the map where you are seeing crop trouble

{/* Report Form Modal — no AnimatePresence exit on full-screen layer (opacity-0 still captures clicks). */} {showReportForm && (
{ setShowReportForm(false) setSelectedLocation(null) }} />
e.stopPropagation()} className="relative z-10 max-h-[90dvh] w-full max-w-md overflow-y-auto overscroll-contain rounded-t-2xl border border-field-soil/10 bg-white p-5 pb-[max(1.25rem,env(safe-area-inset-bottom))] shadow-2xl sm:rounded-2xl sm:p-6 sm:pb-6" >

Report crop trouble

{selectedLocation && (

Location

{selectedLocation.lat.toFixed(4)}, {selectedLocation.lng.toFixed(4)}

)}
setFormData({ ...formData, crop: e.target.value })} placeholder="Corn, wheat, rice" className="field-input py-2.5" />
setFormData({ ...formData, disease: e.target.value })} placeholder="Rust, blight, yellowing" className="field-input py-2.5" />