// ============================================================ // ADMIN: DEVELOPMENTAL MILESTONES MANAGEMENT // ============================================================ (function() { var _inited = false; document.addEventListener('tabChanged', function(e) { if (e.detail.tab !== 'admin' || _inited) return; _inited = true; initMilestonesAdmin(); }); var milestones = []; var ageGroups = []; var domains = []; var editingId = null; function initMilestonesAdmin() { console.log('[AdminMilestones] Initializing...'); // Buttons document.getElementById('btn-refresh-milestones')?.addEventListener('click', loadMilestones); document.getElementById('btn-add-milestone')?.addEventListener('click', function() { editingId = null; openMilestoneModal(); }); document.getElementById('btn-close-milestone-modal')?.addEventListener('click', closeMilestoneModal); document.getElementById('btn-cancel-milestone-modal')?.addEventListener('click', closeMilestoneModal); document.getElementById('btn-save-milestone')?.addEventListener('click', saveMilestone); document.getElementById('btn-bulk-import-milestones')?.addEventListener('click', bulkImportMilestones); document.getElementById('btn-reimport-milestones')?.addEventListener('click', function() { showConfirm('This will DELETE all existing milestones and re-import from static data. Continue?', function() { bulkImportMilestones(true); }, { danger: true, confirmText: 'Re-import' }); }); // Filter document.getElementById('ms-filter-age')?.addEventListener('change', renderMilestones); // Load data loadMetadata(); loadMilestones(); } function loadMetadata() { fetch('/api/admin/milestones/meta', { headers: getAuthHeaders() }) .then(r => r.json()) .then(data => { if (!data.success) return; ageGroups = data.age_groups || []; domains = data.domains || []; // Populate filter dropdown var filterSelect = document.getElementById('ms-filter-age'); if (filterSelect) { filterSelect.innerHTML = ''; ageGroups.forEach(function(age) { var opt = document.createElement('option'); opt.value = age; opt.textContent = age; filterSelect.appendChild(opt); }); } // Populate datalists for modal var ageDatalist = document.getElementById('ms-age-list'); if (ageDatalist) { ageDatalist.innerHTML = ''; ageGroups.forEach(function(age) { var opt = document.createElement('option'); opt.value = age; ageDatalist.appendChild(opt); }); } var domainDatalist = document.getElementById('ms-domain-list'); if (domainDatalist) { domainDatalist.innerHTML = ''; domains.forEach(function(domain) { var opt = document.createElement('option'); opt.value = domain; domainDatalist.appendChild(opt); }); } }) .catch(err => console.error('[AdminMilestones] Failed to load metadata:', err)); } function loadMilestones() { var filterAge = document.getElementById('ms-filter-age')?.value; var url = '/api/admin/milestones' + (filterAge ? '?age_group=' + encodeURIComponent(filterAge) : ''); fetch(url, { headers: getAuthHeaders() }) .then(r => r.json()) .then(data => { if (!data.success) { showToast(data.error || 'Failed to load milestones', 'error'); return; } milestones = data.milestones || []; // Show/hide empty notice var emptyNotice = document.getElementById('ms-empty-notice'); if (emptyNotice) { emptyNotice.style.display = (milestones.length === 0 && !filterAge) ? 'block' : 'none'; } renderMilestones(); }) .catch(err => { console.error('[AdminMilestones] Failed to load:', err); showToast('Failed to load milestones: ' + err.message, 'error'); }); } function renderMilestones() { var list = document.getElementById('milestones-list'); if (!list) return; var filterAge = document.getElementById('ms-filter-age')?.value; var filtered = filterAge ? milestones.filter(m => m.age_group === filterAge) : milestones; if (filtered.length === 0) { list.innerHTML = '

No milestones found. Click "Add Milestone" to create one.

'; return; } // Group by age_group and domain var grouped = {}; filtered.forEach(function(m) { if (!grouped[m.age_group]) grouped[m.age_group] = {}; if (!grouped[m.age_group][m.domain]) grouped[m.age_group][m.domain] = []; grouped[m.age_group][m.domain].push(m); }); var html = ''; Object.keys(grouped).forEach(function(age) { html += '
'; html += '

' + age + '

'; Object.keys(grouped[age]).forEach(function(domain) { html += '
'; html += '
' + domain + '
'; grouped[age][domain].forEach(function(m) { html += '
'; html += '' + m.milestone_text + ''; html += ''; html += ''; html += '
'; }); html += '
'; }); html += '
'; }); list.innerHTML = html; } function openMilestoneModal(milestone) { var modal = document.getElementById('milestone-modal'); var title = document.getElementById('milestone-modal-title'); if (!modal) return; if (milestone) { title.innerHTML = ' Edit Milestone'; document.getElementById('ms-edit-id').value = milestone.id; document.getElementById('ms-edit-age').value = milestone.age_group; document.getElementById('ms-edit-domain').value = milestone.domain; document.getElementById('ms-edit-text').value = milestone.milestone_text; document.getElementById('ms-edit-sort').value = milestone.sort_order; } else { title.innerHTML = ' Add Milestone'; document.getElementById('ms-edit-id').value = ''; document.getElementById('ms-edit-age').value = ''; document.getElementById('ms-edit-domain').value = ''; document.getElementById('ms-edit-text').value = ''; document.getElementById('ms-edit-sort').value = '0'; } modal.style.display = 'flex'; } function closeMilestoneModal() { var modal = document.getElementById('milestone-modal'); if (modal) modal.style.display = 'none'; } function saveMilestone() { var id = document.getElementById('ms-edit-id').value; var age_group = document.getElementById('ms-edit-age').value.trim(); var domain = document.getElementById('ms-edit-domain').value.trim(); var milestone_text = document.getElementById('ms-edit-text').value.trim(); var sort_order = parseInt(document.getElementById('ms-edit-sort').value) || 0; if (!age_group || !domain || !milestone_text) { showToast('Please fill in all fields', 'error'); return; } var method = id ? 'PUT' : 'POST'; var url = id ? '/api/admin/milestones/' + id : '/api/admin/milestones'; fetch(url, { method: method, headers: getAuthHeaders(), body: JSON.stringify({ age_group, domain, milestone_text, sort_order }) }) .then(r => r.json()) .then(data => { if (!data.success) { showToast(data.error || 'Failed to save', 'error'); return; } showToast(id ? 'Milestone updated' : 'Milestone added', 'success'); closeMilestoneModal(); loadMetadata(); loadMilestones(); }) .catch(err => { console.error('[AdminMilestones] Save error:', err); showToast('Failed to save: ' + err.message, 'error'); }); } window.editMilestone = function(id) { var milestone = milestones.find(m => m.id === id); if (!milestone) return; editingId = id; openMilestoneModal(milestone); }; window.deleteMilestone = function(id) { showConfirm('Delete this milestone? This action cannot be undone.', function() { fetch('/api/admin/milestones/' + id, { method: 'DELETE', headers: getAuthHeaders() }) .then(r => r.json()) .then(data => { if (!data.success) { showToast(data.error || 'Failed to delete', 'error'); return; } showToast('Milestone deleted', 'success'); loadMetadata(); loadMilestones(); }) .catch(err => { console.error('[AdminMilestones] Delete error:', err); showToast('Failed to delete: ' + err.message, 'error'); }); }, { danger: true, confirmText: 'Delete' }); }; function bulkImportMilestones(clearExisting) { if (!window.MILESTONES_DATA_STATIC) { showToast('Static milestones data not available', 'error'); return; } var btn = clearExisting ? document.getElementById('btn-reimport-milestones') : document.getElementById('btn-bulk-import-milestones'); if (btn) { btn.disabled = true; btn.innerHTML = ' Importing...'; } // Convert static data to array format for bulk import var milestonesToImport = []; var sortOrder = 0; Object.keys(window.MILESTONES_DATA_STATIC).forEach(function(ageGroup) { Object.keys(window.MILESTONES_DATA_STATIC[ageGroup]).forEach(function(domain) { var items = window.MILESTONES_DATA_STATIC[ageGroup][domain]; items.forEach(function(text) { milestonesToImport.push({ age_group: ageGroup, domain: domain, milestone_text: text, sort_order: sortOrder++ }); }); }); }); // Import directly (backend will handle existing data) fetch('/api/admin/milestones/bulk-import', { method: 'POST', headers: getAuthHeaders(), body: JSON.stringify({ milestones: milestonesToImport, clearExisting: clearExisting }) }) .then(function(r) { return r.json(); }) .then(function(data) { if (btn) { btn.disabled = false; btn.innerHTML = clearExisting ? ' Re-import All' : ' Import Default Milestones Data'; } if (!data.success) { showToast(data.error || 'Import failed', 'error'); return; } showToast('Successfully imported ' + data.imported + ' milestones', 'success'); loadMetadata(); loadMilestones(); }) .catch(function(err) { if (btn) { btn.disabled = false; btn.innerHTML = clearExisting ? ' Re-import All' : ' Import Default Milestones Data'; } console.error('[AdminMilestones] Import error:', err); showToast('Import failed: ' + err.message, 'error'); }); } })();