import { apiGet, apiPost, apiPut, apiDelete } from '../api.js'; import { canEdit, showToast, getRole } from '../app.js'; import { navigate } from '../router.js'; export async function dashboardPage() { const app = document.getElementById('app'); app.innerHTML = `
`; if (canEdit()) { document.getElementById('headerActions').innerHTML = ` + New Questionnaire `; bindImportBundle(); } try { const data = await apiGet('questionnaires.php'); renderGrid(data.questionnaires || []); } catch (e) { showToast(e.message, 'error'); document.getElementById('qGrid').innerHTML = `

${e.message}

`; } } function renderGrid(questionnaires) { const grid = document.getElementById('qGrid'); if (!questionnaires.length) { grid.innerHTML = `

No questionnaires yet

Create your first questionnaire to get started.

`; return; } grid.innerHTML = questionnaires.map(q => { const stateClass = `badge-${(q.state || 'draft').toLowerCase().replace(/\s+/g, '_')}`; const showPts = parseInt(q.showPoints || 0, 10); return `
${canEdit() ? '' : ''} ${esc(q.name)}
${esc(q.state || 'draft')}
v${esc(q.version || '—')} ${q.questionCount || 0} question${q.questionCount == 1 ? '' : 's'} #${q.orderIndex ?? '—'} ${showPts ? 'pts' : ''}
Edit Results ${getRole() === 'admin' ? `` : ''}
`; }).join(''); grid.querySelectorAll('.q-card').forEach(card => { card.addEventListener('click', (e) => { if (e.target.closest('.q-card-actions') || e.target.closest('.drag-handle')) return; navigate(`#/questionnaire/${card.dataset.id}`); }); }); grid.querySelectorAll('.delete-q-btn').forEach(btn => { btn.addEventListener('click', async (e) => { e.stopPropagation(); if (!confirm('Delete this questionnaire and all its data?')) return; try { await apiDelete('questionnaires.php', { questionnaireID: btn.dataset.id }); showToast('Questionnaire deleted', 'success'); btn.closest('.q-card').remove(); } catch (err) { showToast(err.message, 'error'); } }); }); if (canEdit()) initGridDrag(questionnaires); } function initGridDrag(questionnaires) { const grid = document.getElementById('qGrid'); if (!grid) return; let dragItem = null; grid.addEventListener('dragstart', (e) => { const card = e.target.closest('.q-card'); if (!card || !e.target.closest('.drag-handle')) { e.preventDefault(); return; } dragItem = card; card.style.opacity = '0.5'; e.dataTransfer.effectAllowed = 'move'; }); grid.addEventListener('dragover', (e) => { e.preventDefault(); if (!dragItem) return; const cards = [...grid.querySelectorAll('.q-card:not([style*="opacity"])')]; const afterCard = cards.reduce((closest, child) => { const box = child.getBoundingClientRect(); const offset = e.clientY - box.top - box.height / 2; if (offset < 0 && offset > closest.offset) return { offset, element: child }; return closest; }, { offset: Number.NEGATIVE_INFINITY }).element; if (afterCard) grid.insertBefore(dragItem, afterCard); else grid.appendChild(dragItem); }); grid.addEventListener('dragend', async () => { if (!dragItem) return; dragItem.style.opacity = ''; const newOrder = [...grid.querySelectorAll('.q-card')].map(el => el.dataset.id); dragItem = null; for (let i = 0; i < newOrder.length; i++) { const id = newOrder[i]; const q = questionnaires.find(q => q.questionnaireID === id); if (q && parseInt(q.orderIndex, 10) !== i) { try { await apiPut('questionnaires.php', { questionnaireID: id, orderIndex: i }); q.orderIndex = i; } catch (e) { showToast(e.message, 'error'); return; } } } showToast('Order saved', 'success'); }); } function bindImportBundle() { const fileInput = document.getElementById('importBundleFile'); const btn = document.getElementById('importBundleBtn'); if (!fileInput || !btn) return; btn.addEventListener('click', () => fileInput.click()); fileInput.addEventListener('change', async () => { const file = fileInput.files?.[0]; fileInput.value = ''; if (!file) return; let bundle; try { const text = await file.text(); bundle = JSON.parse(text); } catch { showToast('Invalid JSON file', 'error'); return; } if (!bundle?.questionnaires || !Array.isArray(bundle.questionnaires)) { showToast('Not a questionnaire bundle (missing questionnaires array)', 'error'); return; } const count = bundle.questionnaires.length; if (!confirm(`Import ${count} questionnaire(s) from this file?`)) return; const replace = confirm( 'If a questionnaire ID from the file already exists:\n\n' + 'OK — replace that questionnaire\n' + 'Cancel — import as a new copy (new ID)' ); btn.disabled = true; const prev = btn.textContent; btn.textContent = 'Importing…'; try { const result = await apiPost('questionnaires.php', { action: 'importBundle', bundle, replaceIfExists: replace, }); const n = result.imported ?? count; showToast(`Imported ${n} questionnaire(s)`, 'success'); await dashboardPage(); } catch (e) { showToast(e.message, 'error'); } finally { btn.disabled = false; btn.textContent = prev; } }); } function esc(s) { const d = document.createElement('div'); d.textContent = s ?? ''; return d.innerHTML; }