import { useEffect, useState } from 'react'; import { api } from '../../api/client'; import usePushSubscription from '../../hooks/usePushSubscription'; import './SpotPanel.css'; function scoreLabel(value, level, distance) { if (level === 'high') return 'Impact'; if (level === 'medium') return 'Risque'; if (value >= 15 && distance != null && distance < 100) return 'Veille'; return 'OK'; } const TYPE_ICON = { beach: '🏖', port: '⚓', surf: '🏄', fishing: '🎣' }; const TREND_CONFIG = { increasing: { icon: '📈', label: 'Situation en dégradation', cls: 'trend--bad' }, stable: { icon: '➡️', label: 'Situation stable', cls: 'trend--neutral' }, decreasing: { icon: '📉', label: 'Amélioration en cours', cls: 'trend--good' }, }; const ETA_BUFFER_KM = 5; function computeEta(score, horizons) { if (score?.distance != null && score.distance < ETA_BUFFER_KM) return 0; for (const h of [6, 12, 24, 48]) { const horizon = horizons?.[`h${h}`]; if (horizon?.distanceKm != null && horizon.distanceKm < ETA_BUFFER_KM) return h; } return null; } function etaLabel(etaHours, score, distance) { if (etaHours === 0) return { icon: '🚨', label: 'Impact en cours', cls: 'eta--now' }; if (etaHours === 6) return { icon: '⚠️', label: 'Impact prévu dans ~6h', cls: 'eta--soon' }; if (etaHours === 12) return { icon: '⚠️', label: 'Impact prévu dans ~12h', cls: 'eta--soon' }; if (etaHours === 24) return { icon: '🕐', label: 'Impact prévu dans ~24h', cls: 'eta--later' }; if (etaHours === 48) return { icon: '🕐', label: 'Impact prévu dans ~48h', cls: 'eta--later' }; if ((score ?? 0) >= 15 && distance != null && distance < 100) return { icon: '👁', label: 'Sargasses en surveillance', cls: 'eta--watch' }; return { icon: '✅', label: 'Aucun impact prévu (48h)', cls: 'eta--safe' }; } const HORIZON_LABELS = { now: 'Maintenant', h6: '+6h', h12: '+12h', h24: '+24h', h48: '+48h' }; export default function SpotPanel({ spot, activeStep, onClose }) { const [data, setData] = useState(undefined); const [error, setError] = useState(null); useEffect(() => { if (!spot) return; setData(undefined); setError(null); api.spots.score(spot.id) .then(d => setData(d)) .catch(() => setError('Impossible de charger le score.')); }, [spot?.id]); if (!spot) return null; // Score à afficher selon l'étape active const displayScore = activeStep === 'now' ? data?.score : (data?.horizons?.[activeStep] ? { value: data.horizons[activeStep].score, level: data.horizons[activeStep].level, distance: data.horizons[activeStep].distanceKm, confidence: data.horizons[activeStep].confidence, breakdown: data.horizons[activeStep].breakdown, } : null); const lvl = displayScore?.level; return (
{spot.region}
Chargement…
} {error &&{error}
} {data !== undefined && !error && ( displayScore === null ?Aucune donnée pour cet horizon.
: ( <> {activeStep === 'now' && (() => { const eta = etaLabel(computeEta(data?.score, data?.horizons), data?.score?.value, data?.score?.distance); return (Indice de risque
{scoreLabel(displayScore.value, lvl, displayScore.distance)}{displayScore.value} / 100
{patches.length} banc{patches.length > 1 ? 's' : ''} de sargasses détecté{patches.length > 1 ? 's' : ''}
{patches.map((p, i) => { const pct = Math.min((p.distanceKm / MAX_KM) * 100, 100); const sizeLabel = p.areaKm2 >= 10 ? `${p.areaKm2} km²` : `${p.areaKm2} km²`; return (Détail du score
{BREAKDOWN_ITEMS.map(({ key, label, max, icon }) => { const val = breakdown[key] ?? 0; const pct = max > 0 ? (val / max) * 100 : 0; return (Merci pour votre retour !
; return (Vous êtes sur place ?
{error}
}