Files
fsae41.de/public/ausbildung_quiz.html
BolkeDerBaer 038910e9f0 Add service worker for push notifications, create calendar layout, and implement WLAN QR code page
- Implemented a service worker (sw.js) to handle push notifications with dynamic options and notification click events.
- Created a calendar layout in test.html with a grid system for displaying events across days and times.
- Developed a visually engaging WLAN QR code page (wlan.html) with animated backgrounds, particle effects, and tips for connecting to the network.
2026-02-22 00:50:22 +01:00

418 lines
12 KiB
HTML
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.
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Quiz: Ausbildung</title>
<script src="/lib/pocketbase.umd.js"></script>
<script defer src="https://analytics.fsae41.de/script.js" data-website-id="257da02e-d678-47b6-b036-e3bdabaf1405"></script>
<style>
:root {
--bg: #f7fafc;
--card: #ffffff;
--accent: #2563eb;
--muted: #6b7280;
--correct: #16a34a;
--wrong: #ef4444
}
body {
font-family: Inter, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial;
background: var(--bg);
padding: 18px
}
.card {
background: var(--card);
border-radius: 12px;
box-shadow: 0 6px 18px rgba(15, 23, 42, 0.08);
padding: 18px;
max-width: 760px
}
.q-head {
display: flex;
gap: 12px;
align-items: flex-start
}
.q-id {
display: none
}
h2 {
margin: 0 0 8px 0;
font-size: 18px
}
p.meta {
margin: 0 0 14px 0;
color: var(--muted)
}
p.intro {
margin-bottom: 12px;
color: var(--muted)
}
ul.answers {
list-style: none;
padding: 0;
margin: 0;
display: grid;
gap: 10px
}
ul.answers li {
border: 1px solid #e6e9ef;
border-radius: 10px;
padding: 12px;
cursor: pointer;
transition: all .12s
}
ul.answers li.selected {
border-color: var(--accent);
background: rgba(37, 99, 235, 0.06)
}
ul.answers li:hover {
transform: translateY(-2px)
}
ul.answers li.correct {
border-color: var(--correct);
background: rgba(16, 185, 129, 0.06)
}
ul.answers li.incorrect {
border-color: var(--wrong);
background: rgba(239, 68, 68, 0.06)
}
.badge-container {
display: flex;
justify-content: flex-end;
margin-bottom: 4px
}
.badge {
font-size: 12px;
padding: 4px 8px;
border-radius: 999px;
background: #f1f5f9;
color: var(--muted)
}
.controls {
display: flex;
justify-content: space-between;
margin-top: 12px;
flex-wrap: wrap
}
.control-right {
display: flex;
gap: 8px
}
.btn {
padding: 8px 12px;
border-radius: 8px;
border: 0;
cursor: pointer
}
.btn.primary {
background: var(--accent);
color: #fff
}
.btn.ghost {
background: transparent;
border: 1px solid #e6e9ef
}
.btn.danger {
background: var(--wrong);
color: #fff
}
.hint {
margin-top: 10px;
font-size: 13px;
color: var(--muted)
}
#report-box {
display: none;
margin-top: 12px
}
#report-box textarea {
width: 100%;
min-height: 80px;
border: 1px solid #e6e9ef;
border-radius: 8px;
padding: 8px;
font-family: inherit
}
#report-box button {
margin-top: 8px
}
.dropdown-container {
margin-bottom: 12px
}
select#question-select {
padding: 6px 10px;
border-radius: 6px;
border: 1px solid #e6e9ef;
font-family: inherit
}
</style>
</head>
<body>
<div id="quiz-card" class="card" role="region" aria-label="Ausbildungsfrage">
<div class="dropdown-container">
<label for="question-select">Frage auswählen ID: </label>
<select id="question-select"></select>
</div>
<p class="intro" id="q-textIntro">Als Ausbilder setzen Sie zur Anleitung der Auszubildenden auch die
Vier-Stufen-Methode ein.</p>
<div class="q-head">
<div class="q-id" id="q-id">ID 344</div>
<div style="flex:1">
<h2 id="q-text">Welches der folgenden Merkmale trifft auf die erste Stufe zu?</h2>
<p class="meta" id="q-category">Kategorie: 3</p>
<div class="badge-container">
<p class="badge" id="q-type">1 Antwort richtig</p>
</div>
</div>
</div>
<p class="intro" id="answersIntro">Die Auszubildenden sollen…</p>
<ul id="answers" class="answers" role="list">
<!-- Antworten werden hier gerendert -->
</ul>
<div class="controls">
<button class="btn danger" id="report-error">Fehler melden</button>
<button class="btn" id="last-q">Zurück</button>
<div class="control-right">
<button class="btn" id="next-q">Weiter</button>
<button class="btn ghost" id="clear-selection">Auswahl zurücksetzen</button>
<button class="btn primary" id="show-correct">Korrekte Antwort anzeigen</button>
</div>
</div>
<div id="report-box" style="display: none;">
<textarea id="report-text" placeholder="Bitte beschreiben Sie den Fehler..."></textarea>
<button class="btn primary" id="send-report">Absenden</button>
</div>
</div>
<script>
const state = { item: null, selected: [], allQuestions: [] };
function populateDropdown() {
const select = document.getElementById('question-select');
select.innerHTML = '';
state.allQuestions.forEach(q => {
const option = document.createElement('option');
option.value = q.id;
option.textContent = `${q.id}`;
option.selected = (state.item && state.item.id === q.id);
select.appendChild(option);
});
select.addEventListener('change', () => {
const selectedId = select.value;
const question = state.allQuestions.find(q => q.id == selectedId);
if (question) window.updateQuiz(question);
});
}
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
function render(data) {
state.item = data;
state.selected = [];
document.getElementById('q-id').textContent = 'ID ' + (data.id ?? '');
document.getElementById('q-text').textContent = data.text ?? '';
document.getElementById('q-textIntro').innerHTML = data.textIntro || '&nbsp;';
if (data.textIntro) {
document.getElementById('q-textIntro').style.display = 'block';
} else {
document.getElementById('q-textIntro').style.display = 'none';
}
document.getElementById('q-category').textContent = 'Kategorie: ' + (data.category ?? '');
document.getElementById('answersIntro').innerHTML = data.answersIntro || '&nbsp;';
if (data.answersIntro) {
document.getElementById('answersIntro').style.display = 'block';
} else {
document.getElementById('answersIntro').style.display = 'none';
}
const correctCount = (data.answers || []).filter(a => a.correct).length;
document.getElementById('q-type').textContent = `${correctCount} Antwort${correctCount !== 1 ? 'en' : ''} richtig`;
const answersEl = document.getElementById('answers');
answersEl.innerHTML = '';
let answers = [...(data.answers || [])];
//answers = shuffle(answers);
answers.forEach((a, i) => {
const li = document.createElement('li');
li.setAttribute('role', 'button');
li.tabIndex = 0;
li.dataset.index = i;
li.dataset.answerId = a.id;
li.innerHTML = `<div>${a.text}</div>`;
li.addEventListener('click', () => toggleAnswer(i));
li.addEventListener('keydown', (e) => { if (e.key === "Enter" || e.key === " ") toggleAnswer(i) });
answersEl.appendChild(li);
});
const lis = document.querySelectorAll('#answers li');
lis.forEach(li => { li.classList.remove('incorrect', 'correct', 'selected'); li.style.boxShadow = 'none' });
state.item.shuffledAnswers = answers;
}
function toggleAnswer(index) {
const li = document.querySelectorAll('#answers li')[index];
if (state.selected.includes(index)) {
state.selected = state.selected.filter(i => i !== index);
li.classList.remove('selected');
} else {
state.selected.push(index);
li.classList.add('selected');
}
}
window.updateQuiz = function (data) {
if (data && data.answers) {
data.answers = data.answers.map(a => ({ id: a.id, text: a.text, correct: !!a.correct }));
}
render(data);
};
window.showCorrect = function () {
if (!state.item) return;
const lis = document.querySelectorAll('#answers li');
lis.forEach((li, i) => {
const ans = state.item.answers[i];
if (ans && ans.correct) {
li.classList.add('correct');
if (state.selected.includes(i)) li.classList.add('selected');
} else if (state.selected.includes(i)) {
li.classList.add('incorrect');
}
});
};
window.setCorrect = function ({ index = null, answerId = null } = {}) {
if (!state.item) return;
const answers = state.item.answers;
if (index == null && answerId == null) return;
answers.forEach((a, i) => a.correct = ((index != null && i === index) || (answerId != null && a.id === answerId)));
render(state.item);
};
document.getElementById('show-correct').addEventListener('click', () => window.showCorrect());
document.getElementById('clear-selection').addEventListener('click', () => {
state.selected = [];
const lis = document.querySelectorAll('#answers li');
lis.forEach(li => { li.classList.remove('incorrect', 'correct', 'selected'); li.style.boxShadow = 'none' });
});
const nextbt = document.getElementById("next-q");
const lastbt = document.getElementById("last-q");
nextbt.addEventListener("click", () => {
const select = document.getElementById('question-select');
// Nur weitergehen, wenn es noch ein nächstes Element gibt
if (select.selectedIndex < select.options.length - 1) {
select.selectedIndex++;
const selectedId = select.value;
const question = state.allQuestions.find(q => q.id == selectedId);
if (question) window.updateQuiz(question);
}
})
lastbt.addEventListener("click", () => {
const select = document.getElementById('question-select');
if (select.selectedIndex > 0) {
select.selectedIndex--;
const selectedId = select.value;
const question = state.allQuestions.find(q => q.id == selectedId);
if (question) window.updateQuiz(question);
}
})
const reportBtn = document.getElementById('report-error');
const reportBox = document.getElementById('report-box');
const sendReport = document.getElementById('send-report');
reportBtn.addEventListener('click', () => {
reportBox.style.display = reportBox.style.display === 'none' ? 'block' : 'none';
});
sendReport.addEventListener('click', async () => {
const text = document.getElementById('report-text').value.trim();
if (text) {
console.log('Fehlerbericht gesendet:', text);
alert('Vielen Dank für Ihre Rückmeldung!');
const data = {
"question": state.item.id,
"text": text
};
const record = await pb.collection('ADA_report').create(data);
document.getElementById('report-text').value = '';
reportBox.style.display = 'none';
} else {
alert('Bitte geben Sie eine Fehlerbeschreibung ein.');
}
});
// Beispiel-Initialisierung mit mehreren Fragen
state.allQuestions = [];
let pb = new PocketBase();
(async () => {
const records = await pb.collection('ADA_question').getFullList();
console.log(records);
records.forEach(r => {
const item = r;
state.allQuestions.push(item);
});
let r = Math.floor(Math.random() * state.allQuestions.length);
window.updateQuiz(state.allQuestions[r]);
populateDropdown();
})();
</script>
</body>
</html>