Bootssymbol hinzugefügt
This commit is contained in:
@@ -23,6 +23,7 @@ import {
|
||||
buildVoyagePlan,
|
||||
VOYAGE_AMENITIES,
|
||||
voyageAmenityLabel,
|
||||
type BoatProfile,
|
||||
type Coordinate,
|
||||
type NavigationDataSnapshot,
|
||||
type RouteResult,
|
||||
@@ -32,6 +33,11 @@ import {
|
||||
} from "@watermaps/shared";
|
||||
import type { RouteWeatherReport } from "../routeWeatherReport";
|
||||
import type { OfflineVoyage } from "../lib/offline-route";
|
||||
import {
|
||||
boatCategoryLabel,
|
||||
DEFAULT_BOAT_PROFILE,
|
||||
toVesselProfile
|
||||
} from "../lib/boat-profile";
|
||||
import type { RouteLock } from "../voyageHarbours";
|
||||
import { LazyContent } from "./LazyContent";
|
||||
import { NavigationDataPanel } from "./NavigationDataPanel";
|
||||
@@ -58,6 +64,8 @@ type RoutePlannerProps = {
|
||||
routeTides?: RouteTidePlan | null;
|
||||
routeTidesLoading?: boolean;
|
||||
routeTidesError?: string | null;
|
||||
boatProfile?: BoatProfile;
|
||||
routeVesselProfile?: VesselProfile | null;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
pickMode: "start" | "destination" | "waypoint" | null;
|
||||
@@ -66,7 +74,6 @@ type RoutePlannerProps = {
|
||||
destination: Coordinate;
|
||||
waypoints: Coordinate[];
|
||||
departureTime: string;
|
||||
vesselProfile: VesselProfile;
|
||||
}) => void;
|
||||
onPickStart: () => void;
|
||||
onPickDestination: () => void;
|
||||
@@ -80,6 +87,7 @@ type RoutePlannerProps = {
|
||||
onSelectRoute: (routeId: string) => void;
|
||||
guidanceActive?: boolean;
|
||||
onStartGuidance?: () => void;
|
||||
onEditBoat?: () => void;
|
||||
operationalPanelsVisible?: boolean;
|
||||
embedded?: boolean;
|
||||
onCollapse: () => void;
|
||||
@@ -113,6 +121,8 @@ export function RoutePlanner({
|
||||
routeTides = null,
|
||||
routeTidesLoading = false,
|
||||
routeTidesError = null,
|
||||
boatProfile = DEFAULT_BOAT_PROFILE,
|
||||
routeVesselProfile = null,
|
||||
loading,
|
||||
error,
|
||||
pickMode,
|
||||
@@ -129,15 +139,11 @@ export function RoutePlanner({
|
||||
onSelectRoute,
|
||||
guidanceActive = false,
|
||||
onStartGuidance = () => undefined,
|
||||
onEditBoat = () => undefined,
|
||||
operationalPanelsVisible = true,
|
||||
embedded = false,
|
||||
onCollapse
|
||||
}: RoutePlannerProps) {
|
||||
const [draughtM, setDraughtM] = useState(1.4);
|
||||
const [reserveM, setReserveM] = useState(0.5);
|
||||
const [airDraftM, setAirDraftM] = useState(2.5);
|
||||
const [beamM, setBeamM] = useState(3.2);
|
||||
const [speedKn, setSpeedKn] = useState(6);
|
||||
const [departureTime, setDepartureTime] = useState(() => localDateTimeValue(new Date()));
|
||||
const [maxCruisingHoursPerDay, setMaxCruisingHoursPerDay] = useState(8);
|
||||
const [requiredAmenities, setRequiredAmenities] = useState<VoyageAmenity[]>(["overnight"]);
|
||||
@@ -149,6 +155,16 @@ export function RoutePlanner({
|
||||
const [voyageToolsRequested, setVoyageToolsRequested] = useState(false);
|
||||
const [sheetState, setSheetState] = useState<RouteSheetState>("half");
|
||||
const previousResult = useRef(result);
|
||||
const vesselProfile = useMemo(() => toVesselProfile(boatProfile), [boatProfile]);
|
||||
const planningVesselProfile = result && routeVesselProfile
|
||||
? routeVesselProfile
|
||||
: vesselProfile;
|
||||
const speedKn = planningVesselProfile.cruiseSpeedKn ?? boatProfile.cruiseSpeedKn;
|
||||
const routeProfileDiffers = Boolean(
|
||||
result &&
|
||||
routeVesselProfile &&
|
||||
!sameVesselProfile(routeVesselProfile, vesselProfile)
|
||||
);
|
||||
const canRoute = Boolean(startPoint && destination);
|
||||
const isPickingStart = pickMode === "start";
|
||||
const isPickingDestination = pickMode === "destination";
|
||||
@@ -387,7 +403,7 @@ export function RoutePlanner({
|
||||
open={routeOptionsOpen}
|
||||
onToggle={(event) => setRouteOptionsOpen(event.currentTarget.open)}
|
||||
>
|
||||
<summary>Routenoptionen: Boot · Zwischenziele</summary>
|
||||
<summary>Routenoptionen: Zwischenziele</summary>
|
||||
<section className="waypoint-editor" aria-label="Zwischenziele">
|
||||
<div className="waypoint-editor-heading">
|
||||
<strong>Zwischenziele</strong>
|
||||
@@ -446,65 +462,35 @@ export function RoutePlanner({
|
||||
)}
|
||||
</section>
|
||||
|
||||
<div className="route-grid">
|
||||
<label>
|
||||
Tiefgang (m)
|
||||
<input
|
||||
value={draughtM}
|
||||
min={0.1}
|
||||
max={15}
|
||||
step={0.1}
|
||||
type="number"
|
||||
onChange={(event) => setDraughtM(Number(event.target.value))}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Sicherheitsreserve (m)
|
||||
<input
|
||||
value={reserveM}
|
||||
min={0}
|
||||
max={10}
|
||||
step={0.1}
|
||||
type="number"
|
||||
onChange={(event) => setReserveM(Number(event.target.value))}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Durchfahrtshöhe (m)
|
||||
<input
|
||||
value={airDraftM}
|
||||
min={0.5}
|
||||
max={40}
|
||||
step={0.1}
|
||||
type="number"
|
||||
onChange={(event) => setAirDraftM(Number(event.target.value))}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Geschwindigkeit (kn)
|
||||
<input
|
||||
value={speedKn}
|
||||
min={1}
|
||||
max={80}
|
||||
step={0.5}
|
||||
type="number"
|
||||
onChange={(event) => setSpeedKn(Number(event.target.value))}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Breite (m)
|
||||
<input
|
||||
value={beamM}
|
||||
min={0.5}
|
||||
max={40}
|
||||
step={0.1}
|
||||
type="number"
|
||||
onChange={(event) => setBeamM(Number(event.target.value))}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<section
|
||||
className="current-boat-summary"
|
||||
aria-label={`Aktuelles Boot: ${boatProfile.name}`}
|
||||
data-route-profile-differs={routeProfileDiffers}
|
||||
>
|
||||
<span className="current-boat-summary-icon">
|
||||
<ShipWheel size={18} aria-hidden="true" />
|
||||
</span>
|
||||
<span>
|
||||
<strong>{boatProfile.name}</strong>
|
||||
<small>
|
||||
{boatCategoryLabel(boatProfile.category)} · {formatProfileMeters(boatProfile.lengthM)}
|
||||
{" × "}
|
||||
{formatProfileMeters(boatProfile.beamM)} · Tiefgang{" "}
|
||||
{formatProfileMeters(boatProfile.draughtM)}
|
||||
</small>
|
||||
{routeProfileDiffers && (
|
||||
<em role="status">
|
||||
Diese geladene Route wurde mit einem anderen Bootsprofil berechnet.
|
||||
</em>
|
||||
)}
|
||||
</span>
|
||||
<button className="secondary-action compact-action" type="button" onClick={onEditBoat}>
|
||||
Bearbeiten
|
||||
</button>
|
||||
</section>
|
||||
|
||||
<label className="departure-planning">
|
||||
<span>
|
||||
<Clock3 size={14} aria-hidden="true" />
|
||||
@@ -529,8 +515,7 @@ export function RoutePlanner({
|
||||
start: startPoint,
|
||||
destination,
|
||||
waypoints,
|
||||
departureTime: toIsoOrNow(departureTime),
|
||||
vesselProfile: { draughtM, safetyReserveM: reserveM, airDraftM, beamM, cruiseSpeedKn: speedKn }
|
||||
departureTime: toIsoOrNow(departureTime)
|
||||
});
|
||||
}}
|
||||
>
|
||||
@@ -582,6 +567,17 @@ export function RoutePlanner({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{routeProfileDiffers && (
|
||||
<section className="warning-list" aria-label="Abweichendes Bootsprofil" role="alert">
|
||||
<strong>Route gehört zu einem anderen Bootsprofil</strong>
|
||||
<p>
|
||||
<AlertTriangle size={14} aria-hidden="true" />
|
||||
Die geladene Offline-Route behält ihre damaligen Maße. Für {boatProfile.name} muss
|
||||
sie mit dem aktuellen Bootsprofil neu berechnet werden.
|
||||
</p>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{(error || criticalWarnings.length > 0) && (
|
||||
<section
|
||||
className="warning-list route-priority-warnings"
|
||||
@@ -913,13 +909,7 @@ export function RoutePlanner({
|
||||
start: startPoint,
|
||||
destination,
|
||||
waypoints,
|
||||
vesselProfile: {
|
||||
draughtM,
|
||||
safetyReserveM: reserveM,
|
||||
airDraftM,
|
||||
beamM,
|
||||
cruiseSpeedKn: speedKn
|
||||
},
|
||||
vesselProfile: planningVesselProfile,
|
||||
departureAt: result?.departureTime ?? toIsoOrNow(departureTime)
|
||||
}
|
||||
: undefined
|
||||
@@ -979,6 +969,20 @@ function formatMeters(value: number | null | undefined) {
|
||||
return typeof value === "number" ? `${value.toFixed(1)} m` : "-- m";
|
||||
}
|
||||
|
||||
function formatProfileMeters(value: number) {
|
||||
return `${value.toLocaleString("de-DE", { maximumFractionDigits: 2 })} m`;
|
||||
}
|
||||
|
||||
function sameVesselProfile(left: VesselProfile, right: VesselProfile) {
|
||||
return (
|
||||
left.draughtM === right.draughtM &&
|
||||
left.safetyReserveM === right.safetyReserveM &&
|
||||
left.airDraftM === right.airDraftM &&
|
||||
left.beamM === right.beamM &&
|
||||
left.cruiseSpeedKn === right.cruiseSpeedKn
|
||||
);
|
||||
}
|
||||
|
||||
function formatBridgeMargin(value: number | null | undefined) {
|
||||
if (typeof value !== "number") {
|
||||
return "offen";
|
||||
|
||||
Reference in New Issue
Block a user