Sprint 12: UI-Vereinheitlichung + Läufigkeits-Tracker
- by-tabs/by-tab: einheitliche Tab/Pill-Navigation in allen Seiten - by-section-label, by-toolbar: einheitliche Section-Labels und Toolbars - Design-Tokens: fehlende --c-amber, --c-primary-soft ergänzt, Fallback-Werte entfernt - sitting.js: sitting-layout für konsistentes flush-Layout (wie walks) - Läufigkeits-Tracker: neuer Health-Tab für Hündinnen mit Zyklusvorhersage, Timeline vergangener Läufigkeiten, Erinnerungen und auto-berechnetem Nächst-Datum - emptyState-Bug: icon-Parameter muss SVG sein, nicht Icon-Name (dog/bell/warning gefixt) - SW-Cache: by-v103, APP_VER: 79
This commit is contained in:
parent
32d630d5a1
commit
b58789373c
30 changed files with 4344 additions and 523 deletions
|
|
@ -43,14 +43,16 @@ window.Page_sitting = (() => {
|
|||
// ----------------------------------------------------------
|
||||
function _render() {
|
||||
_container.innerHTML = `
|
||||
<div class="sitting-tabs" id="sit-tabs">
|
||||
<button class="sitting-tab active" data-sit-tab="suchen">${UI.icon('magnifying-glass')} Sitter finden</button>
|
||||
${_state.user ? `
|
||||
<button class="sitting-tab" data-sit-tab="profil">${UI.icon('user')} Mein Profil</button>
|
||||
<button class="sitting-tab" data-sit-tab="anfragen">${UI.icon('bell')} Anfragen</button>
|
||||
` : ''}
|
||||
<div class="sitting-layout">
|
||||
<div class="sitting-tabs by-tabs" id="sit-tabs">
|
||||
<button class="by-tab active" data-sit-tab="suchen">${UI.icon('magnifying-glass')} Sitter finden</button>
|
||||
${_state.user ? `
|
||||
<button class="by-tab" data-sit-tab="profil">${UI.icon('user')} Mein Profil</button>
|
||||
<button class="by-tab" data-sit-tab="anfragen">${UI.icon('bell')} Anfragen</button>
|
||||
` : ''}
|
||||
</div>
|
||||
<div id="sit-content" class="sitting-content"></div>
|
||||
</div>
|
||||
<div id="sit-content" class="sitting-content"></div>
|
||||
`;
|
||||
_container.addEventListener('click', _onClick);
|
||||
}
|
||||
|
|
@ -95,7 +97,7 @@ window.Page_sitting = (() => {
|
|||
// ---- Tab: Sitter suchen ----
|
||||
function _renderSuchen(el) {
|
||||
if (!_sitters.length) {
|
||||
el.innerHTML = UI.emptyState({ icon: 'dog', title: 'Keine Sitter', text: 'Noch keine Sitter in deiner Nähe registriert.' });
|
||||
el.innerHTML = UI.emptyState({ icon: UI.icon('dog'), title: 'Keine Sitter', text: 'Noch keine Sitter in deiner Nähe registriert.' });
|
||||
return;
|
||||
}
|
||||
el.innerHTML = `
|
||||
|
|
@ -138,7 +140,7 @@ window.Page_sitting = (() => {
|
|||
<div style="font-size:3rem">${UI.icon('paw-print')}</div>
|
||||
<h3>Werde Hundesitter</h3>
|
||||
<p>Biete anderen Hundebesitzern deine Dienste an und verdiene etwas dazu.</p>
|
||||
<button class="btn btn-primary" id="sit-create-profil-btn">Profil erstellen</button>
|
||||
<button class="btn btn-primary" id="sit-create-profil-btn">${UI.icon('plus')} Profil erstellen</button>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
|
|
@ -149,7 +151,7 @@ window.Page_sitting = (() => {
|
|||
<div class="sitting-my-profil">
|
||||
<div class="sitting-profil-header">
|
||||
<div class="sitting-profil-status ${s.aktiv ? 'active' : 'inactive'}">
|
||||
${s.aktiv ? `${UI.icon('check')} Aktiv` : 'Pausiert'}
|
||||
${s.aktiv ? `${UI.icon('check')} Aktiv` : `${UI.icon('pause')} Pausiert`}
|
||||
</div>
|
||||
<button class="btn btn-secondary btn-sm" id="sit-edit-profil-btn">${UI.icon('pencil-simple')} Bearbeiten</button>
|
||||
</div>
|
||||
|
|
@ -172,31 +174,36 @@ window.Page_sitting = (() => {
|
|||
let html = '';
|
||||
|
||||
if (inbox.length) {
|
||||
html += `<div class="sitting-section-label">${UI.icon('bell')} Eingehende Anfragen (als Sitter)</div>`;
|
||||
html += `<div class="by-section-label">${UI.icon('bell')} Eingehende Anfragen (als Sitter)</div>`;
|
||||
html += inbox.map(r => _requestCardHTML(r, 'inbox')).join('');
|
||||
}
|
||||
|
||||
if (myReqs.length) {
|
||||
html += `<div class="sitting-section-label" style="margin-top:var(--space-4)">${UI.icon('upload')} Meine Anfragen</div>`;
|
||||
html += `<div class="by-section-label" style="margin-top:var(--space-4)">${UI.icon('upload')} Meine Anfragen</div>`;
|
||||
html += myReqs.map(r => _requestCardHTML(r, 'sent')).join('');
|
||||
}
|
||||
|
||||
if (!inbox.length && !myReqs.length) {
|
||||
html = UI.emptyState({ icon: 'bell', title: 'Keine Anfragen', text: 'Noch keine Sitting-Anfragen vorhanden.' });
|
||||
html = UI.emptyState({ icon: UI.icon('bell'), title: 'Keine Anfragen', text: 'Noch keine Sitting-Anfragen vorhanden.' });
|
||||
}
|
||||
|
||||
el.innerHTML = html;
|
||||
}
|
||||
|
||||
const STATUS_ICON = { offen: 'clock', angenommen: 'check-circle', abgelehnt: 'x-circle', abgebrochen: 'minus-circle' };
|
||||
const STATUS_LABEL = { offen: 'Offen', angenommen: 'Angenommen', abgelehnt: 'Abgelehnt', abgebrochen: 'Abgebrochen' };
|
||||
const STATUS_COLOR = { offen: '#f59e0b', angenommen: '#10b981', abgelehnt: '#ef4444', abgebrochen: '#6b7280' };
|
||||
|
||||
function _requestCardHTML(r, mode) {
|
||||
const STATUS_COLOR = { offen: '#f59e0b', angenommen: '#10b981', abgelehnt: '#ef4444', abgebrochen: '#6b7280' };
|
||||
const color = STATUS_COLOR[r.status] || '#6b7280';
|
||||
const icon = STATUS_ICON[r.status] || 'question';
|
||||
const label = STATUS_LABEL[r.status] || r.status;
|
||||
const name = mode === 'inbox' ? r.anfragender_name : r.sitter_name;
|
||||
return `
|
||||
<div class="sitting-request-card" data-sit-req="${r.id}" data-sit-req-mode="${mode}">
|
||||
<div class="sitting-req-header">
|
||||
<span class="sitting-req-name">${UI.escHtml(name || '?')}</span>
|
||||
<span class="sitting-req-status" style="color:${color}">${r.status}</span>
|
||||
<span class="sitting-req-status" style="color:${color}">${UI.icon(icon)} ${label}</span>
|
||||
</div>
|
||||
<div class="sitting-req-dates">${UI.icon('calendar-dots')} ${r.von} – ${r.bis}</div>
|
||||
${r.nachricht ? `<div class="sitting-req-msg">${UI.escHtml(r.nachricht)}</div>` : ''}
|
||||
|
|
@ -295,7 +302,7 @@ window.Page_sitting = (() => {
|
|||
`;
|
||||
const footer = `
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-primary" type="submit" form="${id}" id="sit-anfrage-submit">Anfrage senden</button>
|
||||
<button class="btn btn-primary" type="submit" form="${id}" id="sit-anfrage-submit">${UI.icon('paper-plane-tilt')} Anfrage senden</button>
|
||||
`;
|
||||
UI.modal.open({ title: 'Anfrage senden', body, footer });
|
||||
|
||||
|
|
@ -313,7 +320,7 @@ window.Page_sitting = (() => {
|
|||
dog_ids: dogIds,
|
||||
nachricht: fd.get('nachricht') || null,
|
||||
};
|
||||
if (submitBtn) { submitBtn.disabled = true; submitBtn.textContent = '…'; }
|
||||
if (submitBtn) { submitBtn.disabled = true; submitBtn.innerHTML = '…'; }
|
||||
try {
|
||||
await API.sitting.sendRequest(data);
|
||||
UI.modal.close();
|
||||
|
|
@ -322,7 +329,7 @@ window.Page_sitting = (() => {
|
|||
} catch (err) {
|
||||
UI.toast(err.message, 'error');
|
||||
} finally {
|
||||
if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = 'Anfrage senden'; }
|
||||
if (submitBtn) { submitBtn.disabled = false; submitBtn.innerHTML = `${UI.icon('paper-plane-tilt')} Anfrage senden`; }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -385,7 +392,7 @@ window.Page_sitting = (() => {
|
|||
const footer = `
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-primary" type="submit" form="${id}" id="sit-profil-submit">
|
||||
${s ? 'Speichern' : 'Profil erstellen'}
|
||||
${s ? `${UI.icon('floppy-disk')} Speichern` : `${UI.icon('plus')} Profil erstellen`}
|
||||
</button>
|
||||
`;
|
||||
UI.modal.open({ title: s ? 'Sitter-Profil bearbeiten' : 'Sitter-Profil erstellen', body, footer });
|
||||
|
|
@ -416,7 +423,7 @@ window.Page_sitting = (() => {
|
|||
};
|
||||
if (s) data.aktiv = form.querySelector('[name="aktiv"]')?.checked ? 1 : 0;
|
||||
|
||||
if (submitBtn) { submitBtn.disabled = true; submitBtn.textContent = '…'; }
|
||||
if (submitBtn) { submitBtn.disabled = true; submitBtn.innerHTML = '…'; }
|
||||
try {
|
||||
if (s) {
|
||||
await API.sitting.updateMe(data);
|
||||
|
|
@ -429,7 +436,7 @@ window.Page_sitting = (() => {
|
|||
} catch (err) {
|
||||
UI.toast(err.message, 'error');
|
||||
} finally {
|
||||
if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = s ? 'Speichern' : 'Profil erstellen'; }
|
||||
if (submitBtn) { submitBtn.disabled = false; submitBtn.innerHTML = s ? `${UI.icon('floppy-disk')} Speichern` : `${UI.icon('plus')} Profil erstellen`; }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue