Reisen: Wohnorte mit Datum, Anreise je Besuch, Umzüge in der Kilometer-Rechnung

- Wohnorte als Liste mit "seit"-Datum (Tabelle travel_homes, ersetzt den
  einzelnen Wohnort; Migration Schritt 5 übernimmt ihn): Reisen starten
  beim damals gültigen Wohnort, Wechsel zählen als Umzug, Tage am
  Wohnort nicht als Reisetage
- "In dieser Zeit hier gewohnt" im Besuch: legt Wohnort ab Beginn und den
  vorherigen wieder ab Ende + 1 Tag an
- Anreise je Besuch (automatisch / von zuhause / vom vorherigen Ort):
  Rundreisen auch mit mehreren Tagen zwischen den Orten
- Fix: Besuche bleiben bearbeitbar, auch wenn eine Fotodatei fehlt;
  Speicherfehler erscheinen im Dialog statt verdeckt dahinter

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
guyg1474andClaude Opus 5.5 committed 2026-10-15 20:40:40 +02:00
1 parent 56c4f38ef0
commit 1d7497bead
11 files changed
+343 -113

No files matched your search

+29 -13
View File
@@ -1,6 +1,7 @@
import { useState } from 'react';
import LAENDER from '../../data/laender.json';
import CitySearch from './CitySearch.jsx';
import { BY_CODE, CONTINENTS, continentOf, countryName, num } from './laender.js';
import { BY_CODE, CONTINENTS, continentOf, countryName, fmtDay, num } from './laender.js';
import { travelDays, travelRoute } from './route.js';
const pct = (part, total) => (total ? (part / total) * 100 : 0);
@@ -11,7 +12,8 @@ const TOTAL_AREA = LAENDER.reduce((s, c) => s + (c.area || 0), 0);
* Statistik: Länder (Welt und je Kontinent), Fläche, äußerste Punkte, Kilometer entlang der Zeitleiste.
* Grundlage für "Welt": alle Länder und Gebiete der Länderdaten (world-countries).
*/
export default function TravelStats({ visited, cities, visits, wishes, home, onSetHome, onRemoveHome, busy, onSelectCity }) {
export default function TravelStats({ visited, cities, visits, wishes, homes, onAddHome, onRemoveHome, busy, onSelectCity }) {
const [since, setSince] = useState('');
const codes = [...visited].filter((c) => BY_CODE.has(c));
const area = codes.reduce((s, c) => s + (BY_CODE.get(c).area || 0), 0);
const continents = CONTINENTS.map((name) => {
@@ -30,10 +32,10 @@ export default function TravelStats({ visited, cities, visits, wishes, home, onS
];
// Kilometer: Reisen ab Wohnort, Rundreisen und Ausflüge (siehe route.js)
const route = travelRoute(visits, cities, home);
const days = travelDays(visits);
const route = travelRoute(visits, cities, homes);
const days = travelDays(visits, cities, homes);
const summary = [
`${route.trips} ${route.trips === 1 ? 'Reise' : 'Reisen'}${home ? ` ab ${home.name}` : ''}`,
`${route.trips} ${route.trips === 1 ? 'Reise' : 'Reisen'}${homes.length ? ' ab Wohnort' : ''}`,
route.excursions && `${route.excursions} ${route.excursions === 1 ? 'Ausflug' : 'Ausflüge'}`,
`${days} Reisetage`,
].filter(Boolean).join(' · ');
@@ -51,16 +53,30 @@ export default function TravelStats({ visited, cities, visits, wishes, home, onS
</div>
<section className="stat-home" aria-labelledby="stat-wohnort">
<h2 className="section-title" id="stat-wohnort">Wohnort</h2>
{home ? (
<p className="stat-home-current">
⌂ <strong>{home.name}</strong> <small>{countryName(home.country)}</small>
<button className="link-btn danger" type="button" disabled={busy} onClick={onRemoveHome}>Entfernen</button>
</p>
<h2 className="section-title" id="stat-wohnort">Wohnorte</h2>
{homes.length ? (
<ul className="stat-homes">
{homes.map((h) => (
<li key={h.id}>
⌂ <strong>{h.name}</strong> <small>{countryName(h.country)} · {h.since ? `seit ${fmtDay(h.since)}` : 'von Anfang an'}</small>
<button className="link-btn danger" type="button" disabled={busy} onClick={() => onRemoveHome(h)} aria-label={`Wohnort ${h.name} entfernen`}>✕</button>
</li>
))}
</ul>
) : (
<p className="hint">Mit Wohnort zählt jede Reise als Hin- und Rückweg ab zuhause.</p>
)}
<CitySearch mode="home" cities={[]} onAdd={onSetHome} busy={busy} />
<div className="stat-home-add">
<label className="stat-home-since">
<span>Seit</span>
<input type="date" value={since} onChange={(e) => setSince(e.target.value)} aria-describedby="since-hint" />
</label>
<CitySearch mode="home" cities={[]} onAdd={async (r) => { if (await onAddHome({ ...r, since: since || null })) setSince(''); }} busy={busy} />
</div>
<p className="hint" id="since-hint">
„Seit“ leer = von Anfang an. Zog man um oder wohnte länger im Ausland, einen weiteren Wohnort mit Datum eintragen –
oder beim Besuch „In dieser Zeit hier gewohnt“ wählen.
</p>
{route.legs.length > 0 && (
<details className="stat-route">
<summary>Wie gerechnet? ({route.legs.length} Teilstrecken)</summary>
@@ -73,7 +89,7 @@ export default function TravelStats({ visited, cities, visits, wishes, home, onS
</li>
))}
</ol>
{!home && <p className="hint">Ohne Wohnort fehlen Hin- und Heimwege; die Reisen werden direkt nacheinander verbunden.</p>}
{!homes.length && <p className="hint">Ohne Wohnort fehlen Hin- und Heimwege; die Reisen werden direkt nacheinander verbunden.</p>}
</details>
)}
</section>