| # | Vimeo ID | Thumb | Title | Description | Category |
|---|
| # | Title | URL | Description | Category |
|---|
| # | Vimeo ID | Thumb | Title |
|---|
// ============================================================ // STATE // ============================================================ const SHEET_ID = '1vwxaQgR627bIsVO2oCYp-Ra2NRw1ae8ivADx245TtgA'; const DRIVE_FOLDER = '1JxOOHrKRylak2Mt7O6LoRDisslis-adJ'; const PASS = 'arego2026'; const LS_KEY = 'arego_hub_v2';
let state = { fields: { home: {}, gratitude: {}, mission: {}, community: {}, peptalks: {}, mybusiness: {}, profile: {} }, styles: { home: {}, gratitude: {}, mission: {}, community: {}, peptalks: {}, mybusiness: {}, profile: {} }, tables: { peptalks: [], resources: [], training: [] }, dirty: {}, lastSaved: null, lastPublished: null, publishedState: null };
let currentSection = 'home'; let mediaTargetField = null; let previewOpen = false;
// ============================================================ // INIT // ============================================================ function init() { loadFromStorage(); restoreFields(); renderAllTables(); updateSavedLabel(); loadSheetData(); }
function loadFromStorage() { try { const saved = localStorage.getItem(LS_KEY); if (saved) { const parsed = JSON.parse(saved); state = Object.assign(state, parsed); } } catch(e) {} }
function saveToStorage() { state.lastSaved = Date.now(); localStorage.setItem(LS_KEY, JSON.stringify(state)); updateSavedLabel(); }
function updateSavedLabel() { const el = document.getElementById('savedLabel'); const dot = document.getElementById('savedDot'); if (!state.lastSaved) { el.textContent = 'Not saved'; return; } const mins = Math.floor((Date.now() - state.lastSaved) / 60000); el.textContent = mins < 1 ? 'Saved just now' : `Saved ${mins}m ago`; dot.style.background = '#10B981'; } function restoreFields() { Object.keys(state.fields).forEach(section => { Object.keys(state.fields[section]).forEach(key => { const el = document.getElementById(`${section}_${key}`); if (el) { el.value = state.fields[section][key]; if (key.toLowerCase().includes('image') || key.toLowerCase().includes('img')) { updateImgPreview(el, `${section}_heroImg`); } } }); }); }
// ============================================================ // PASSWORD // ============================================================ document.getElementById('gateInput').addEventListener('keydown', e => { if (e.key==='Enter') checkPassword(); });
function checkPassword() { const val = document.getElementById('gateInput').value; if (val === PASS) { document.getElementById('gate').style.display = 'none'; document.getElementById('app').classList.add('visible'); init(); } else { document.getElementById('gateError').style.display = 'block'; document.getElementById('gateInput').value = ''; document.getElementById('gateInput').focus(); } }
// ============================================================ // NAVIGATION // ============================================================ function switchSection(section, el) { currentSection = section; document.querySelectorAll('.section-panel').forEach(p => p.classList.remove('active')); document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active')); document.getElementById('panel-'+section).classList.add('active'); el.classList.add('active'); if (previewOpen) { setTimeout(() => refreshPreview(), 300); } }
// ============================================================ // FIELD CHANGES // ============================================================ function onFieldChange(section, key, value) { state.fields[section][key] = value; markDirty(section); debounceSave(); }
function onStyleChange(section, key, value) { if (!state.styles[section]) state.styles[section] = {}; state.styles[section][key] = value; markDirty(section); debounceSave(); }
function markDirty(section) { state.dirty[section] = true; const navItem = document.querySelector(`.nav-item[data-section="${section}"]`); if (navItem) navItem.classList.add('dirty'); }
let saveTimer; function debounceSave() { clearTimeout(saveTimer); saveTimer = setTimeout(() => saveToStorage(), 800); }
// ============================================================ // IMAGE / LINK UTILS // ============================================================ function updateImgPreview(input, imgId) { const val = input.value.trim(); const img = document.getElementById(imgId); if (!img) return; if (val && (val.startsWith('http') || val.startsWith('/'))) { img.src = val; img.classList.add('visible'); img.onerror = () => img.classList.remove('visible'); } else { img.classList.remove('visible'); } }
function testLink(url) { if (url) window.open(url, '_blank'); else showToast('No URL entered'); }
function detectLinkType(input, iconId) { const val = input.value.trim().toLowerCase(); const el = document.getElementById(iconId); if (!el) return; if (!val) { el.textContent = ''; return; } if (val.includes('vimeo.com')) { el.textContent = '🎬'; // Auto-extract Vimeo ID if full URL const match = val.match(/vimeo\.com\/(\d+)/); if (match) el.title = 'Vimeo ID: ' + match[1]; } else if (val.includes('youtube.com') || val.includes('youtu.be')) { el.textContent = '📺'; } else if (val.match(/\.(jpg|jpeg|png|gif|webp|svg)$/)) { el.textContent = '🖼️'; } else if (val.match(/\.(pdf|doc|docx|xls|xlsx)$/)) { el.textContent = '📄'; } else { el.textContent = '🔗'; } }
function extractVimeoId(input) { const val = input.value.trim(); const match = val.match(/vimeo\.com\/(\d+)/); if (match) { input.value = match[1]; input.dispatchEvent(new Event('input')); } }
// ============================================================ // STYLE TOOLBAR // ============================================================ function toggleStyleBar(id) { const el = document.getElementById(id); if (!el) return; el.classList.toggle('visible'); }
// ============================================================ // TABLES - PEP TALKS // ============================================================ function addPepTalkRow(data) { const row = data || { vimeoId:'', title:'', description:'', category:'Full Episode' }; state.tables.peptalks.push(row); renderPepTalksTable(); markDirty('peptalks'); debounceSave(); }
function renderPepTalksTable() { const tbody = document.getElementById('peptalksBody'); tbody.innerHTML = ''; state.tables.peptalks.forEach((row, i) => { const tr = document.createElement('tr'); tr.innerHTML = `

`; tbody.appendChild(tr); }); document.getElementById('peptalksCount').textContent = `${state.tables.peptalks.length} video${state.tables.peptalks.length!==1?'s':''}`; }
function updateVimeoThumb(input, imgId) { const val = input.value.trim(); const img = document.getElementById(imgId); if (!img) return; if (val && /^\d+$/.test(val)) { img.src = `https://vumbnail.com/${val}.jpg`; img.classList.add('visible'); } else { img.classList.remove('visible'); } }
// RESOURCES TABLE function addResourceRow(data) { const row = data || { title:'', url:'', description:'', category:'General' }; state.tables.resources.push(row); renderResourcesTable(); markDirty('mybusiness'); debounceSave(); }
function renderResourcesTable() { const tbody = document.getElementById('resourcesBody'); tbody.innerHTML = ''; state.tables.resources.forEach((row, i) => { const tr = document.createElement('tr'); tr.innerHTML = `
`; tbody.appendChild(tr); }); document.getElementById('resourcesCount').textContent = `${state.tables.resources.length} resource${state.tables.resources.length!==1?'s':''}`; }
// TRAINING TABLE function addTrainingRow(data) { const row = data || { vimeoId:'', title:'' }; state.tables.training.push(row); renderTrainingTable(); markDirty('mybusiness'); debounceSave(); }
function renderTrainingTable() { const tbody = document.getElementById('trainingBody'); tbody.innerHTML = ''; state.tables.training.forEach((row, i) => { const tr = document.createElement('tr'); tr.innerHTML = `

`; tbody.appendChild(tr); }); }
function updateTableCell(table, idx, key, value) { state.tables[table][idx][key] = value; if (table === 'peptalks') markDirty('peptalks'); else markDirty('mybusiness'); debounceSave(); }
function deleteRow(table, idx) { state.tables[table].splice(idx, 1); if (table === 'peptalks') renderPepTalksTable(); else if (table === 'resources') renderResourcesTable(); else renderTrainingTable(); debounceSave(); }
function renderAllTables() { renderPepTalksTable(); renderResourcesTable(); renderTrainingTable(); }
// ============================================================ // GOOGLE SHEET DATA LOAD // ============================================================ async function loadSheetData() { const tabs = ['PepTalks','Resources','Announcements']; for (const tab of tabs) { try { const url = `https://docs.google.com/spreadsheets/d/${SHEET_ID}/gviz/tq?tqx=out:csv&sheet=${tab}`; const r = await fetch(url); const csv = await r.text(); const rows = parseCSV(csv); if (tab === 'PepTalks' && rows.length > 1 && state.tables.peptalks.length === 0) { rows.slice(1).forEach(row => { if (row.length >= 1 && row[0]) { addPepTalkRow({ vimeoId: row[0]||'', title: row[1]||'', description: row[2]||'', category: row[3]||'Full Episode' }); } }); } else if (tab === 'Resources' && rows.length > 1 && state.tables.resources.length === 0) { rows.slice(1).forEach(row => { if (row.length >= 1 && row[0]) { addResourceRow({ title: row[0]||'', url: row[1]||'', description: row[2]||'', category: row[3]||'General' }); } }); } else if (tab === 'Announcements' && rows.length > 1) { const r1 = rows[1]; if (r1 && !state.fields.home.bannerText) { const bannerEl = document.getElementById('home_bannerText'); if (bannerEl && r1[0]) { bannerEl.value = r1[0]; onFieldChange('home','bannerText',r1[0]); } const linkEl = document.getElementById('home_bannerLink'); if (linkEl && r1[1]) { linkEl.value = r1[1]; onFieldChange('home','bannerLink',r1[1]); } } } } catch(e) { console.warn('Sheet load failed for', tab, e); } } }
function parseCSV(text) { const rows = []; const lines = text.split('\n'); for (const line of lines) { if (!line.trim()) continue; const row = []; let inQuote = false, cur = ''; for (let i = 0; i < line.length; i++) { const c = line[i]; if (c === '"') { if (inQuote && line[i+1] === '"') { cur += '"'; i++; } else inQuote = !inQuote; } else if (c === ',' && !inQuote) { row.push(cur.trim()); cur = ''; } else { cur += c; } } row.push(cur.trim()); rows.push(row); } return rows; } // ============================================================ // PREVIEW PANE // ============================================================ function togglePreview() { previewOpen = !previewOpen; const pane = document.getElementById('previewPane'); const btn = document.getElementById('previewToggle'); pane.classList.toggle('open', previewOpen); btn.classList.toggle('active', previewOpen); if (previewOpen) { document.getElementById('previewFrame').src = 'https://aregoapp.netlify.app'; } } function refreshPreview() { const frame = document.getElementById('previewFrame'); frame.src = frame.src; } // ============================================================ // PUBLISH MODAL // ============================================================ function openPublish() { buildPublishContent(); document.getElementById('publishModal').classList.add('open'); } function buildPublishContent() { // CSV PepTalks let pt = 'VimeoID,Title,Description,Category\n'; state.tables.peptalks.forEach(r => { pt += `"${r.vimeoId}","${r.title}","${r.description}","${r.category}"\n`; }); document.getElementById('csvPeptalks').textContent = pt.trim() || '(no pep talks)';
// CSV Resources let res = 'Title,URL,Description,Category\n'; state.tables.resources.forEach(r => { res += `"${r.title}","${r.url}","${r.description}","${r.category}"\n`; }); document.getElementById('csvResources').textContent = res.trim() || '(no resources)';
// CSV Announcements const bt = state.fields.home.bannerText || ''; const bl = state.fields.home.bannerLink || ''; document.getElementById('csvAnnouncements').textContent = `Text,Link\n"${bt}","${bl}"`;
// Style JSON const styleObj = {}; Object.keys(state.styles).forEach(s => { if (Object.keys(state.styles[s]).length > 0) styleObj[s] = state.styles[s]; }); document.getElementById('styleJson').textContent = JSON.stringify(styleObj, null, 2);
// Diff buildDiff();
// Save published state state.publishedState = JSON.parse(JSON.stringify({ fields: state.fields, tables: state.tables })); state.dirty = {}; document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('dirty')); saveToStorage(); }
function buildDiff() { const el = document.getElementById('diffView'); if (!state.lastPublished) { el.innerHTML = '
'; state.lastPublished = JSON.parse(JSON.stringify({ fields: state.fields, tables: state.tables })); return; } const lines = []; const curr = state.fields; const prev = state.lastPublished.fields || {}; Object.keys(curr).forEach(section => { Object.keys(curr[section]).forEach(key => { const old = (prev[section]||{})[key] || ''; const nw = curr[section][key] || ''; if (old !== nw) { if (old) lines.push(`
`); lines.push(`
`); } }); }); if (lines.length === 0) lines.push('
'); el.innerHTML = lines.join(''); state.lastPublished = JSON.parse(JSON.stringify({ fields: state.fields, tables: state.tables })); }
function switchModalTab(el, tabId) { document.querySelectorAll('.modal-tab').forEach(t => t.classList.remove('active')); document.querySelectorAll('.modal-tab-content').forEach(t => t.classList.remove('active')); el.classList.add('active'); document.getElementById(tabId).classList.add('active'); }
function copyCurrentTab() { const activeTab = document.querySelector('.modal-tab-content.active'); const codeBlocks = activeTab.querySelectorAll('.code-block'); let text = ''; codeBlocks.forEach(b => text += b.textContent + '\n\n'); if (!text.trim()) { const diffLines = activeTab.querySelectorAll('.diff-line'); diffLines.forEach(l => text += l.textContent + '\n'); } navigator.clipboard.writeText(text.trim()).then(() => showToast('Copied to clipboard!')); }
// ============================================================ // MEDIA PICKER // ============================================================ function openMediaPicker(fieldId) { mediaTargetField = fieldId; document.getElementById('mediaModal').classList.add('open'); loadRecentMedia(); }
function loadRecentMedia() { const grid = document.getElementById('mediaGrid'); // Show placeholder items from Drive (public folder - limited without auth) // We show common placeholder for now grid.innerHTML = '
'; }
function useMediaUrl() { const val = document.getElementById('mediaUrlInput').value.trim(); if (val && mediaTargetField) { const el = document.getElementById(mediaTargetField); if (el) { el.value = val; el.dispatchEvent(new Event('input')); } closeModal('mediaModal'); showToast('URL applied!'); } }
function handleFileUpload(e) { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = function(ev) { const dataUrl = ev.target.result; if (mediaTargetField) { const el = document.getElementById(mediaTargetField); if (el) { el.value = dataUrl; el.dispatchEvent(new Event('input')); } } closeModal('mediaModal'); showToast('Image loaded!'); }; reader.readAsDataURL(file); }
function handleDrop(e) { e.preventDefault(); const file = e.dataTransfer.files[0]; if (file) { const fakeEvent = { target: { files: [file] }}; handleFileUpload(fakeEvent); } }
// ============================================================ // MODAL UTILS // ============================================================ function closeModal(id) { document.getElementById(id).classList.remove('open'); }
document.addEventListener('keydown', e => { if (e.key === 'Escape') { document.querySelectorAll('.modal-overlay.open').forEach(m => m.classList.remove('open')); } });
document.querySelectorAll('.modal-overlay').forEach(overlay => { overlay.addEventListener('click', e => { if (e.target === overlay) overlay.classList.remove('open'); }); });
// ============================================================ // TOAST // ============================================================ function showToast(msg) { const t = document.getElementById('toast'); t.textContent = msg; t.classList.add('show'); setTimeout(() => t.classList.remove('show'), 2500); }
// ============================================================ // UTILS // ============================================================ function esc(str) { return (str||'').replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"'); }
// Auto-update saved label setInterval(updateSavedLabel, 30000);