Frontend Sprint 3+4: Dog-Switcher, Health-Seite, Multi-Dog Tagebuch

- app.js: vollständiger Dog-Switcher (Avatar im Header/Sidebar, Quickpicker
  bei 3+ Hunden, setActiveDog, localStorage-Persistenz), iOS Ghost-Click Fix,
  Loading-Guard, Logout State Reset
- index.html: Dog-Switcher HTML, Favicon-Links, Sidebar "+ Neu erstellen",
  Navigation Tab Karte → Gesundheit
- health.js (neu): vollständiges Health-Frontend mit Tabs (Impfung, Entwurmung,
  Tierarzt, Medikament, Gewicht-Kurve, Allergie, Dokument), Ampel-System,
  KI-Zusammenfassung
- dog-profile.js: "+ Weiteren Hund anlegen" Button + _openCreateModal(),
  Event-Delegation statt direkter Listener (kein Doppelaufruf)
- diary.js: Dog-Picker im Formular, Avatar-Reihe auf Karten, Dog-Chips
  im Detail-Modal, dog_ids im API-Payload
- poison.js: Erledigt-Dialog mit Grundauswahl (beseitigt/fehlerhaft/anderes)
- api.js: health-Endpoints (list, create, update, delete, upload, ki)
- ui.js: confirm() Fix (resolve vor close)
- layout.css: Dog-Switcher Styles, scrollbare Sidebar-Nav, User-Item fix
- components.css: Health-Styles, Diary Dog-Picker, Ampel-Punkte, Gewicht-SVG
- icons/: Favicon-Set (ico, 16px, 32px, 180px, 192px, 512px)
This commit is contained in:
rene 2026-04-13 19:30:03 +02:00
parent 6f48ec581d
commit d8b9561fff
16 changed files with 1597 additions and 91 deletions

View file

@ -441,6 +441,7 @@ textarea.form-control {
padding: var(--space-4);
backdrop-filter: blur(2px);
animation: overlay-in var(--transition-normal) ease;
touch-action: manipulation;
}
@media (min-width: 768px) {
.modal-overlay { align-items: center; }
@ -812,3 +813,409 @@ textarea.form-control {
/* Leaflet-Attribution ausblenden */
.leaflet-control-attribution { display: none !important; }
/* ============================================================
GESUNDHEIT
============================================================ */
/* Header mit KI-Button */
.health-header {
display: flex;
justify-content: flex-end;
padding: var(--space-3) 0 var(--space-2);
}
/* Tab-Leiste — Mobile: horizontal scrollbar, Desktop: umbrechen */
.health-tabs {
display: flex;
flex-wrap: wrap;
gap: var(--space-1) var(--space-1);
padding-bottom: var(--space-2);
margin-bottom: var(--space-3);
}
/* Auf sehr kleinen Screens: scrollen statt umbrechen */
@media (max-width: 480px) {
.health-tabs {
flex-wrap: nowrap;
overflow-x: auto;
padding-right: var(--space-4);
scrollbar-width: none;
}
.health-tabs::-webkit-scrollbar { display: none; }
}
.health-tab {
flex-shrink: 0;
padding: var(--space-2) var(--space-3);
border: 2px solid var(--c-border);
border-radius: var(--radius-full);
background: var(--c-surface);
color: var(--c-text-secondary);
font-size: var(--text-sm);
font-weight: var(--weight-medium);
cursor: pointer;
white-space: nowrap;
transition: all var(--transition-fast);
touch-action: manipulation;
}
.health-tab.active {
background: var(--c-primary);
border-color: var(--c-primary);
color: var(--c-text-inverse);
}
/* Karten-Liste */
.health-list {
display: flex;
flex-direction: column;
gap: var(--space-3);
}
/* Einzelne Karte */
.health-card {
background: var(--c-surface);
border: 1px solid var(--c-border);
border-radius: var(--radius-md);
padding: var(--space-4);
cursor: pointer;
display: flex;
gap: var(--space-3);
align-items: flex-start;
transition: box-shadow var(--transition-fast), transform var(--transition-fast);
}
.health-card:active { transform: scale(0.985); }
.health-card--inactive { opacity: 0.55; }
.health-card-body { flex: 1; min-width: 0; }
.health-card-title { font-weight: var(--weight-semibold); margin-bottom: var(--space-1); }
.health-card-meta { font-size: var(--text-sm); color: var(--c-text-secondary); }
.health-card-next { font-size: var(--text-sm); font-weight: var(--weight-medium); margin-top: var(--space-1); }
.health-card-note { font-size: var(--text-sm); color: var(--c-text-secondary); margin-top: var(--space-1); }
/* Ampel-Punkt (links an der Karte) */
.health-card-ampel {
width: 10px;
height: 10px;
border-radius: 50%;
flex-shrink: 0;
margin-top: 5px;
}
.ampel-green { background: #22c55e; }
.ampel-yellow { background: #f59e0b; }
.ampel-red { background: #ef4444; }
.ampel-grey { background: var(--c-border); }
.ampel-text-green { color: #16a34a; }
.ampel-text-yellow { color: #d97706; }
.ampel-text-red { color: #dc2626; }
/* Gruppen-Label (z.B. "Aktuelle Medikamente") */
.health-group-label {
font-size: var(--text-sm);
font-weight: var(--weight-semibold);
color: var(--c-text-secondary);
text-transform: uppercase;
letter-spacing: 0.05em;
padding: var(--space-3) 0 var(--space-1);
}
/* Gewicht-Diagramm-Wrapper */
.health-chart-wrap {
background: var(--c-surface);
border: 1px solid var(--c-border);
border-radius: var(--radius-md);
padding: var(--space-4);
margin-bottom: var(--space-4);
overflow: hidden;
}
/* Dokument-Thumbnail und Icon */
.health-doc-thumb {
width: 56px;
height: 56px;
object-fit: cover;
border-radius: var(--radius-sm);
flex-shrink: 0;
}
.health-doc-icon {
width: 56px;
height: 56px;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
background: var(--c-surface-2);
border-radius: var(--radius-sm);
flex-shrink: 0;
}
/* Detail-Dialog DL */
.health-detail-dl {
display: grid;
grid-template-columns: auto 1fr;
gap: var(--space-1) var(--space-4);
font-size: var(--text-sm);
}
.health-detail-dl dt {
color: var(--c-text-secondary);
font-weight: var(--weight-medium);
white-space: nowrap;
}
.health-detail-dl dd { margin: 0; }
/* ------------------------------------------------------------
DOG SWITCHER
Avatar-Leiste in Header (Mobile) + Sidebar-Logo (Desktop)
------------------------------------------------------------ */
/* Aktiver (linker) Hund — primärer Ring */
.dog-sw-active {
width: 34px;
height: 34px;
border-radius: var(--radius-full);
overflow: hidden;
background: var(--c-surface-2);
flex-shrink: 0;
cursor: pointer;
border: 2.5px solid var(--c-primary);
transition: transform var(--transition-fast),
box-shadow var(--transition-fast);
}
.dog-sw-active:hover {
transform: scale(1.06);
box-shadow: 0 0 0 3px var(--c-primary-subtle);
}
.dog-sw-active img { width:100%; height:100%; object-fit:cover; display:block; }
.dog-sw-active span {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
font-size: 17px;
}
/* "Ban Yaro" Label — füllt den Zwischenraum */
.dog-sw-title {
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Gruppe der inaktiven Hunde (rechts) */
.dog-sw-others {
display: flex;
align-items: center;
position: relative; /* Anker für Quickpicker-Dropdown */
flex-shrink: 0;
}
/* Inaktiver Hund-Avatar */
.dog-sw-other {
width: 28px;
height: 28px;
border-radius: var(--radius-full);
overflow: hidden;
background: var(--c-surface-2);
border: 2px solid var(--c-surface);
cursor: pointer;
flex-shrink: 0;
transition: transform var(--transition-fast);
position: relative;
}
.dog-sw-other:hover { transform: scale(1.12); }
.dog-sw-other img { width:100%; height:100%; object-fit:cover; display:block; }
.dog-sw-other span {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
font-size: 14px;
}
/* Gestapelter Avatar-Stack (3+ Hunde) */
.dog-sw-stack {
display: flex;
align-items: center;
cursor: pointer;
}
.dog-sw-stack .dog-sw-other { margin-left: -9px; }
.dog-sw-stack .dog-sw-other:first-child { margin-left: 0; }
.dog-sw-stack .dog-sw-other--0 { z-index: 3; }
.dog-sw-stack .dog-sw-other--1 { z-index: 2; }
.dog-sw-stack .dog-sw-other--2 { z-index: 1; }
.dog-sw-stack:hover .dog-sw-other { filter: brightness(1.05); }
/* +N Überlauf-Badge */
.dog-sw-more {
width: 28px;
height: 28px;
border-radius: var(--radius-full);
background: var(--c-surface-2);
border: 2px solid var(--c-surface);
display: flex;
align-items: center;
justify-content: center;
font-size: 9px;
font-weight: var(--weight-bold);
color: var(--c-text-secondary);
margin-left: -9px;
position: relative;
z-index: 0;
}
/* Quickpicker-Dropdown */
.dog-quickpick {
position: absolute;
top: calc(100% + 10px);
right: 0;
background: var(--c-surface);
border: 1px solid var(--c-border-light);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-lg);
padding: var(--space-2);
min-width: 170px;
z-index: 600;
}
.dog-quickpick.hidden { display: none; }
.dog-qp-item {
display: flex;
align-items: center;
gap: var(--space-3);
padding: var(--space-2) var(--space-3);
border-radius: var(--radius-md);
cursor: pointer;
font-size: var(--text-sm);
font-weight: var(--weight-medium);
color: var(--c-text);
transition: background var(--transition-fast);
-webkit-tap-highlight-color: transparent;
}
.dog-qp-item:hover { background: var(--c-bg); }
.dog-qp-av {
width: 32px;
height: 32px;
border-radius: var(--radius-full);
overflow: hidden;
background: var(--c-surface-2);
flex-shrink: 0;
}
.dog-qp-av img { width:100%; height:100%; object-fit:cover; display:block; }
.dog-qp-av span {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
font-size: 16px;
}
/* Sidebar: größerer aktiver Avatar */
#sidebar-dog-switcher .dog-sw-active {
width: 36px;
height: 36px;
}
/* ------------------------------------------------------------
DIARY Multi-Dog (Hunde-Auswahl im Formular + Karten-Anzeige)
------------------------------------------------------------ */
/* Avatar-Reihe in der Tagebuch-Karte */
.diary-dog-row {
display: flex;
align-items: center;
gap: -4px; /* überlappend via margin */
margin-top: var(--space-2);
flex-wrap: wrap;
gap: var(--space-1);
}
.diary-dog-av {
width: 22px;
height: 22px;
border-radius: var(--radius-full);
overflow: hidden;
background: var(--c-surface-2);
border: 1.5px solid var(--c-surface);
flex-shrink: 0;
}
.diary-dog-av img { width:100%; height:100%; object-fit:cover; display:block; }
.diary-dog-av span {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
font-size: 12px;
}
/* Hunde-Chip in der Detail-Ansicht */
.diary-detail-dogs {
display: flex;
flex-wrap: wrap;
gap: var(--space-2);
margin-bottom: var(--space-3);
}
.diary-dog-chip {
display: flex;
align-items: center;
gap: var(--space-1);
padding: 3px 8px 3px 4px;
background: var(--c-surface-2);
border-radius: var(--radius-full);
font-size: var(--text-xs);
font-weight: var(--weight-medium);
color: var(--c-text-secondary);
}
.diary-dog-chip .diary-dog-av {
width: 20px;
height: 20px;
}
/* Hunde-Picker im Formular */
.diary-dog-picker {
display: flex;
flex-wrap: wrap;
gap: var(--space-2);
}
.diary-dog-pick-item {
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-2) var(--space-3);
border: 1.5px solid var(--c-border-light);
border-radius: var(--radius-full);
cursor: pointer;
font-size: var(--text-sm);
font-weight: var(--weight-medium);
color: var(--c-text-secondary);
transition: border-color var(--transition-fast),
background var(--transition-fast),
color var(--transition-fast);
-webkit-tap-highlight-color: transparent;
user-select: none;
}
.diary-dog-pick-item input { display: none; }
.diary-dog-pick-item .diary-dog-av {
width: 26px;
height: 26px;
}
.diary-dog-pick-item:hover {
border-color: var(--c-primary);
color: var(--c-text);
}
.diary-dog-pick-item.checked {
border-color: var(--c-primary);
background: var(--c-primary-subtle);
color: var(--c-primary-dark);
font-weight: var(--weight-semibold);
}

View file

@ -58,6 +58,16 @@
flex: 1;
}
/* Dog Switcher Container im Header */
#header-dog-switcher {
flex: 1;
display: flex;
align-items: center;
gap: var(--space-2);
min-width: 0;
overflow: visible;
}
.header-back {
display: flex;
align-items: center;
@ -201,7 +211,7 @@
background: var(--c-surface);
border-right: 1px solid var(--c-border-light);
flex-direction: column;
overflow-y: auto;
overflow: hidden; /* Sidebar selbst scrollt nicht */
box-shadow: var(--shadow-sm);
}
@ -229,14 +239,31 @@
color: var(--c-text);
}
.sidebar-nav {
flex: 1;
padding: var(--space-4) var(--space-2);
display: flex;
flex-direction: column;
gap: var(--space-1);
.sidebar-add {
padding: var(--space-4) var(--space-4) var(--space-2);
flex-shrink: 0;
}
.sidebar-nav {
flex: 1;
padding: var(--space-2) var(--space-2) var(--space-4);
display: flex;
flex-direction: column;
gap: var(--space-1);
overflow-y: auto;
min-height: 0; /* wichtig: flex-child darf kleiner werden als Inhalt */
/* Firefox */
scrollbar-width: thin;
scrollbar-color: var(--c-primary) var(--c-surface);
}
.sidebar-nav::-webkit-scrollbar { width: 6px; }
.sidebar-nav::-webkit-scrollbar-track { background: var(--c-surface); }
.sidebar-nav::-webkit-scrollbar-thumb {
background: var(--c-primary);
border-radius: 3px;
}
.sidebar-nav::-webkit-scrollbar-thumb:hover { background: var(--c-primary-dark); }
.sidebar-section-label {
font-size: var(--text-xs);
font-weight: var(--weight-semibold);
@ -270,6 +297,12 @@
color: var(--c-primary-dark);
font-weight: var(--weight-semibold);
}
/* User-Eintrag bekommt nie den Active-Stil */
.sidebar-item--user.active {
background: transparent;
color: var(--c-text-secondary);
font-weight: var(--weight-medium);
}
.sidebar-item-icon {
font-size: 18px;
width: 24px;
@ -291,11 +324,7 @@
padding: 0 var(--space-1);
}
.sidebar-footer {
padding: var(--space-4) var(--space-2);
border-top: 1px solid var(--c-border-light);
flex-shrink: 0;
}
/* sidebar-footer entfernt — Einstellungen/Konto sind jetzt Teil der scrollbaren Nav */
/* ------------------------------------------------------------
5. PAGE WRAPPER (inneres Layout der Seiten)