- 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.
418 lines
12 KiB
HTML
418 lines
12 KiB
HTML
<!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 || ' ';
|
||
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 || ' ';
|
||
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> |