126 lines
4.7 KiB
JavaScript
126 lines
4.7 KiB
JavaScript
|
|
// =====================================================
|
||
|
|
// Eksamenstrener — practice questions with guided answers
|
||
|
|
// =====================================================
|
||
|
|
|
||
|
|
let examData = null;
|
||
|
|
let examFilter = 'all';
|
||
|
|
|
||
|
|
async function examLoad() {
|
||
|
|
if (examData) return examData;
|
||
|
|
try {
|
||
|
|
const res = await fetch('data/exam.json');
|
||
|
|
if (!res.ok) throw new Error('Not ready');
|
||
|
|
examData = await res.json();
|
||
|
|
} catch (e) {
|
||
|
|
console.warn('Eksamen-data ikke klar ennå:', e.message);
|
||
|
|
examData = [];
|
||
|
|
}
|
||
|
|
return examData;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Normalize category names — agents sometimes used "samfunnsansvar"
|
||
|
|
function normalizeCat(cat) {
|
||
|
|
if (cat === 'samfunnsansvar') return 'samfunn';
|
||
|
|
return cat;
|
||
|
|
}
|
||
|
|
|
||
|
|
function examFilteredQuestions() {
|
||
|
|
if (examFilter === 'all') return examData;
|
||
|
|
return examData.filter(q => normalizeCat(q.category) === examFilter);
|
||
|
|
}
|
||
|
|
|
||
|
|
function examCategoryLabel(cat) {
|
||
|
|
return {
|
||
|
|
etikk: 'Oppgave I · Etikk',
|
||
|
|
baerekraft: 'Oppgave II · Bærekraft',
|
||
|
|
samfunn: 'Oppgave III · Samfunnsansvar',
|
||
|
|
case: 'Oppgave IV · Case-drøfting'
|
||
|
|
}[normalizeCat(cat)] || cat;
|
||
|
|
}
|
||
|
|
|
||
|
|
function examCategoryColor(cat) {
|
||
|
|
return {
|
||
|
|
etikk: 'var(--theme-etikk)',
|
||
|
|
baerekraft: 'var(--theme-baerekraft)',
|
||
|
|
samfunn: 'var(--theme-samfunn)',
|
||
|
|
case: 'var(--theme-verktoy)'
|
||
|
|
}[normalizeCat(cat)] || 'var(--accent)';
|
||
|
|
}
|
||
|
|
|
||
|
|
function examRender() {
|
||
|
|
const container = document.getElementById('examQuestions');
|
||
|
|
if (!container) return;
|
||
|
|
|
||
|
|
// Filter chips at top
|
||
|
|
container.innerHTML = `
|
||
|
|
<div class="fc-filters reveal" id="examFilters" style="margin-bottom:var(--sp-7)">
|
||
|
|
<button class="fc-filter ${examFilter==='all'?'fc-filter--active':''}" data-filter="all">Alle</button>
|
||
|
|
<button class="fc-filter ${examFilter==='etikk'?'fc-filter--active':''}" data-filter="etikk">Etikk</button>
|
||
|
|
<button class="fc-filter ${examFilter==='baerekraft'?'fc-filter--active':''}" data-filter="baerekraft">Bærekraft</button>
|
||
|
|
<button class="fc-filter ${examFilter==='samfunn'?'fc-filter--active':''}" data-filter="samfunn">Samfunnsansvar</button>
|
||
|
|
<button class="fc-filter ${examFilter==='case'?'fc-filter--active':''}" data-filter="case">Case</button>
|
||
|
|
</div>
|
||
|
|
<div id="examList"></div>
|
||
|
|
`;
|
||
|
|
|
||
|
|
const list = document.getElementById('examList');
|
||
|
|
const questions = examFilteredQuestions();
|
||
|
|
let counter = 0;
|
||
|
|
questions.forEach(q => {
|
||
|
|
counter++;
|
||
|
|
const num = String(counter).padStart(2, '0');
|
||
|
|
const color = examCategoryColor(q.category);
|
||
|
|
const div = document.createElement('div');
|
||
|
|
div.className = 'exam-q reveal';
|
||
|
|
div.innerHTML = `
|
||
|
|
<div class="exam-q__head">
|
||
|
|
<div class="exam-q__num" style="color:${color}">${num}</div>
|
||
|
|
<div class="exam-q__head-info">
|
||
|
|
<span class="exam-q__category">${examCategoryLabel(q.category)}</span>
|
||
|
|
<span class="exam-q__weight">${q.title || ''}</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<p class="exam-q__sub">${q.question}</p>
|
||
|
|
${q.checklist ? `
|
||
|
|
<h4 style="margin-top:var(--sp-5); font-family:var(--f-mono); font-size:var(--s-0); letter-spacing:0.15em; text-transform:uppercase; color:var(--muted); font-weight:600">Hva må svaret inneholde</h4>
|
||
|
|
<ul style="margin-top:var(--sp-2); padding-left:var(--sp-5)">
|
||
|
|
${q.checklist.map(c => `<li>${c}</li>`).join('')}
|
||
|
|
</ul>
|
||
|
|
` : ''}
|
||
|
|
<button class="exam-q__toggle" data-toggle>Vis veiledet svar ↓</button>
|
||
|
|
<div class="exam-q__reveal" style="display:none" data-answer>
|
||
|
|
<div class="exam-q__reveal-label">Veiledet svar</div>
|
||
|
|
<div class="markdown-content">${SMF.renderMarkdown(q.guidedAnswer || '')}</div>
|
||
|
|
${q.tips ? `<div style="margin-top:var(--sp-4); padding-top:var(--sp-3); border-top:1px solid var(--line); font-family:var(--f-display); font-style:italic; font-size:var(--s-2); color:var(--ink-2)"><strong style="font-family:var(--f-mono); font-size:0.6875rem; letter-spacing:0.15em; text-transform:uppercase; color:var(--accent); font-weight:600; font-style:normal; display:block; margin-bottom:6px">Tips</strong>${q.tips}</div>` : ''}
|
||
|
|
</div>
|
||
|
|
`;
|
||
|
|
list.appendChild(div);
|
||
|
|
});
|
||
|
|
|
||
|
|
// Attach toggles
|
||
|
|
list.querySelectorAll('[data-toggle]').forEach(btn => {
|
||
|
|
btn.addEventListener('click', () => {
|
||
|
|
const reveal = btn.nextElementSibling;
|
||
|
|
const isOpen = reveal.style.display !== 'none';
|
||
|
|
reveal.style.display = isOpen ? 'none' : 'block';
|
||
|
|
btn.textContent = isOpen ? 'Vis veiledet svar ↓' : 'Skjul veiledet svar ↑';
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// Attach filters
|
||
|
|
document.getElementById('examFilters').addEventListener('click', (e) => {
|
||
|
|
const f = e.target.closest('[data-filter]');
|
||
|
|
if (f) {
|
||
|
|
examFilter = f.dataset.filter;
|
||
|
|
examRender();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
async function examInit() {
|
||
|
|
await examLoad();
|
||
|
|
examRender();
|
||
|
|
}
|
||
|
|
|
||
|
|
SMF.examInit = examInit;
|