banyaro/backend/static/js/pages/welcome.js

327 lines
15 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* ============================================================
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>
<!-- Burger-Menü-Hinweis (nur Mobile, auf Desktop gibt es die Sidebar) -->
<button id="welcome-menu-hint" class="mobile-only"
style="display:flex;align-items:center;gap:var(--space-3);
background:var(--c-primary-subtle);border-radius:var(--radius-md);
padding:var(--space-3) var(--space-4);margin-bottom:var(--space-5);
border:none;width:100%;text-align:left;cursor:pointer">
<div id="welcome-burger-icon"
style="width:36px;height:36px;border-radius:var(--radius-md);
background:var(--c-primary);flex-shrink:0;
display:flex;align-items:center;justify-content:center">
<svg style="width:20px;height:20px;color:#fff" aria-hidden="true">
<use href="/icons/phosphor.svg#list"></use>
</svg>
</div>
<div>
<div style="font-size:var(--text-sm);font-weight:var(--weight-semibold);
color:var(--c-primary);margin-bottom:2px">
Alle Funktionen im Menü oben rechts
</div>
<div style="font-size:var(--text-xs);color:var(--c-text-secondary);line-height:1.4">
Tippe auf das ☰-Symbol für Karte, Routen, Giftköder-Alarm, Forum und vieles mehr.
</div>
</div>
</button>
<!-- Features: Mein Hund -->
${_featureCard('Mein Hund', [
['book-open', 'Tagebuch', 'Momente, Fotos und Meilensteine festhalten', 'diary'],
['syringe', 'Gesundheit', 'Impfungen, Tierarztbesuche & Medikamente', 'health'],
['target', 'Training', 'Übungen, Pläne und KI-Trainer', 'uebungen'],
['books', 'Wiki & Wissen', 'Rassen, Ernährung, Erste Hilfe', 'wiki'],
])}
<!-- Features: Community -->
${_featureCard('Community', [
['users', 'Freunde', 'Verbinde dich mit anderen Hundebesitzern', 'friends'],
['chat-circle-dots', 'Nachrichten', 'Private Chats mit deinen Freunden', 'chat'],
['push-pin', 'Forum', 'Diskussionen, Tipps und Austausch', 'forum'],
['paw-print', 'Gassi-Treffen', 'Hunde-Dates mit anderen Besitzern', 'walks'],
['house-line', 'Sitting', 'Dogsitter finden oder selbst anbieten', 'sitting'],
['magnifying-glass', 'Verlorene Hunde','Hilf gesuchte Hunde zu finden', 'lost'],
])}
<!-- Features: Entdecken -->
${_featureCard('Entdecken', [
['map-trifold', 'Karte & Routen', 'Hundefreundliche Orte und Spazierwege', 'map'],
['calendar-dots', 'Events', 'Veranstaltungen in deiner Nähe', 'events'],
['warning-octagon','Giftköder-Alarm', 'Community-Warnungen in deiner Nähe', 'poison'],
])}
<!-- 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 _featureCard(heading, items) {
return `
<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)">
${heading}
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:0">
${items.map(([icon, title, desc, page], i) => `
<button data-nav="${page}"
style="display:flex;gap:var(--space-3);align-items:flex-start;
padding:var(--space-4);text-align:left;background:none;border:none;
cursor:pointer;width:100%;transition:background var(--transition-fast);
${i % 2 === 0 ? 'border-right:1px solid var(--c-border);' : ''}
${i < items.length - (items.length % 2 === 0 ? 2 : 1) ? '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>
</button>
`).join('')}
</div>
</div>`;
}
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');
});
// Feature-Kacheln → navigieren
_container.querySelectorAll('[data-nav]').forEach(btn => {
btn.addEventListener('click', () => App.navigate(btn.dataset.nav));
btn.addEventListener('mouseenter', () => btn.style.background = 'var(--c-surface-2)');
btn.addEventListener('mouseleave', () => btn.style.background = '');
});
// Burger-Menü-Hinweis öffnet die Sidebar
_container.querySelector('#welcome-menu-hint')?.addEventListener('click', () => {
document.getElementById('header-menu-btn')?.click();
});
// Hamburger-Button 3× kurz pulsieren lassen
_pulseMenuBtn();
}
function _pulseMenuBtn() {
const btn = document.getElementById('header-menu-btn');
if (!btn) return;
if (!document.getElementById('welcome-pulse-style')) {
const s = document.createElement('style');
s.id = 'welcome-pulse-style';
s.textContent = `
@keyframes wc-pulse {
0%,100% { transform: scale(1); box-shadow: none; }
50% { transform: scale(1.25); box-shadow: 0 0 0 6px var(--c-primary-subtle); }
}
.wc-pulsing { animation: wc-pulse 0.6s ease-in-out 3; border-radius: var(--radius-md); }
`;
document.head.appendChild(s);
}
setTimeout(() => {
btn.classList.add('wc-pulsing');
btn.addEventListener('animationend', () => btn.classList.remove('wc-pulsing'), { once: true });
}, 800);
}
// ----------------------------------------------------------
// PUBLIC
// ----------------------------------------------------------
return { init, refresh, onDogChange };
})();