Compare commits
No commits in common. "5acfa9d8f61437a6b08f2865d8b1e431b6ba744a" and "eb72d6f675856bfc985779c6fcf2cc261d856d4b" have entirely different histories.
5acfa9d8f6
...
eb72d6f675
5 changed files with 14 additions and 573 deletions
|
|
@ -115,70 +115,21 @@
|
||||||
--safe-right: env(safe-area-inset-right, 0px);
|
--safe-right: env(safe-area-inset-right, 0px);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dark Mode — System-Präferenz (nur wenn kein manuelles Theme gesetzt) */
|
/* Dark Mode — vorbereitet, nicht aktiv */
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
:root:not([data-theme="light"]):not([data-theme="dark"]) {
|
:root {
|
||||||
--c-bg: #1A1410;
|
--c-bg: #1A1410;
|
||||||
--c-surface: #241C14;
|
--c-surface: #241C14;
|
||||||
--c-surface-2: #2E2418;
|
--c-surface-2: #2E2418;
|
||||||
--c-surface-3: #3A2E20;
|
--c-surface-3: #3A2E20;
|
||||||
--c-border: #4A3C2C;
|
--c-border: #4A3C2C;
|
||||||
--c-border-light: #3A2E20;
|
--c-border-light: #3A2E20;
|
||||||
|
|
||||||
--c-primary-subtle: #2A1C0A;
|
|
||||||
--c-primary-soft: #2E1E08;
|
|
||||||
|
|
||||||
--c-nature-subtle: #1A2214;
|
|
||||||
--c-sky-subtle: #141C22;
|
|
||||||
--c-danger-subtle: #2A100A;
|
|
||||||
--c-success-subtle: #122010;
|
|
||||||
--c-warning-subtle: #261A08;
|
|
||||||
--c-info-subtle: #10182A;
|
|
||||||
|
|
||||||
--c-text: #F0EAE0;
|
--c-text: #F0EAE0;
|
||||||
--c-text-secondary: #C0B0A0;
|
--c-text-secondary: #C0B0A0;
|
||||||
--c-text-muted: #806A58;
|
--c-text-muted: #806A58;
|
||||||
--c-text-inverse: #2A1F14;
|
|
||||||
|
|
||||||
--shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.30);
|
|
||||||
--shadow-sm: 0 1px 4px rgba(0, 0, 0, 0.35), 0 1px 2px rgba(0, 0, 0, 0.25);
|
|
||||||
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.40), 0 2px 4px rgba(0, 0, 0, 0.25);
|
|
||||||
--shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.45), 0 4px 8px rgba(0, 0, 0, 0.30);
|
|
||||||
--shadow-xl: 0 16px 40px rgba(0, 0, 0, 0.50), 0 8px 16px rgba(0, 0, 0, 0.35);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Manuelles Dark-Theme via data-theme="dark" (überschreibt auch prefers-color-scheme: light) */
|
|
||||||
:root[data-theme="dark"] {
|
|
||||||
--c-bg: #1A1410;
|
|
||||||
--c-surface: #241C14;
|
|
||||||
--c-surface-2: #2E2418;
|
|
||||||
--c-surface-3: #3A2E20;
|
|
||||||
--c-border: #4A3C2C;
|
|
||||||
--c-border-light: #3A2E20;
|
|
||||||
|
|
||||||
--c-primary-subtle: #2A1C0A;
|
|
||||||
--c-primary-soft: #2E1E08;
|
|
||||||
|
|
||||||
--c-nature-subtle: #1A2214;
|
|
||||||
--c-sky-subtle: #141C22;
|
|
||||||
--c-danger-subtle: #2A100A;
|
|
||||||
--c-success-subtle: #122010;
|
|
||||||
--c-warning-subtle: #261A08;
|
|
||||||
--c-info-subtle: #10182A;
|
|
||||||
|
|
||||||
--c-text: #F0EAE0;
|
|
||||||
--c-text-secondary: #C0B0A0;
|
|
||||||
--c-text-muted: #806A58;
|
|
||||||
--c-text-inverse: #2A1F14;
|
|
||||||
|
|
||||||
--shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.30);
|
|
||||||
--shadow-sm: 0 1px 4px rgba(0, 0, 0, 0.35), 0 1px 2px rgba(0, 0, 0, 0.25);
|
|
||||||
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.40), 0 2px 4px rgba(0, 0, 0, 0.25);
|
|
||||||
--shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.45), 0 4px 8px rgba(0, 0, 0, 0.30);
|
|
||||||
--shadow-xl: 0 16px 40px rgba(0, 0, 0, 0.50), 0 8px 16px rgba(0, 0, 0, 0.35);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------
|
/* ------------------------------------------------------------
|
||||||
2. RESET & BASE
|
2. RESET & BASE
|
||||||
------------------------------------------------------------ */
|
------------------------------------------------------------ */
|
||||||
|
|
|
||||||
|
|
@ -170,10 +170,6 @@
|
||||||
<div class="page-body page-container"></div>
|
<div class="page-body page-container"></div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="page" id="page-onboarding">
|
|
||||||
<div class="page-body page-container"></div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="page active" id="page-diary">
|
<section class="page active" id="page-diary">
|
||||||
<div class="page-body page-container">
|
<div class="page-body page-container">
|
||||||
<!-- wird von diary.js befüllt -->
|
<!-- wird von diary.js befüllt -->
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
Router, State-Management, Navigation, Initialisierung.
|
Router, State-Management, Navigation, Initialisierung.
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
const APP_VER = '179'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
const APP_VER = '175'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||||
|
|
||||||
const App = (() => {
|
const App = (() => {
|
||||||
|
|
||||||
|
|
@ -33,7 +33,6 @@ const App = (() => {
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
const pages = {
|
const pages = {
|
||||||
welcome: { title: 'Willkommen', module: null },
|
welcome: { title: 'Willkommen', module: null },
|
||||||
onboarding: { title: 'Einrichtung', module: null, requiresAuth: true },
|
|
||||||
diary: { title: 'Tagebuch', module: null, requiresAuth: true },
|
diary: { title: 'Tagebuch', module: null, requiresAuth: true },
|
||||||
health: { title: 'Gesundheit', module: null, requiresAuth: true },
|
health: { title: 'Gesundheit', module: null, requiresAuth: true },
|
||||||
'dog-profile': { title: 'Mein Hund', module: null, requiresAuth: true },
|
'dog-profile': { title: 'Mein Hund', module: null, requiresAuth: true },
|
||||||
|
|
@ -414,9 +413,9 @@ const App = (() => {
|
||||||
}
|
}
|
||||||
await _loadDogs();
|
await _loadDogs();
|
||||||
|
|
||||||
// Eingeloggter User ohne Hund → Onboarding-Wizard (einmalig)
|
// Eingeloggter User ohne Hund (z.B. nach Reload) → direkt zur Hund-Anlage
|
||||||
if (state.dogs.length === 0 && !localStorage.getItem('by_onboarding_done')) {
|
if (state.dogs.length === 0) {
|
||||||
navigate('onboarding');
|
navigate('dog-profile');
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateNotifBadge();
|
_updateNotifBadge();
|
||||||
|
|
|
||||||
|
|
@ -1,461 +0,0 @@
|
||||||
/* ============================================================
|
|
||||||
BAN YARO — Onboarding-Wizard
|
|
||||||
3-Schritt-Wizard für neue User ohne Hund.
|
|
||||||
============================================================ */
|
|
||||||
|
|
||||||
window.Page_onboarding = (() => {
|
|
||||||
|
|
||||||
let _container = null;
|
|
||||||
let _appState = null;
|
|
||||||
let _step = 1; // 1 = Willkommen, 2 = Hund anlegen, 3 = Fertig
|
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
// INIT
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
async function init(container, appState) {
|
|
||||||
_container = container;
|
|
||||||
_appState = appState;
|
|
||||||
_step = 1;
|
|
||||||
_render();
|
|
||||||
}
|
|
||||||
|
|
||||||
function refresh() {
|
|
||||||
// Wenn User nach Abschluss zurücknavigiert und schon fertig ist → Tagebuch
|
|
||||||
if (localStorage.getItem('by_onboarding_done')) {
|
|
||||||
App.navigate('diary');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_render();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onDogChange() {}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
// RENDER
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
function _render() {
|
|
||||||
_container.innerHTML = `
|
|
||||||
<div style="max-width:480px;margin:0 auto;padding:var(--space-6) var(--space-4) var(--space-8)">
|
|
||||||
|
|
||||||
<!-- Schritt-Indikator -->
|
|
||||||
<div style="display:flex;align-items:center;justify-content:center;
|
|
||||||
gap:var(--space-2);margin-bottom:var(--space-8)">
|
|
||||||
${[1, 2, 3].map(n => `
|
|
||||||
<div style="display:flex;align-items:center;gap:var(--space-2)">
|
|
||||||
<div style="width:32px;height:32px;border-radius:50%;
|
|
||||||
display:flex;align-items:center;justify-content:center;
|
|
||||||
font-size:var(--text-sm);font-weight:var(--weight-bold);
|
|
||||||
transition:background 0.2s,color 0.2s;
|
|
||||||
${n === _step
|
|
||||||
? 'background:var(--c-primary);color:#fff;'
|
|
||||||
: n < _step
|
|
||||||
? 'background:var(--c-success);color:#fff;'
|
|
||||||
: 'background:var(--c-bg-secondary);color:var(--c-text-secondary);'}">
|
|
||||||
${n < _step
|
|
||||||
? `<svg style="width:16px;height:16px" aria-hidden="true"><use href="/icons/phosphor.svg#check"></use></svg>`
|
|
||||||
: n}
|
|
||||||
</div>
|
|
||||||
${n < 3 ? `<div style="width:40px;height:2px;
|
|
||||||
background:${n < _step ? 'var(--c-success)' : 'var(--c-border)'};
|
|
||||||
border-radius:1px;transition:background 0.2s"></div>` : ''}
|
|
||||||
</div>
|
|
||||||
`).join('')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Schritt-Inhalt -->
|
|
||||||
<div id="onboarding-step-content">
|
|
||||||
${_stepContent()}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
_bindEvents();
|
|
||||||
}
|
|
||||||
|
|
||||||
function _stepContent() {
|
|
||||||
if (_step === 1) return _step1();
|
|
||||||
if (_step === 2) return _step2();
|
|
||||||
if (_step === 3) return _step3();
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
// SCHRITT 1 — Willkommen
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
function _step1() {
|
|
||||||
return `
|
|
||||||
<div style="text-align:center">
|
|
||||||
|
|
||||||
<!-- Logo -->
|
|
||||||
<div style="margin-bottom:var(--space-6)">
|
|
||||||
<img src="/icons/icon-180.png" alt="Ban Yaro"
|
|
||||||
style="width:96px;height:96px;border-radius:var(--radius-xl);
|
|
||||||
box-shadow:var(--shadow-md)">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Überschrift -->
|
|
||||||
<h1 style="font-size:var(--text-2xl);font-weight:var(--weight-bold);
|
|
||||||
color:var(--c-text);margin:0 0 var(--space-4)">
|
|
||||||
Willkommen bei Ban Yaro!
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<!-- Beschreibung -->
|
|
||||||
<p style="font-size:var(--text-base);color:var(--c-text-secondary);
|
|
||||||
line-height:1.6;margin:0 0 var(--space-3)">
|
|
||||||
Ban Yaro ist dein digitaler Begleiter für alles rund um deinen Hund —
|
|
||||||
Tagebuch, Gesundheit, Karte und Community in einer App.
|
|
||||||
</p>
|
|
||||||
<p style="font-size:var(--text-base);color:var(--c-text-secondary);
|
|
||||||
line-height:1.6;margin:0 0 var(--space-8)">
|
|
||||||
In nur zwei Schritten richtest du dein Profil ein und bist sofort startklar.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<!-- Feature-Highlights -->
|
|
||||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-3);
|
|
||||||
margin-bottom:var(--space-8);text-align:left">
|
|
||||||
${[
|
|
||||||
['book-open', 'Tagebuch', 'Momente & Fotos'],
|
|
||||||
['syringe', 'Gesundheit', 'Impfungen & Arzt'],
|
|
||||||
['map-trifold', 'Karte', 'Orte & Routen'],
|
|
||||||
['users', 'Community', 'Freunde & Treffen'],
|
|
||||||
].map(([icon, title, desc]) => `
|
|
||||||
<div style="display:flex;gap:var(--space-3);align-items:center;
|
|
||||||
background:var(--c-bg-secondary);border-radius:var(--radius-md);
|
|
||||||
padding:var(--space-3)">
|
|
||||||
<div style="width:36px;height:36px;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)">${title}</div>
|
|
||||||
<div style="font-size:var(--text-xs);color:var(--c-text-secondary)">${desc}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`).join('')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Buttons -->
|
|
||||||
<div style="display:flex;flex-direction:column;gap:var(--space-3)">
|
|
||||||
<button class="btn btn-primary" id="ob-next-btn" style="width:100%">
|
|
||||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#arrow-right"></use></svg>
|
|
||||||
Los geht's
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-ghost" id="ob-skip-btn" style="width:100%">
|
|
||||||
Überspringen
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
// SCHRITT 2 — Hund anlegen
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
function _step2() {
|
|
||||||
const today = new Date().toISOString().slice(0, 10);
|
|
||||||
return `
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<!-- Überschrift -->
|
|
||||||
<div style="text-align:center;margin-bottom:var(--space-6)">
|
|
||||||
<div style="width:64px;height:64px;border-radius:50%;
|
|
||||||
background:var(--c-primary-subtle);margin:0 auto var(--space-4);
|
|
||||||
display:flex;align-items:center;justify-content:center">
|
|
||||||
<svg style="width:32px;height:32px;color:var(--c-primary)" aria-hidden="true">
|
|
||||||
<use href="/icons/phosphor.svg#dog"></use>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<h2 style="font-size:var(--text-xl);font-weight:var(--weight-bold);
|
|
||||||
color:var(--c-text);margin:0 0 var(--space-2)">
|
|
||||||
Dein erster Hund
|
|
||||||
</h2>
|
|
||||||
<p style="font-size:var(--text-sm);color:var(--c-text-secondary);margin:0">
|
|
||||||
Nur der Name ist Pflicht — alles andere kannst du später ergänzen.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Formular -->
|
|
||||||
<form id="ob-dog-form" autocomplete="off">
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="form-label">Name *</label>
|
|
||||||
<input class="form-control" type="text" name="name" id="ob-dog-name"
|
|
||||||
placeholder="z. B. Luna, Max, Bello …" required autofocus>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="form-label">
|
|
||||||
Rasse
|
|
||||||
<span style="color:var(--c-text-secondary);font-weight:400">(optional)</span>
|
|
||||||
</label>
|
|
||||||
<input class="form-control" type="text" name="rasse"
|
|
||||||
placeholder="z. B. Golden Retriever, Mischling …">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="form-label">
|
|
||||||
Geburtstag
|
|
||||||
<span style="color:var(--c-text-secondary);font-weight:400">(optional)</span>
|
|
||||||
</label>
|
|
||||||
<input class="form-control" type="date" name="geburtstag" max="${today}">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Foto-Upload -->
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="form-label">
|
|
||||||
Foto
|
|
||||||
<span style="color:var(--c-text-secondary);font-weight:400">(optional)</span>
|
|
||||||
</label>
|
|
||||||
<div id="ob-photo-preview" style="display:none;margin-bottom:var(--space-2)">
|
|
||||||
<img id="ob-photo-img" src="" alt="Vorschau"
|
|
||||||
style="width:80px;height:80px;object-fit:cover;
|
|
||||||
border-radius:50%;border:2px solid var(--c-border)">
|
|
||||||
</div>
|
|
||||||
<label class="btn btn-secondary" style="width:100%;cursor:pointer;
|
|
||||||
display:flex;align-items:center;justify-content:center;gap:var(--space-2)">
|
|
||||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#camera"></use></svg>
|
|
||||||
<span id="ob-photo-label">Foto auswählen</span>
|
|
||||||
<input type="file" name="foto" id="ob-photo-input"
|
|
||||||
accept="image/*" style="display:none">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<!-- Buttons -->
|
|
||||||
<div style="display:flex;gap:var(--space-3);margin-top:var(--space-6)">
|
|
||||||
<button class="btn btn-secondary" id="ob-back-btn">
|
|
||||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#arrow-left"></use></svg>
|
|
||||||
</button>
|
|
||||||
<button type="submit" form="ob-dog-form" class="btn btn-primary" id="ob-save-btn"
|
|
||||||
style="flex:1">
|
|
||||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#dog"></use></svg>
|
|
||||||
Hund anlegen
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div style="text-align:center;margin-top:var(--space-3)">
|
|
||||||
<button class="btn btn-ghost" id="ob-skip-btn" style="font-size:var(--text-sm)">
|
|
||||||
Ohne Hund fortfahren
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
// SCHRITT 3 — Fertig
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
function _step3() {
|
|
||||||
const dogName = _appState.activeDog?.name;
|
|
||||||
return `
|
|
||||||
<div style="text-align:center">
|
|
||||||
|
|
||||||
<!-- Erfolgs-Icon -->
|
|
||||||
<div style="margin-bottom:var(--space-6)">
|
|
||||||
<div style="width:80px;height:80px;border-radius:50%;
|
|
||||||
background:var(--c-success-subtle,#dcfce7);margin:0 auto var(--space-4);
|
|
||||||
display:flex;align-items:center;justify-content:center">
|
|
||||||
<svg style="width:40px;height:40px;color:var(--c-success)" aria-hidden="true">
|
|
||||||
<use href="/icons/phosphor.svg#check-circle"></use>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Text -->
|
|
||||||
<h2 style="font-size:var(--text-2xl);font-weight:var(--weight-bold);
|
|
||||||
color:var(--c-text);margin:0 0 var(--space-4)">
|
|
||||||
Dein Profil ist bereit!
|
|
||||||
</h2>
|
|
||||||
${dogName ? `
|
|
||||||
<p style="font-size:var(--text-base);color:var(--c-text-secondary);
|
|
||||||
line-height:1.6;margin:0 0 var(--space-3)">
|
|
||||||
<strong>${_esc(dogName)}</strong> ist jetzt in Ban Yaro.
|
|
||||||
Du kannst jetzt Einträge im Tagebuch anlegen, die Gesundheit pflegen
|
|
||||||
und viele weitere Funktionen nutzen.
|
|
||||||
</p>
|
|
||||||
` : `
|
|
||||||
<p style="font-size:var(--text-base);color:var(--c-text-secondary);
|
|
||||||
line-height:1.6;margin:0 0 var(--space-3)">
|
|
||||||
Ban Yaro ist bereit. Du kannst jetzt die Karte, das Wiki und viele
|
|
||||||
weitere Funktionen erkunden.
|
|
||||||
</p>
|
|
||||||
`}
|
|
||||||
<p style="font-size:var(--text-sm);color:var(--c-text-secondary);
|
|
||||||
line-height:1.6;margin:0 0 var(--space-8)">
|
|
||||||
Du kannst dein Hundeprofil jederzeit unter
|
|
||||||
<strong>Mein Hund</strong> bearbeiten und ergänzen.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<!-- CTA -->
|
|
||||||
<div style="display:flex;flex-direction:column;gap:var(--space-3)">
|
|
||||||
<button class="btn btn-primary" id="ob-diary-btn" style="width:100%">
|
|
||||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#book-open"></use></svg>
|
|
||||||
Zum Tagebuch
|
|
||||||
</button>
|
|
||||||
${dogName ? `
|
|
||||||
<button class="btn btn-secondary" id="ob-profile-btn" style="width:100%">
|
|
||||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#dog"></use></svg>
|
|
||||||
Profil vervollständigen
|
|
||||||
</button>
|
|
||||||
` : ''}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
// EVENTS
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
function _bindEvents() {
|
|
||||||
// Weiter-Button (Schritt 1)
|
|
||||||
_container.querySelector('#ob-next-btn')?.addEventListener('click', () => {
|
|
||||||
_step = 2;
|
|
||||||
_render();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Zurück-Button (Schritt 2)
|
|
||||||
_container.querySelector('#ob-back-btn')?.addEventListener('click', () => {
|
|
||||||
_step = 1;
|
|
||||||
_render();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Überspringen
|
|
||||||
_container.querySelector('#ob-skip-btn')?.addEventListener('click', () => {
|
|
||||||
_finish();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Foto-Vorschau
|
|
||||||
_container.querySelector('#ob-photo-input')?.addEventListener('change', e => {
|
|
||||||
const file = e.target.files?.[0];
|
|
||||||
if (!file) return;
|
|
||||||
const url = URL.createObjectURL(file);
|
|
||||||
const preview = _container.querySelector('#ob-photo-preview');
|
|
||||||
const img = _container.querySelector('#ob-photo-img');
|
|
||||||
const label = _container.querySelector('#ob-photo-label');
|
|
||||||
if (preview) preview.style.display = '';
|
|
||||||
if (img) img.src = url;
|
|
||||||
if (label) label.textContent = file.name.length > 20
|
|
||||||
? file.name.slice(0, 17) + '...'
|
|
||||||
: file.name;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Formular abschicken (Schritt 2)
|
|
||||||
_container.querySelector('#ob-dog-form')?.addEventListener('submit', async e => {
|
|
||||||
e.preventDefault();
|
|
||||||
await _saveDog(e.target);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Zu Tagebuch (Schritt 3)
|
|
||||||
_container.querySelector('#ob-diary-btn')?.addEventListener('click', () => {
|
|
||||||
App.navigate('diary');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Zu Hund-Profil (Schritt 3)
|
|
||||||
_container.querySelector('#ob-profile-btn')?.addEventListener('click', () => {
|
|
||||||
App.navigate('dog-profile');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
// HUND SPEICHERN
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
async function _saveDog(form) {
|
|
||||||
const saveBtn = _container.querySelector('#ob-save-btn');
|
|
||||||
if (saveBtn) {
|
|
||||||
saveBtn.disabled = true;
|
|
||||||
saveBtn.innerHTML = `
|
|
||||||
<svg class="ph-icon spin" aria-hidden="true"><use href="/icons/phosphor.svg#circle-notch"></use></svg>
|
|
||||||
Wird angelegt…
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const data = new FormData(form);
|
|
||||||
const payload = {
|
|
||||||
name: data.get('name')?.trim(),
|
|
||||||
rasse: data.get('rasse')?.trim() || null,
|
|
||||||
geburtstag: data.get('geburtstag') || null,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!payload.name) {
|
|
||||||
UI.toast.error('Bitte gib einen Namen ein.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hund anlegen
|
|
||||||
const dog = await API.dogs.create(payload);
|
|
||||||
|
|
||||||
// Foto hochladen (falls vorhanden)
|
|
||||||
const fotoFile = data.get('foto');
|
|
||||||
if (fotoFile && fotoFile.size > 0) {
|
|
||||||
try {
|
|
||||||
const fd = new FormData();
|
|
||||||
fd.append('file', fotoFile);
|
|
||||||
await API.dogs.uploadPhoto(dog.id, fd);
|
|
||||||
} catch {
|
|
||||||
// Foto-Upload-Fehler ist nicht kritisch
|
|
||||||
UI.toast.warning('Hund angelegt, Foto konnte nicht hochgeladen werden.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// State aktualisieren
|
|
||||||
const dogs = await API.dogs.list();
|
|
||||||
_appState.dogs = dogs;
|
|
||||||
const newDog = dogs.find(d => d.id === dog.id) || dogs[0];
|
|
||||||
_appState.activeDog = newDog;
|
|
||||||
if (newDog) {
|
|
||||||
localStorage.setItem('by_active_dog', String(newDog.id));
|
|
||||||
}
|
|
||||||
App.renderDogSwitcher();
|
|
||||||
|
|
||||||
UI.toast.success(`${_esc(dog.name)} wurde angelegt!`);
|
|
||||||
|
|
||||||
_step = 3;
|
|
||||||
_render();
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
UI.toast.error(err.message || 'Hund konnte nicht angelegt werden.');
|
|
||||||
} finally {
|
|
||||||
if (saveBtn) {
|
|
||||||
saveBtn.disabled = false;
|
|
||||||
saveBtn.innerHTML = `
|
|
||||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#dog"></use></svg>
|
|
||||||
Hund anlegen
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
// ABSCHLUSS
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
function _finish() {
|
|
||||||
localStorage.setItem('by_onboarding_done', '1');
|
|
||||||
if (_appState.dogs.length > 0) {
|
|
||||||
App.navigate('diary');
|
|
||||||
} else {
|
|
||||||
App.navigate('map');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
// HELPER
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
function _esc(s) {
|
|
||||||
return UI.escape(s || '');
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
// PUBLIC
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
return { init, refresh, onDogChange };
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
@ -182,32 +182,6 @@ window.Page_settings = (() => {
|
||||||
App-Einstellungen
|
App-Einstellungen
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body" style="padding:0">
|
<div class="card-body" style="padding:0">
|
||||||
|
|
||||||
<!-- Dark-Mode-Auswahl -->
|
|
||||||
<div style="display:flex;align-items:center;gap:var(--space-3);
|
|
||||||
padding:var(--space-4);border-bottom:1px solid var(--c-border)">
|
|
||||||
<svg class="ph-icon" aria-hidden="true" style="width:1.25rem;height:1.25rem"><use href="/icons/phosphor.svg#moon"></use></svg>
|
|
||||||
<div style="flex:1">
|
|
||||||
<div style="font-weight:500">Dark Mode</div>
|
|
||||||
<div style="font-size:var(--text-xs);color:var(--c-text-secondary);margin-top:2px">
|
|
||||||
Erscheinungsbild der App
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<select id="select-theme"
|
|
||||||
style="padding:var(--space-1) var(--space-2);
|
|
||||||
border:1.5px solid var(--c-border);
|
|
||||||
border-radius:var(--radius-md);
|
|
||||||
background:var(--c-surface);
|
|
||||||
color:var(--c-text);
|
|
||||||
font-size:var(--text-sm);
|
|
||||||
font-family:inherit;
|
|
||||||
cursor:pointer">
|
|
||||||
<option value="system" ${(localStorage.getItem('by_theme')||'system') === 'system' ? 'selected' : ''}>System</option>
|
|
||||||
<option value="light" ${localStorage.getItem('by_theme') === 'light' ? 'selected' : ''}>Hell</option>
|
|
||||||
<option value="dark" ${localStorage.getItem('by_theme') === 'dark' ? 'selected' : ''}>Dunkel</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="display:flex;align-items:center;gap:var(--space-3);
|
<div style="display:flex;align-items:center;gap:var(--space-3);
|
||||||
padding:var(--space-4);border-bottom:1px solid var(--c-border)">
|
padding:var(--space-4);border-bottom:1px solid var(--c-border)">
|
||||||
<svg class="ph-icon" aria-hidden="true" style="width:1.25rem;height:1.25rem"><use href="/icons/phosphor.svg#eye-slash"></use></svg>
|
<svg class="ph-icon" aria-hidden="true" style="width:1.25rem;height:1.25rem"><use href="/icons/phosphor.svg#eye-slash"></use></svg>
|
||||||
|
|
@ -446,24 +420,6 @@ window.Page_settings = (() => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('select-theme')?.addEventListener('change', e => {
|
|
||||||
const val = e.target.value;
|
|
||||||
localStorage.setItem('by_theme', val);
|
|
||||||
const html = document.documentElement;
|
|
||||||
if (val === 'dark') {
|
|
||||||
html.setAttribute('data-theme', 'dark');
|
|
||||||
} else if (val === 'light') {
|
|
||||||
html.setAttribute('data-theme', 'light');
|
|
||||||
} else {
|
|
||||||
html.removeAttribute('data-theme');
|
|
||||||
}
|
|
||||||
UI.toast.info(
|
|
||||||
val === 'dark' ? 'Dark Mode aktiviert.' :
|
|
||||||
val === 'light' ? 'Hell-Modus aktiviert.' :
|
|
||||||
'Theme folgt der Systemeinstellung.'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('toggle-pocket-mode')?.addEventListener('change', e => {
|
document.getElementById('toggle-pocket-mode')?.addEventListener('change', e => {
|
||||||
localStorage.setItem('by_pocket_mode', String(e.target.checked));
|
localStorage.setItem('by_pocket_mode', String(e.target.checked));
|
||||||
UI.toast.info(e.target.checked
|
UI.toast.info(e.target.checked
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue