Reisen: Wunschliste, Besuche mit Fotos, Zeitleiste und Statistik

- Wunschliste (Spalte travel_cities.wish): eigene Punktform auf der Karte,
  zählt nicht als besucht, für andere Konten unsichtbar; "War ich da ✓"
- Besuche je Stadt (Tabellen travel_visits, travel_visit_photos): Zeitraum,
  Begleitung, Notizen, Fotos (Upload ohne Metadaten); nur für das eigene
  Konto; ein Besuch macht aus einem Wunsch einen Besuch
- Fotos von Besuchen zählen beim Aufräumen verwaister Bilder als benutzt;
  beim Entfernen eines Fotos, Besuchs oder einer Stadt werden die Dateien
  gelöscht
- Zeitleiste "Wann war ich wo?" nach Jahren, Fotos mit Lightbox
- Statistik: Länder (Welt, je Kontinent), Landfläche, äußerste Orte,
  Kilometer entlang der Besuche
- Ansichten unter der Karte als Tabs: Besuchte Orte, Zeitleiste,
  Statistik, Wunschliste
- Migration Schritt 4, Tests für Wunschliste, Besuche, Fotos und Aufräumen

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
guyg1474andClaude Opus 5.5 committed 2026-10-15 19:25:07 +02:00
1 parent 9835f5e69e
commit bbf740603e
16 files changed
+695 -32

No files matched your search

+40
View File
@@ -196,3 +196,43 @@
.info-card { position: relative; left: auto; bottom: auto; width: auto; max-height: none; border-width: 1px 0 0; box-shadow: none; }
.info-facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* Wunschliste: Ring statt gefülltem Punkt */
.map-city.wish .map-city-dot { fill: var(--bg); stroke: var(--warn); stroke-width: 2.5; stroke-dasharray: 3 2; }
.city-search-row { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center; margin-bottom: 14px; }
.city-search-row .city-search { flex: 1 1 320px; margin: 0; }
/* Besuche, Zeitleiste, Statistik */
.info-visits { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; padding-top: 8px; border-top: 1px dashed var(--border); }
.info-visits ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.info-visits small { color: var(--muted); }
.travel-tabs { display: flex; margin-top: 24px; }
.visit-photos { list-style: none; margin: 0 0 8px; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.visit-photos li { position: relative; }
.visit-photos img { display: block; width: 84px; height: 84px; object-fit: cover; border-radius: var(--radius-s); border: 1px solid var(--border); }
.visit-photos li > .link-btn { position: absolute; top: 2px; right: 2px; background: var(--overlay); padding: 0 5px; border-radius: var(--radius-s); }
.visit-photos.small img { width: 56px; height: 56px; }
.visit-photos button { padding: 0; border: 0; background: none; cursor: zoom-in; }
.timeline-travel { margin-top: 24px; display: grid; gap: 22px; }
.timeline-travel ol { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 10px; }
.timeline-entry { display: grid; grid-template-columns: minmax(9em, 14em) 1fr auto; gap: 4px 16px; align-items: start; padding: 10px 0; border-top: 1px dashed var(--border); }
.timeline-date { color: var(--muted); font-size: 0.85rem; font-variant-numeric: tabular-nums; }
.timeline-body { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.timeline-body .hint { margin: 0; }
.timeline-city { font: inherit; font-weight: 600; color: var(--bright); background: none; border: 0; padding: 0; text-align: left; cursor: pointer; }
.timeline-city small { font-weight: 400; color: var(--muted); }
.timeline-city:hover { color: var(--accent); }
.timeline-notes { margin: 0; white-space: pre-line; color: var(--read); }
@media (max-width: 640px) { .timeline-entry { grid-template-columns: 1fr auto; } .timeline-date { grid-column: 1 / -1; } }
.travel-stats { margin-top: 24px; display: grid; gap: 24px; }
.stat-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; }
.stat-tile { display: flex; flex-direction: column; gap: 4px; padding: 14px 16px; border: 1px solid var(--border); border-radius: var(--radius-m); background: var(--surface); }
.stat-tile strong { font-family: var(--font-display); font-size: 1.8rem; line-height: 1.1; color: var(--bright); }
.stat-tile span { color: var(--muted); font-size: 0.82rem; }
.stat-continents { display: grid; gap: 10px; max-width: 640px; }
.stat-points { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.stat-points small { color: var(--muted); }
.travel-wishes { margin-top: 24px; }
.link-picker ul.visit-photos { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; max-height: none; }
.link-picker ul.visit-photos li { width: auto; padding: 0; border: 0; }
.visit-hint { margin: -8px 0 0; }