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).
480 lines
21 KiB
JavaScript
480 lines
21 KiB
JavaScript
/* ============================================================
|
|
BAN YARO — Hunde-Persönlichkeitstest
|
|
10 Fragen, 4 Typen: Abenteurer / Entdecker / Kuschler / Denker
|
|
============================================================ */
|
|
|
|
window.Page_personality = (() => {
|
|
|
|
let _container = null;
|
|
let _appState = null;
|
|
let _current = 0; // Aktuelle Frage (0-basiert)
|
|
let _scores = { A:0, B:0, C:0, D:0 };
|
|
let _answers = []; // Gewählte Typen je Frage
|
|
|
|
const LS_KEY = 'banyaro_personality_result';
|
|
|
|
// ----------------------------------------------------------
|
|
// FRAGEN
|
|
// ----------------------------------------------------------
|
|
const FRAGEN = [
|
|
{ frage: "Wie reagiert dein Hund auf neue Orte?",
|
|
antworten: [
|
|
{ text: "Stürmt sofort los — alles erkunden!", typ: 'A' },
|
|
{ text: "Schaut erst vorsichtig, dann neugierig", typ: 'B' },
|
|
{ text: "Bleibt lieber bei mir in der Nähe", typ: 'C' },
|
|
{ text: "Analysiert die Lage gründlich", typ: 'D' },
|
|
]},
|
|
{ frage: "Was macht dein Hund am liebsten?",
|
|
antworten: [
|
|
{ text: "Rennen, Ball, endlos spielen", typ: 'A' },
|
|
{ text: "Schnüffeln und die Welt erkunden", typ: 'B' },
|
|
{ text: "Kuscheln auf dem Sofa", typ: 'C' },
|
|
{ text: "Tricks lernen und Aufgaben lösen", typ: 'D' },
|
|
]},
|
|
{ frage: "Wie verhält er sich mit anderen Hunden?",
|
|
antworten: [
|
|
{ text: "Spielt sofort und wild mit", typ: 'A' },
|
|
{ text: "Friendly, aber wählerisch", typ: 'B' },
|
|
{ text: "Lieber zu zweit als in der Gruppe", typ: 'C' },
|
|
{ text: "Beobachtet erstmal genau", typ: 'D' },
|
|
]},
|
|
{ frage: "Wie reagiert er auf Kommandos?",
|
|
antworten: [
|
|
{ text: "Macht alles — wenn er Lust hat 😅", typ: 'A' },
|
|
{ text: "Gut, aber manchmal abgelenkt", typ: 'B' },
|
|
{ text: "Sehr zuverlässig, will gefallen", typ: 'C' },
|
|
{ text: "Präzise und fokussiert", typ: 'D' },
|
|
]},
|
|
{ frage: "Was passiert wenn du heimkommst?",
|
|
antworten: [
|
|
{ text: "Explosiver Freudentanz!", typ: 'A' },
|
|
{ text: "Wedelt freudig, bleibt aber cool", typ: 'B' },
|
|
{ text: "Kuschelt sich sofort an dich", typ: 'C' },
|
|
{ text: "Bringt dir sein Lieblingsspielzeug", typ: 'D' },
|
|
]},
|
|
{ frage: "Wie ist er bei Geräuschen/Gewitter?",
|
|
antworten: [
|
|
{ text: "Interessiert sich dafür oder ignoriert es", typ: 'A' },
|
|
{ text: "Schaut kurz, dann weiter", typ: 'B' },
|
|
{ text: "Sucht Schutz bei dir", typ: 'C' },
|
|
{ text: "Analysiert die Situation", typ: 'D' },
|
|
]},
|
|
{ frage: "Sein Verhältnis zu Kindern?",
|
|
antworten: [
|
|
{ text: "Liebt das wilde Spielen!", typ: 'A' },
|
|
{ text: "Gut, aber auf seine Art", typ: 'B' },
|
|
{ text: "Sanft und geduldig", typ: 'C' },
|
|
{ text: "Vorsichtig, aber freundlich", typ: 'D' },
|
|
]},
|
|
{ frage: "Was macht er alleine zu Hause?",
|
|
antworten: [
|
|
{ text: "Schläft oder spielt mit Spielzeug", typ: 'A' },
|
|
{ text: "Schaut aus dem Fenster", typ: 'B' },
|
|
{ text: "Wartet sehnsüchtig auf dich", typ: 'C' },
|
|
{ text: "Sucht sich Beschäftigung", typ: 'D' },
|
|
]},
|
|
{ frage: "Beim Gassigehen:",
|
|
antworten: [
|
|
{ text: "Zieht an der Leine — immer vorwärts!", typ: 'A' },
|
|
{ text: "Läuft locker aber entdeckungsfreudig", typ: 'B' },
|
|
{ text: "Bleibt gerne neben dir", typ: 'C' },
|
|
{ text: "Systematisches Schnüffeln", typ: 'D' },
|
|
]},
|
|
{ frage: "Was sagt er über dich aus?",
|
|
antworten: [
|
|
{ text: "Mein Mensch hält mit mir mit!", typ: 'A' },
|
|
{ text: "Gibt mir Freiheit und Abenteuer", typ: 'B' },
|
|
{ text: "Mein bester Freund", typ: 'C' },
|
|
{ text: "Versteht mich wirklich", typ: 'D' },
|
|
]},
|
|
];
|
|
|
|
// ----------------------------------------------------------
|
|
// TYPEN
|
|
// ----------------------------------------------------------
|
|
const TYPEN = {
|
|
A: {
|
|
key: 'A',
|
|
emoji: '🏔️',
|
|
name: 'Der Abenteurer',
|
|
desc: 'Immer vorwärts, immer mehr! Dein Hund lebt im Augenblick und liebt das Unbekannte.',
|
|
staerken: ['Energiegeladen', 'Mutig', 'Lebensfroh'],
|
|
aktivitaeten: [
|
|
{ label: 'Routen', page: 'routes' },
|
|
{ label: 'Karte', page: 'map' },
|
|
{ label: 'Training', page: 'uebungen' },
|
|
],
|
|
aktivitaetLabels: ['Agility', 'Canicross', 'Lange Wanderungen', 'Nasenarbeit'],
|
|
rassen: ['Husky', 'Malinois', 'Border Collie'],
|
|
color: '#f97316',
|
|
bg: 'linear-gradient(135deg, #f97316, #ea580c)',
|
|
},
|
|
B: {
|
|
key: 'B',
|
|
emoji: '🌍',
|
|
name: 'Der Entdecker',
|
|
desc: 'Neugierig auf alles, aber mit Köpfchen. Dein Hund ist der perfekte Begleiter für jede Situation.',
|
|
staerken: ['Anpassungsfähig', 'Sozial', 'Ausgeglichen'],
|
|
aktivitaeten: [
|
|
{ label: 'Karte', page: 'map' },
|
|
{ label: 'Events', page: 'events' },
|
|
{ label: 'Routen', page: 'routes' },
|
|
],
|
|
aktivitaetLabels: ['Mantrailing', 'Dummy-Training', 'Gassi-Treffen'],
|
|
rassen: ['Labrador', 'Golden Retriever', 'Beagle'],
|
|
color: '#0ea5e9',
|
|
bg: 'linear-gradient(135deg, #0ea5e9, #0284c7)',
|
|
},
|
|
C: {
|
|
key: 'C',
|
|
emoji: '🥰',
|
|
name: 'Der Kuschler',
|
|
desc: 'Verbundenheit über alles. Dein Hund liebt Menschen mehr als alles andere.',
|
|
staerken: ['Loyal', 'Einfühlsam', 'Zuverlässig'],
|
|
aktivitaeten: [
|
|
{ label: 'Tagebuch', page: 'diary' },
|
|
{ label: 'Training', page: 'uebungen' },
|
|
{ label: 'Gesundheit', page: 'health' },
|
|
],
|
|
aktivitaetLabels: ['Trick-Training', 'Therapy-Dog-Ausbildung', 'Ruhige Spaziergänge'],
|
|
rassen: ['Cavalier KCS', 'Bichon Frisé', 'Mops'],
|
|
color: '#ec4899',
|
|
bg: 'linear-gradient(135deg, #ec4899, #db2777)',
|
|
},
|
|
D: {
|
|
key: 'D',
|
|
emoji: '🧠',
|
|
name: 'Der Denker',
|
|
desc: 'Stratege mit Seele. Dein Hund denkt bevor er handelt — und ist dabei brillant.',
|
|
staerken: ['Intelligent', 'Fokussiert', 'Lernbegeistert'],
|
|
aktivitaeten: [
|
|
{ label: 'Übungen', page: 'uebungen' },
|
|
{ label: 'Training', page: 'trainingsplaene' },
|
|
{ label: 'Wiki', page: 'wiki' },
|
|
],
|
|
aktivitaetLabels: ['Zieltraining', 'Geruchsarbeit', 'Rally Obedience', 'Intelligenzspielzeug'],
|
|
rassen: ['Poodle', 'Schäferhund', 'Rottweiler'],
|
|
color: '#8b5cf6',
|
|
bg: 'linear-gradient(135deg, #8b5cf6, #7c3aed)',
|
|
},
|
|
};
|
|
|
|
// ----------------------------------------------------------
|
|
// LIFECYCLE
|
|
// ----------------------------------------------------------
|
|
function init(container, appState) {
|
|
_container = container;
|
|
_appState = appState;
|
|
_renderPage();
|
|
}
|
|
|
|
function refresh() {}
|
|
|
|
function onDogChange() {}
|
|
|
|
// ----------------------------------------------------------
|
|
// RENDER EINSTIEG
|
|
// ----------------------------------------------------------
|
|
function _renderPage() {
|
|
// Gespeichertes Ergebnis aus localStorage?
|
|
const saved = _loadResult();
|
|
if (saved) {
|
|
_renderResult(TYPEN[saved.typ], saved.scores, true);
|
|
} else {
|
|
_renderIntro();
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// INTRO
|
|
// ----------------------------------------------------------
|
|
function _renderIntro() {
|
|
_container.innerHTML = `
|
|
<div class="page-body page-container">
|
|
<div style="text-align:center;padding:var(--space-6) var(--space-4) var(--space-4)">
|
|
<div style="font-size:3.5rem;margin-bottom:var(--space-3)">🐾</div>
|
|
<h1 style="font-size:var(--text-xl);font-weight:800;margin-bottom:var(--space-2)">
|
|
Hunde-Persönlichkeitstest
|
|
</h1>
|
|
<p style="color:var(--c-text-secondary);font-size:var(--text-sm);max-width:320px;margin:0 auto var(--space-6)">
|
|
10 Fragen — finde heraus welcher der 4 Persönlichkeitstypen deinen Hund am besten beschreibt!
|
|
</p>
|
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-3);max-width:340px;margin:0 auto var(--space-6)">
|
|
${Object.values(TYPEN).map(t => `
|
|
<div style="background:var(--c-surface);border:1px solid var(--c-border);border-radius:var(--radius-lg);
|
|
padding:var(--space-3);text-align:center">
|
|
<div style="font-size:1.8rem">${t.emoji}</div>
|
|
<div style="font-size:var(--text-xs);font-weight:600;margin-top:4px;color:${t.color}">${t.name}</div>
|
|
</div>`).join('')}
|
|
</div>
|
|
<button class="btn btn-primary" style="padding:14px 40px;font-size:1rem;font-weight:700;border-radius:999px"
|
|
id="quiz-start-btn">Quiz starten</button>
|
|
</div>
|
|
</div>`;
|
|
|
|
document.getElementById('quiz-start-btn').addEventListener('click', _startQuiz);
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// QUIZ STARTEN
|
|
// ----------------------------------------------------------
|
|
function _startQuiz() {
|
|
_current = 0;
|
|
_scores = { A:0, B:0, C:0, D:0 };
|
|
_answers = [];
|
|
_renderQuestion();
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// FRAGE RENDERN
|
|
// ----------------------------------------------------------
|
|
function _renderQuestion() {
|
|
const q = FRAGEN[_current];
|
|
const pct = Math.round((_current / FRAGEN.length) * 100);
|
|
|
|
_container.innerHTML = `
|
|
<div class="page-body page-container">
|
|
<!-- Fortschritt -->
|
|
<div style="padding:var(--space-4) var(--space-4) 0">
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">
|
|
<span class="text-xs-muted">
|
|
Frage ${_current + 1} von ${FRAGEN.length}
|
|
</span>
|
|
<span style="font-size:var(--text-xs);font-weight:600;color:var(--c-primary)">${pct}%</span>
|
|
</div>
|
|
<div style="height:6px;background:var(--c-border);border-radius:3px;overflow:hidden">
|
|
<div style="height:100%;width:${pct}%;background:var(--c-primary);border-radius:3px;transition:width .4s"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Frage -->
|
|
<div style="padding:var(--space-5) var(--space-4) var(--space-3)">
|
|
<h2 style="font-size:var(--text-lg);font-weight:700;line-height:1.4;margin:0">${q.frage}</h2>
|
|
</div>
|
|
|
|
<!-- Antworten -->
|
|
<div style="padding:0 var(--space-4) var(--space-6);display:flex;flex-direction:column;gap:var(--space-3)">
|
|
${q.antworten.map((a, i) => `
|
|
<button class="quiz-answer-btn" data-typ="${a.typ}"
|
|
style="text-align:left;padding:var(--space-4);background:var(--c-surface);
|
|
border:2px solid var(--c-border);border-radius:var(--radius-lg);
|
|
font-size:var(--text-sm);cursor:pointer;transition:all .15s;
|
|
color:var(--c-text);line-height:1.4;
|
|
display:flex;align-items:center;gap:var(--space-3)">
|
|
<span style="width:28px;height:28px;border-radius:50%;background:var(--c-bg);
|
|
border:1.5px solid var(--c-border);display:flex;align-items:center;justify-content:center;
|
|
font-size:11px;font-weight:700;color:var(--c-text-secondary);flex-shrink:0">
|
|
${String.fromCharCode(65 + i)}
|
|
</span>
|
|
<span>${a.text}</span>
|
|
</button>`).join('')}
|
|
</div>
|
|
</div>`;
|
|
|
|
_container.querySelectorAll('.quiz-answer-btn').forEach(btn => {
|
|
btn.addEventListener('click', () => _answerQuestion(btn.dataset.typ));
|
|
btn.addEventListener('mouseenter', () => {
|
|
btn.style.borderColor = 'var(--c-primary)';
|
|
btn.style.background = 'var(--c-primary-subtle, rgba(var(--c-primary-rgb,59,130,246),.08))';
|
|
});
|
|
btn.addEventListener('mouseleave', () => {
|
|
if (!btn.classList.contains('selected')) {
|
|
btn.style.borderColor = 'var(--c-border)';
|
|
btn.style.background = 'var(--c-surface)';
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// ANTWORT VERARBEITEN
|
|
// ----------------------------------------------------------
|
|
function _answerQuestion(typ) {
|
|
_scores[typ]++;
|
|
_answers.push(typ);
|
|
_current++;
|
|
|
|
if (_current < FRAGEN.length) {
|
|
// Kurze Animation — zeige Auswahl kurz grün
|
|
_renderQuestion();
|
|
} else {
|
|
_calcAndShowResult();
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// AUSWERTUNG
|
|
// ----------------------------------------------------------
|
|
function _calcAndShowResult() {
|
|
// Mehrheits-Typ finden; bei Gleichstand letzter bestimmender Typ
|
|
let maxScore = 0;
|
|
let winner = _answers[_answers.length - 1]; // Fallback: letzte Antwort
|
|
for (const [typ, score] of Object.entries(_scores)) {
|
|
if (score > maxScore) {
|
|
maxScore = score;
|
|
winner = typ;
|
|
}
|
|
}
|
|
// Bei Gleichstand: letzter in _answers der einen der Max-Score-Typen hat
|
|
const maxTypes = Object.entries(_scores)
|
|
.filter(([, s]) => s === maxScore)
|
|
.map(([t]) => t);
|
|
if (maxTypes.length > 1) {
|
|
for (let i = _answers.length - 1; i >= 0; i--) {
|
|
if (maxTypes.includes(_answers[i])) { winner = _answers[i]; break; }
|
|
}
|
|
}
|
|
|
|
_saveResult(winner, _scores);
|
|
_renderResult(TYPEN[winner], _scores, false);
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// ERGEBNIS RENDERN
|
|
// ----------------------------------------------------------
|
|
function _renderResult(typ, scores, fromStorage) {
|
|
const dogName = _appState?.activeDog?.name || 'dein Hund';
|
|
const shareText = `${dogName} ist ${typ.name} ${typ.emoji} — macht den Test auf ban.yaro.de!`;
|
|
|
|
const scoreBars = Object.entries(scores)
|
|
.sort(([,a],[,b]) => b - a)
|
|
.map(([t, s]) => {
|
|
const tp = TYPEN[t];
|
|
const pct = Math.round((s / FRAGEN.length) * 100);
|
|
return `
|
|
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
|
|
<span style="font-size:1rem;width:24px;text-align:center">${tp.emoji}</span>
|
|
<div class="flex-1">
|
|
<div style="height:8px;background:var(--c-border);border-radius:4px;overflow:hidden">
|
|
<div style="height:100%;width:${pct}%;background:${tp.color};border-radius:4px;transition:width .6s"></div>
|
|
</div>
|
|
</div>
|
|
<span style="font-size:var(--text-xs);color:var(--c-text-secondary);width:28px;text-align:right">${s}/${FRAGEN.length}</span>
|
|
</div>`;
|
|
}).join('');
|
|
|
|
_container.innerHTML = `
|
|
<div class="page-body page-container">
|
|
<!-- Hero -->
|
|
<div style="background:${typ.bg};padding:var(--space-8) var(--space-4) var(--space-6);
|
|
text-align:center;color:#fff;border-radius:0 0 var(--radius-xl) var(--radius-xl);
|
|
margin-bottom:var(--space-5)">
|
|
<div style="font-size:4rem;margin-bottom:var(--space-2)">${typ.emoji}</div>
|
|
<div style="font-size:var(--text-xs);font-weight:600;opacity:.8;text-transform:uppercase;letter-spacing:.1em;
|
|
margin-bottom:var(--space-1)">Persönlichkeitstyp</div>
|
|
<h1 style="font-size:var(--text-2xl);font-weight:800;margin:0 0 var(--space-3)">${typ.name}</h1>
|
|
<p style="font-size:var(--text-sm);opacity:.9;max-width:300px;margin:0 auto;line-height:1.5">${typ.desc}</p>
|
|
</div>
|
|
|
|
<!-- Stärken -->
|
|
<div class="card" style="margin:0 var(--space-4) var(--space-4)">
|
|
<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)">Stärken</div>
|
|
<div style="padding:var(--space-3) var(--space-4);display:flex;flex-wrap:wrap;gap:8px">
|
|
${typ.staerken.map(s => `
|
|
<span style="background:${typ.color}22;color:${typ.color};padding:5px 14px;
|
|
border-radius:999px;font-size:var(--text-xs);font-weight:600">${s}</span>`).join('')}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Empfohlene Aktivitäten -->
|
|
<div class="card" style="margin:0 var(--space-4) var(--space-4)">
|
|
<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)">Empfohlene Aktivitäten</div>
|
|
<div style="padding:var(--space-3) var(--space-4)">
|
|
<div style="display:flex;flex-wrap:wrap;gap:8px;margin-bottom:var(--space-3)">
|
|
${typ.aktivitaetLabels.map(a => `
|
|
<span style="background:var(--c-surface);border:1px solid var(--c-border);
|
|
padding:5px 14px;border-radius:999px;font-size:var(--text-xs)">${a}</span>`).join('')}
|
|
</div>
|
|
<div style="display:flex;gap:8px;flex-wrap:wrap">
|
|
${typ.aktivitaeten.map(a => `
|
|
<button class="btn btn-secondary" style="font-size:var(--text-xs);padding:6px 14px;border-radius:999px"
|
|
onclick="App.navigate('${a.page}')">${a.label} →</button>`).join('')}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bekannte Rassen -->
|
|
<div class="card" style="margin:0 var(--space-4) var(--space-4)">
|
|
<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)">Typische Rassen</div>
|
|
<div style="padding:var(--space-3) var(--space-4);display:flex;flex-wrap:wrap;gap:8px">
|
|
${typ.rassen.map(r => `
|
|
<span style="background:var(--c-bg);border:1px solid var(--c-border);
|
|
padding:5px 14px;border-radius:999px;font-size:var(--text-xs)">${r}</span>`).join('')}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Punkteverteilung -->
|
|
<div class="card" style="margin:0 var(--space-4) var(--space-4)">
|
|
<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)">Dein Profil</div>
|
|
<div class="p-4">${scoreBars}</div>
|
|
</div>
|
|
|
|
<!-- Teilen + Nochmal -->
|
|
<div style="padding:0 var(--space-4) var(--space-8);display:flex;flex-direction:column;gap:var(--space-3)">
|
|
<button class="btn btn-secondary" id="quiz-share-btn"
|
|
style="padding:14px;font-size:var(--text-sm);border-radius:var(--radius-lg);
|
|
display:flex;align-items:center;justify-content:center;gap:8px">
|
|
<svg style="width:18px;height:18px" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/>
|
|
<polyline points="16 6 12 2 8 6"/>
|
|
<line x1="12" y1="2" x2="12" y2="15"/>
|
|
</svg>
|
|
Ergebnis teilen
|
|
</button>
|
|
<button class="btn btn-primary" id="quiz-restart-btn"
|
|
style="padding:14px;font-size:var(--text-sm);border-radius:var(--radius-lg)">
|
|
Nochmal machen
|
|
</button>
|
|
</div>
|
|
</div>`;
|
|
|
|
// Share
|
|
document.getElementById('quiz-share-btn')?.addEventListener('click', async () => {
|
|
if (navigator.share) {
|
|
try {
|
|
await navigator.share({ text: shareText, url: 'https://ban.yaro.de' });
|
|
} catch {}
|
|
} else {
|
|
await navigator.clipboard.writeText(shareText);
|
|
UI.toast.success('In die Zwischenablage kopiert!');
|
|
}
|
|
});
|
|
|
|
// Neustart
|
|
document.getElementById('quiz-restart-btn')?.addEventListener('click', () => {
|
|
localStorage.removeItem(LS_KEY);
|
|
_startQuiz();
|
|
});
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// LOCALSTORAGE
|
|
// ----------------------------------------------------------
|
|
function _saveResult(typ, scores) {
|
|
try {
|
|
localStorage.setItem(LS_KEY, JSON.stringify({ typ, scores, ts: Date.now() }));
|
|
} catch {}
|
|
}
|
|
|
|
function _loadResult() {
|
|
try {
|
|
const raw = localStorage.getItem(LS_KEY);
|
|
if (!raw) return null;
|
|
return JSON.parse(raw);
|
|
} catch { return null; }
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// PUBLIC
|
|
// ----------------------------------------------------------
|
|
return { init, refresh, onDogChange };
|
|
|
|
})();
|