let children = []; function showToast(msg) { const toast = document.getElementById('toast'); toast.textContent = msg; toast.classList.add('show'); setTimeout(() => toast.classList.remove('show'), 2200); } function fmtDate(iso) { if (!iso) return ''; return new Date(iso).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' }); } async function boot() { try { const me = await api.get('/api/auth/me'); document.getElementById('householdName').textContent = me.household.name; document.getElementById('whoami').textContent = `Logged in as ${me.parent.name} (${me.parent.email})`; } catch { window.location.href = '/login.html'; return; } await Promise.all([loadParents(), loadChildren()]); } async function loadParents() { const { parents } = await api.get('/api/household'); const el = document.getElementById('parentsList'); el.innerHTML = ''; parents.forEach((p) => { const card = document.createElement('div'); card.className = 'entity-card'; card.innerHTML = `

${escapeHtml(p.name)}

${escapeHtml(p.email)}
`; el.appendChild(card); }); } async function loadChildren() { const data = await api.get('/api/children'); children = data.children; const el = document.getElementById('childrenList'); el.innerHTML = ''; if (children.length === 0) { el.innerHTML = '
No children yet — add one to create their first calendar.
'; return; } for (const child of children) { const card = await renderChildCard(child); el.appendChild(card); } } async function renderChildCard(child) { const { calendars } = await api.get(`/api/children/${child.id}/calendars`); const card = document.createElement('div'); card.className = 'entity-card'; const calendarsHtml = calendars.length ? calendars.map((c) => calendarRowHtml(child, c)).join('') : '
No calendars yet.
'; card.innerHTML = `

${escapeHtml(child.name)}

${calendars.length} calendar${calendars.length === 1 ? '' : 's'}
${calendarsHtml}
`; card.querySelectorAll('[data-action]').forEach((btn) => { btn.addEventListener('click', () => handleCardAction(btn.dataset.action, btn.dataset)); }); return card; } function calendarRowHtml(child, cal) { const isActive = child.activeCalendarId === cal.id; return `
${escapeHtml(cal.title)}
Updated ${fmtDate(cal.updated_at)}${isActive ? ' · on tablet' : ''}
Open ${isActive ? '' : ``}
`; } function escapeHtml(str) { const div = document.createElement('div'); div.textContent = str; return div.innerHTML; } async function handleCardAction(action, ds) { if (action === 'new-cal') { document.getElementById('newCalChildId').value = ds.child; document.getElementById('newCalTitle').value = ''; document.getElementById('newCalDuplicate').checked = false; openModal('newCalendarModal'); } else if (action === 'duplicate-cal') { const title = prompt('Title for the duplicated calendar:', `Copy of ${ds.title}`); if (!title) return; await api.post(`/api/children/${ds.child}/calendars`, { title, duplicateFromCalendarId: Number(ds.cal) }); showToast('Calendar duplicated'); await loadChildren(); } else if (action === 'set-active') { await api.patch(`/api/children/${ds.child}`, { activeCalendarId: Number(ds.cal) }); showToast('Tablet will now show this calendar'); await loadChildren(); } else if (action === 'delete-cal') { if (!confirm('Delete this calendar? This cannot be undone.')) return; await api.del(`/api/calendars/${ds.cal}`); showToast('Calendar deleted'); await loadChildren(); } else if (action === 'kiosk-link') { const child = children.find((c) => c.id === Number(ds.child)); openKioskModal(child); } else if (action === 'delete-child') { if (!confirm('Delete this child and all their calendars? This cannot be undone.')) return; await api.del(`/api/children/${ds.child}`); showToast('Child deleted'); await loadChildren(); } } function openModal(id) { document.getElementById(id).classList.remove('hidden'); } function closeModal(id) { document.getElementById(id).classList.add('hidden'); } document.getElementById('addChildBtn').addEventListener('click', () => { document.getElementById('childNameInput').value = ''; openModal('addChildModal'); }); document.getElementById('cancelAddChildBtn').addEventListener('click', () => closeModal('addChildModal')); document.getElementById('addChildForm').addEventListener('submit', async (e) => { e.preventDefault(); await api.post('/api/children', { name: document.getElementById('childNameInput').value }); closeModal('addChildModal'); showToast('Child added'); await loadChildren(); }); document.getElementById('cancelNewCalBtn').addEventListener('click', () => closeModal('newCalendarModal')); document.getElementById('newCalendarForm').addEventListener('submit', async (e) => { e.preventDefault(); const childId = document.getElementById('newCalChildId').value; const title = document.getElementById('newCalTitle').value; const duplicate = document.getElementById('newCalDuplicate').checked; let body = { title }; if (duplicate) { const { calendars } = await api.get(`/api/children/${childId}/calendars`); if (calendars.length) body.duplicateFromCalendarId = calendars[0].id; } const { calendar } = await api.post(`/api/children/${childId}/calendars`, body); closeModal('newCalendarModal'); window.location.href = `/calendar.html?calendarId=${calendar.id}`; }); document.getElementById('inviteBtn').addEventListener('click', async () => { const { invite } = await api.post('/api/household/invites'); const link = `${window.location.origin}${invite.joinPath}`; document.getElementById('inviteLinkField').value = link; openModal('inviteModal'); }); document.getElementById('closeInviteBtn').addEventListener('click', () => closeModal('inviteModal')); document.getElementById('copyInviteBtn').addEventListener('click', () => { document.getElementById('inviteLinkField').select(); navigator.clipboard.writeText(document.getElementById('inviteLinkField').value); showToast('Link copied'); }); let kioskModalChild = null; function openKioskModal(child) { kioskModalChild = child; document.getElementById('kioskChildName').textContent = child.name; document.getElementById('kioskLinkField').value = `${window.location.origin}${child.kioskPath}`; openModal('kioskModal'); } document.getElementById('closeKioskBtn').addEventListener('click', () => closeModal('kioskModal')); document.getElementById('copyKioskBtn').addEventListener('click', () => { document.getElementById('kioskLinkField').select(); navigator.clipboard.writeText(document.getElementById('kioskLinkField').value); showToast('Link copied'); }); document.getElementById('regenKioskBtn').addEventListener('click', async () => { if (!confirm('Regenerate this tablet link? The old link will stop working.')) return; const data = await api.post(`/api/children/${kioskModalChild.id}/kiosk-token/regenerate`); document.getElementById('kioskLinkField').value = `${window.location.origin}${data.kioskPath}`; showToast('Link regenerated'); await loadChildren(); }); document.getElementById('logoutBtn').addEventListener('click', async () => { await api.post('/api/auth/logout'); window.location.href = '/login.html'; }); boot();