import { AlertTriangle, CheckCircle2, Cookie, Save, Sailboat, Trash2, X } from "lucide-react"; import { useEffect, useId, useState, type FormEvent } from "react"; import type { BoatCategory, BoatProfile } from "@watermaps/shared"; import { BOAT_CATEGORY_OPTIONS, validateBoatProfile } from "../lib/boat-profile"; import "./BoatProfilePanel.css"; export type BoatProfilePanelProps = { profile: BoatProfile; stored: boolean; hasActiveRoute?: boolean; onSave: (profile: BoatProfile) => boolean; onDelete: () => boolean; }; type Feedback = { kind: "success" | "error"; message: string; }; type BoatProfileDraft = { name: string; category: BoatCategory; lengthM: string; beamM: string; airDraftM: string; draughtM: string; safetyReserveM: string; cruiseSpeedKn: string; bowRollerHeightM: string; }; type NumericDraftKey = Exclude; export function BoatProfilePanel({ profile, stored, hasActiveRoute = false, onSave, onDelete }: BoatProfilePanelProps) { const headingId = useId(); const categoryHelpId = useId(); const deleteConfirmationId = useId(); const [draft, setDraft] = useState(() => toDraft(profile)); const [feedback, setFeedback] = useState(null); const [confirmDelete, setConfirmDelete] = useState(false); useEffect(() => { setDraft(toDraft(profile)); }, [profile]); useEffect(() => { if (!stored) { setConfirmDelete(false); } }, [stored]); const updateName = (name: string) => { setDraft((current) => ({ ...current, name })); setFeedback(null); }; const updateCategory = (category: BoatCategory) => { setDraft((current) => ({ ...current, category })); setFeedback(null); }; const updateNumber = (key: NumericDraftKey, value: string) => { setDraft((current) => ({ ...current, [key]: value })); setFeedback(null); }; const save = (event: FormEvent) => { event.preventDefault(); setConfirmDelete(false); const validation = validateBoatProfile(toProfile(draft)); if (!validation.valid) { setFeedback({ kind: "error", message: validation.error }); return; } if (!onSave(validation.profile)) { setFeedback({ kind: "error", message: "Die Bootsdaten konnten nicht im Cookie gespeichert werden. Bitte prüfe die Cookie-Einstellungen dieses Browsers." }); return; } setDraft(toDraft(validation.profile)); setFeedback({ kind: "success", message: "Die Bootsdaten wurden in diesem Browser gespeichert." }); }; const deleteProfile = () => { if (!onDelete()) { setFeedback({ kind: "error", message: "Die gespeicherten Bootsdaten konnten nicht gelöscht werden. Bitte prüfe die Cookie-Einstellungen dieses Browsers." }); return; } setConfirmDelete(false); setFeedback({ kind: "success", message: "Die gespeicherten Bootsdaten wurden aus diesem Browser gelöscht." }); }; return (

Aktuelles Boot

{stored ? "Im Cookie gespeichert" : "Noch nicht gespeichert"}

Diese Werte gelten automatisch für neue Routen und die weiteren Navigationswerkzeuge.

{hasActiveRoute && (
)}
Beschreibt den Bootstyp; beeinflusst das Routing derzeit nicht.
Abmessungen und Fahrtwerte

Bitte die Werte für den normalen Fahrzustand eintragen. Sie beeinflussen Durchfahrt, Tiefenprüfung und Fahrtdauer.

updateNumber("lengthM", value)} /> updateNumber("beamM", value)} /> updateNumber("airDraftM", value)} /> updateNumber("draughtM", value)} /> updateNumber("safetyReserveM", value)} /> updateNumber("cruiseSpeedKn", value)} />
Ankern

Dieser Wert ist von der Durchfahrtshöhe getrennt und wird für die Ketten- und Trossenberechnung verwendet.

updateNumber("bowRollerHeightM", value)} />
{feedback && (

{feedback.kind === "success" ? (

)}
{stored && ( )}
{stored && confirmDelete && (

Bootsdaten wirklich aus diesem Browser löschen? Neue Routen verwenden danach bis zum erneuten Speichern wieder die Standardwerte.

)}
); } function TextField({ label, help, value, onChange }: { label: string; help: string; value: string; onChange: (value: string) => void; }) { const id = useId(); const helpId = `${id}-help`; return (
onChange(event.target.value)} aria-describedby={helpId} autoComplete="off" minLength={1} maxLength={80} required /> {help}
); } function NumberField({ label, help, unit, min, max, value, onChange }: { label: string; help: string; unit: "m" | "kn"; min: number; max: number; value: string; onChange: (value: string) => void; }) { const id = useId(); const helpId = `${id}-help`; return (
onChange(event.target.value)} aria-describedby={helpId} min={min} max={max} step="any" required /> {help}
); } function toDraft(profile: BoatProfile): BoatProfileDraft { return { name: profile.name, category: profile.category, lengthM: String(profile.lengthM), beamM: String(profile.beamM), airDraftM: String(profile.airDraftM), draughtM: String(profile.draughtM), safetyReserveM: String(profile.safetyReserveM), cruiseSpeedKn: String(profile.cruiseSpeedKn), bowRollerHeightM: String(profile.bowRollerHeightM) }; } function toProfile(draft: BoatProfileDraft): BoatProfile { return { name: draft.name, category: draft.category, lengthM: parseNumber(draft.lengthM), beamM: parseNumber(draft.beamM), airDraftM: parseNumber(draft.airDraftM), draughtM: parseNumber(draft.draughtM), safetyReserveM: parseNumber(draft.safetyReserveM), cruiseSpeedKn: parseNumber(draft.cruiseSpeedKn), bowRollerHeightM: parseNumber(draft.bowRollerHeightM) }; } function parseNumber(value: string): number { return value.trim() ? Number(value) : Number.NaN; }