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
246
backend/static/js/pages/welcome.js
Normal file
246
backend/static/js/pages/welcome.js
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
/* ============================================================
|
||||
BAN YARO — Willkommensseite
|
||||
Über die App, Features, Installations-Anleitung.
|
||||
============================================================ */
|
||||
|
||||
window.Page_welcome = (() => {
|
||||
|
||||
let _container = null;
|
||||
let _appState = null;
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// INIT
|
||||
// ----------------------------------------------------------
|
||||
async function init(container, appState) {
|
||||
_container = container;
|
||||
_appState = appState;
|
||||
_render();
|
||||
}
|
||||
|
||||
function refresh() { _render(); }
|
||||
function onDogChange() {}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// RENDER
|
||||
// ----------------------------------------------------------
|
||||
function _render() {
|
||||
const isInstalled = window.matchMedia('(display-mode: standalone)').matches
|
||||
|| window.navigator.standalone === true;
|
||||
|
||||
_container.innerHTML = `
|
||||
<div style="max-width:480px;margin:0 auto;padding:var(--space-6) var(--space-4) var(--space-8)">
|
||||
|
||||
<!-- Hero -->
|
||||
<div style="text-align:center;margin-bottom:var(--space-8)">
|
||||
<img src="/icons/icon-180.png" alt="Ban Yaro"
|
||||
style="width:88px;height:88px;border-radius:var(--radius-xl);
|
||||
box-shadow:var(--shadow-md);margin-bottom:var(--space-4)">
|
||||
<h1 style="font-size:var(--text-2xl);font-weight:var(--weight-bold);
|
||||
color:var(--c-text);margin:0 0 var(--space-2)">Ban Yaro</h1>
|
||||
<p style="font-size:var(--text-base);color:var(--c-text-secondary);
|
||||
margin:0;line-height:1.5">
|
||||
Die Plattform für Hundebesitzer —<br>Tagebuch, Gesundheit, Community und mehr.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Features -->
|
||||
<div class="card" style="margin-bottom:var(--space-5)">
|
||||
<div style="padding:var(--space-3) var(--space-4);
|
||||
font-size:var(--text-xs);font-weight:600;
|
||||
color:var(--c-text-secondary);text-transform:uppercase;
|
||||
letter-spacing:0.05em;border-bottom:1px solid var(--c-border)">
|
||||
Was Ban Yaro kann
|
||||
</div>
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:0">
|
||||
${[
|
||||
['book-open', 'Tagebuch', 'Momente, Fotos und Meilensteine festhalten'],
|
||||
['syringe', 'Gesundheit', 'Impfungen, Tierarztbesuche & Medikamente'],
|
||||
['map-trifold', 'Karte & Routen', 'Hundefreundliche Orte und Spazierwege'],
|
||||
['warning-octagon','Giftköder-Alarm', 'Community-Warnungen in deiner Nähe'],
|
||||
['paw-print', 'Gassi-Treffen', 'Hunde-Dates mit anderen Besitzern'],
|
||||
['house-line', 'Sitting', 'Dogsitter finden oder selbst anbieten'],
|
||||
['target', 'Training', 'Übungen, Pläne und KI-Trainer'],
|
||||
['books', 'Wiki & Wissen', 'Rassen, Ernährung, Erste Hilfe'],
|
||||
].map(([icon, title, desc], i) => `
|
||||
<div style="display:flex;gap:var(--space-3);align-items:flex-start;
|
||||
padding:var(--space-4);
|
||||
${i % 2 === 0 ? 'border-right:1px solid var(--c-border);' : ''}
|
||||
${i < 6 ? 'border-bottom:1px solid var(--c-border);' : ''}">
|
||||
<div style="width:34px;height:34px;border-radius:var(--radius-md);
|
||||
background:var(--c-primary-subtle);flex-shrink:0;
|
||||
display:flex;align-items:center;justify-content:center">
|
||||
<svg style="width:18px;height:18px;color:var(--c-primary)" aria-hidden="true">
|
||||
<use href="/icons/phosphor.svg#${icon}"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size:var(--text-sm);font-weight:var(--weight-semibold);
|
||||
color:var(--c-text);margin-bottom:2px">${title}</div>
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-secondary);
|
||||
line-height:1.4">${desc}</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- App installieren -->
|
||||
<div class="card" style="margin-bottom:var(--space-5)" id="install-section">
|
||||
<div style="padding:var(--space-3) var(--space-4);
|
||||
font-size:var(--text-xs);font-weight:600;
|
||||
color:var(--c-text-secondary);text-transform:uppercase;
|
||||
letter-spacing:0.05em;border-bottom:1px solid var(--c-border)">
|
||||
App installieren
|
||||
</div>
|
||||
<div style="padding:var(--space-4)">
|
||||
${isInstalled
|
||||
? `<div style="display:flex;gap:var(--space-3);align-items:center;
|
||||
color:var(--c-success)">
|
||||
<svg style="width:20px;height:20px;flex-shrink:0" aria-hidden="true">
|
||||
<use href="/icons/phosphor.svg#check"></use>
|
||||
</svg>
|
||||
<span style="font-size:var(--text-sm);font-weight:var(--weight-semibold)">
|
||||
Ban Yaro ist bereits installiert.
|
||||
</span>
|
||||
</div>`
|
||||
: _installHTML()
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CTA wenn nicht eingeloggt -->
|
||||
${!_appState.user ? `
|
||||
<div style="display:flex;flex-direction:column;gap:var(--space-3)">
|
||||
<button class="btn btn-primary" id="welcome-register-btn">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#user-plus"></use></svg>
|
||||
Kostenlos registrieren
|
||||
</button>
|
||||
<button class="btn btn-ghost" id="welcome-login-btn">
|
||||
Bereits registriert? Anmelden
|
||||
</button>
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
<!-- Footer -->
|
||||
<p style="text-align:center;font-size:var(--text-xs);color:var(--c-text-muted);
|
||||
margin-top:var(--space-6)">
|
||||
Ban Yaro · Deine Daten auf eigenem Server in Deutschland.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
`;
|
||||
|
||||
_bindEvents();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// INSTALLATIONS-ANLEITUNG (plattformabhängig)
|
||||
// ----------------------------------------------------------
|
||||
function _installHTML() {
|
||||
const ua = navigator.userAgent;
|
||||
const isIOS = /iPad|iPhone|iPod/.test(ua) && !window.MSStream;
|
||||
const isSafari = /^((?!chrome|android).)*safari/i.test(ua);
|
||||
const isAndroid = /android/i.test(ua);
|
||||
const hasPrompt = !!App.getInstallPrompt();
|
||||
|
||||
// Android/Chrome mit nativem Prompt
|
||||
if ((isAndroid || hasPrompt) && hasPrompt) {
|
||||
return `
|
||||
<p style="font-size:var(--text-sm);color:var(--c-text-secondary);
|
||||
margin:0 0 var(--space-3);line-height:1.5">
|
||||
Installiere Ban Yaro direkt auf deinem Gerät — kein App Store nötig.
|
||||
Die App verhält sich wie eine native App und funktioniert auch offline.
|
||||
</p>
|
||||
<button class="btn btn-primary" id="install-android-btn" style="width:100%">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#download-simple"></use></svg>
|
||||
Ban Yaro installieren
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
|
||||
// iOS Safari
|
||||
if (isIOS && isSafari) {
|
||||
return `
|
||||
<p style="font-size:var(--text-sm);color:var(--c-text-secondary);
|
||||
margin:0 0 var(--space-4);line-height:1.5">
|
||||
Installiere Ban Yaro auf deinem iPhone oder iPad:
|
||||
</p>
|
||||
${_steps([
|
||||
['share', 'Tippe auf das <strong>Teilen-Symbol</strong> in Safari (Rechteck mit Pfeil nach oben)'],
|
||||
['plus', 'Scrolle nach unten und tippe auf <strong>„Zum Home-Bildschirm"</strong>'],
|
||||
['check', 'Tippe rechts oben auf <strong>„Hinzufügen"</strong> — fertig!'],
|
||||
])}
|
||||
<p style="font-size:var(--text-xs);color:var(--c-text-muted);margin:var(--space-3) 0 0">
|
||||
Funktioniert nur in Safari, nicht in anderen Browsern auf iOS.
|
||||
</p>
|
||||
`;
|
||||
}
|
||||
|
||||
// Desktop oder andere Browser
|
||||
return `
|
||||
<p style="font-size:var(--text-sm);color:var(--c-text-secondary);
|
||||
margin:0 0 var(--space-4);line-height:1.5">
|
||||
Ban Yaro lässt sich in Chrome, Edge und anderen modernen Browsern installieren:
|
||||
</p>
|
||||
${_steps([
|
||||
['globe', 'Öffne Ban Yaro in <strong>Chrome</strong> oder <strong>Edge</strong>'],
|
||||
['download-simple','Klicke in der Adressleiste auf das <strong>Installieren-Symbol</strong> (↓ mit Kreis)'],
|
||||
['check', 'Bestätige die Installation — Ban Yaro öffnet sich dann wie eine Desktop-App'],
|
||||
])}
|
||||
<p style="font-size:var(--text-xs);color:var(--c-text-muted);margin:var(--space-3) 0 0">
|
||||
Auf Android: Menü (⋮) → <strong>„App installieren"</strong> oder
|
||||
<strong>„Zum Startbildschirm hinzufügen"</strong>.
|
||||
</p>
|
||||
`;
|
||||
}
|
||||
|
||||
function _steps(list) {
|
||||
return `
|
||||
<ol style="margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:var(--space-3)">
|
||||
${list.map(([icon, text], i) => `
|
||||
<li style="display:flex;gap:var(--space-3);align-items:flex-start">
|
||||
<div style="width:28px;height:28px;border-radius:50%;flex-shrink:0;
|
||||
background:var(--c-primary);color:#fff;
|
||||
display:flex;align-items:center;justify-content:center;
|
||||
font-size:var(--text-xs);font-weight:var(--weight-bold)">
|
||||
${i + 1}
|
||||
</div>
|
||||
<span style="font-size:var(--text-sm);color:var(--c-text);line-height:1.5;
|
||||
padding-top:4px">${text}</span>
|
||||
</li>
|
||||
`).join('')}
|
||||
</ol>
|
||||
`;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// EVENTS
|
||||
// ----------------------------------------------------------
|
||||
function _bindEvents() {
|
||||
// Android-Install-Button
|
||||
_container.querySelector('#install-android-btn')?.addEventListener('click', async () => {
|
||||
const prompt = App.getInstallPrompt();
|
||||
if (!prompt) return;
|
||||
prompt.prompt();
|
||||
const { outcome } = await prompt.userChoice;
|
||||
if (outcome === 'accepted') {
|
||||
UI.toast.success('Ban Yaro wird installiert!');
|
||||
_render(); // zeigt "bereits installiert"
|
||||
}
|
||||
});
|
||||
|
||||
// CTAs für nicht-eingeloggte User
|
||||
_container.querySelector('#welcome-register-btn')?.addEventListener('click', () => {
|
||||
App.navigate('settings');
|
||||
});
|
||||
_container.querySelector('#welcome-login-btn')?.addEventListener('click', () => {
|
||||
App.navigate('settings');
|
||||
});
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// PUBLIC
|
||||
// ----------------------------------------------------------
|
||||
return { init, refresh, onDogChange };
|
||||
|
||||
})();
|
||||
Loading…
Add table
Add a link
Reference in a new issue