"use client" import { useEffect, useRef, useState } from "react" import { TargetDistance } from "@/components/target-distance" import { haversineMeters, type LatLng } from "@/lib/geo" // Rangefinder-datalag (ADR-064) -- henter rundens rå banepunkter (grønn // front/midt/bak, hindringer) og kombinerer dem klient-side med spillerens // LIVE GPS-posisjon via samme watchPosition-mønster som map-point-picker.tsx // allerede bruker for slagmåling (ADR-048 tillegg 2026-08-08 del 2) -- // `enableHighAccuracy`, `maximumAge: 1000`, grasiøs degradering ved avslått // tillatelse, `clearWatch` ved avmontering. `TargetDistance` selv forblir // "dum" (V0-eksportert, ren visning) -- all logikk bor her. type ApiTargetPoint = { poi_type: string location: "front" | "middle" | "back" | null side_fairway: "left" | "center" | "right" | null latitude: number longitude: number } const _HAZARD_LABELS: Record = { green_bunker: "Bunker (grønn)", fairway_bunker: "Bunker (fairway)", water: "Vannhinder", } export function HoleTargetDistance({ roundId, holeNumber, size = "compact", className, }: { roundId: string holeNumber: number size?: "compact" | "full" className?: string }) { const [points, setPoints] = useState(null) const [livePosition, setLivePosition] = useState(null) const [isLive, setIsLive] = useState(false) const watchIdRef = useRef(null) useEffect(() => { let cancelled = false setPoints(null) fetch(`/rounds/${roundId}/holes/${holeNumber}/target-points`, { credentials: "include" }) .then((res) => (res.ok ? res.json() : [])) .then((data: ApiTargetPoint[]) => { if (!cancelled) setPoints(data) }) .catch(() => { if (!cancelled) setPoints([]) }) return () => { cancelled = true } }, [roundId, holeNumber]) useEffect(() => { // Ingen vits å spore GPS når banen uansett ikke har koordinatdata for // dette hullet -- unngår unødvendig batteribruk/tillatelses-prompt. if (!points || points.length === 0) return if (typeof navigator === "undefined" || !navigator.geolocation) return watchIdRef.current = navigator.geolocation.watchPosition( (pos) => { setLivePosition({ lat: pos.coords.latitude, lng: pos.coords.longitude }) setIsLive(true) }, () => { // Avslått/feilet -- degraderer grasiøst, komponenten viser bare "ingen data". setIsLive(false) }, { enableHighAccuracy: true, maximumAge: 1000 }, ) return () => { if (watchIdRef.current !== null && typeof navigator !== "undefined" && navigator.geolocation) { navigator.geolocation.clearWatch(watchIdRef.current) } watchIdRef.current = null } }, [points]) // Fortsatt laster, ELLER banen har rett og slett ingen koordinatdata // (det store flertallet av runder, TeeOff-baner har ingen GolfAPI-kobling) // -- ingen synlig rad i det hele tatt, ikke en "ingen data"-lapp på HVER // eneste runde. Lappen (TargetDistance sin egen null-tilstand) er forbeholdt // "banen HAR data, men venter på GPS-posisjon ennå" -- da vet vi det er // verdt å vise noe. if (points === null || points.length === 0) return null if (!livePosition) { return (
) } const greenPoints = points.filter((p) => p.poi_type === "green" && p.location) const front = greenPoints.find((p) => p.location === "front") const middle = greenPoints.find((p) => p.location === "middle") const back = greenPoints.find((p) => p.location === "back") if (!front || !middle || !back) { return } const distances = { front: Math.round(haversineMeters(livePosition, { lat: front.latitude, lng: front.longitude })), middle: Math.round(haversineMeters(livePosition, { lat: middle.latitude, lng: middle.longitude })), back: Math.round(haversineMeters(livePosition, { lat: back.latitude, lng: back.longitude })), } const hazards = points.filter((p) => p.poi_type in _HAZARD_LABELS) let nearestHazard: { label: string; distanceMeters: number } | null = null for (const h of hazards) { const d = haversineMeters(livePosition, { lat: h.latitude, lng: h.longitude }) if (nearestHazard === null || d < nearestHazard.distanceMeters) { const base = _HAZARD_LABELS[h.poi_type] const label = h.location === "front" || h.location === "back" ? `${base} (${h.location === "front" ? "front" : "bak"})` : base nearestHazard = { label, distanceMeters: Math.round(d) } } } return (
) }