Sign in

Create account

Quick find

Type: courses, about, contact, faq, favorites, cart.

    Your Favorites

    Curate, compare, and export your saved personal growth courses. Manage your list with ease.

    Time on page: 00:00 Saved courses: 0

    Need a hand?

    Our team can help you pick the right course for your goals.

    +1 (415) 204-9836

    Export favorites as text

    Copy or download your list. Each line includes the title, provider, and ID.

    Ask an expert

    Need immediate help? Call us at +1 (415) 204-9836.

    '; if(footerMount) footerMount.innerHTML = f || ''; }); // Theme toggle const themeBtn = $('[data-theme-toggle]'); const applyTheme = (t) => { const root = document.documentElement; if(t==='dark'){ root.classList.add('dark'); } else { root.classList.remove('dark'); } try{ localStorage.setItem('theme', t); }catch(e){} }; if(themeBtn){ themeBtn.addEventListener('click', ()=>{ const isDark = document.documentElement.classList.contains('dark'); applyTheme(isDark?'light':'dark'); }); } // Timer const timerEl = $('[data-timer]'); let seconds = 0; setInterval(()=>{ seconds++; const m = String(Math.floor(seconds/60)).padStart(2,'0'); const s = String(seconds%60).padStart(2,'0'); if(timerEl) timerEl.textContent = m+':'+s; },1000); // Cookie banner const cookieBanner = $('#cookie-banner'); const showBanner = () => { cookieBanner.classList.remove('translate-y-full','opacity-0','pointer-events-none'); cookieBanner.classList.add('translate-y-0','opacity-100','pointer-events-auto'); }; const hideBanner = () => { cookieBanner.classList.add('translate-y-full','opacity-0','pointer-events-none'); cookieBanner.classList.remove('translate-y-0','opacity-100','pointer-events-auto'); }; try{ const consent = localStorage.getItem('gg_cookie_consent'); if(!consent){ setTimeout(showBanner, 700); } }catch(e){} $('[data-cookies-accept]')?.addEventListener('click', ()=>{ try{ localStorage.setItem('gg_cookie_consent','accepted'); }catch(e){} hideBanner(); }); $('[data-cookies-decline]')?.addEventListener('click', ()=>{ try{ localStorage.setItem('gg_cookie_consent','declined'); }catch(e){} hideBanner(); }); // Favorites state const grid = $('[data-favs-grid]'); const emptyState = $('[data-empty]'); const countEl = $('[data-count]'); const searchEl = $('[data-search]'); const sortEl = $('[data-sort]'); const exportBtn = $('[data-export-btn]'); const clearBtn = $('[data-clear-btn]'); const EXPORT_DIALOG = $('[data-export-dialog]'); const EXPORT_TEXT = $('[data-export-text]'); const COPY_BTN = $('[data-copy-btn]'); const DL_BTN = $('[data-download-btn]'); const FB_DIALOG = $('[data-feedback-dialog]'); const OPEN_FB_BTNS = $$('[data-open-feedback]'); const FB_NAME = $('[data-fb-name]'); const FB_EMAIL = $('[data-fb-email]'); const FB_MSG = $('[data-fb-msg]'); const FB_CONSENT = $('[data-fb-consent]'); const FB_SUBMIT = $('[data-fb-submit]'); let catalog = []; let favorites = []; function readFavorites(){ try{ const a = JSON.parse(localStorage.getItem('gg_favorites')||'[]'); const b = JSON.parse(localStorage.getItem('favorites')||'[]'); const merged = Array.from(new Set([...(Array.isArray(a)?a:[]),...(Array.isArray(b)?b:[])])); favorites = merged.filter(v=>typeof v==='string' || typeof v==='number').map(String); localStorage.setItem('gg_favorites', JSON.stringify(favorites)); }catch(e){ favorites = []; } } function writeFavorites(){ try{ localStorage.setItem('gg_favorites', JSON.stringify(favorites)); }catch(e){} } function fmtDuration(mins){ if(!mins && mins!==0) return '—'; const m = Number(mins); if(m<60) return m+' min'; const h = Math.floor(m/60), r = m%60; return h+'h'+(r?(' '+r+'m'):''); } function card(course, idx){ const id = course.id ?? ''; const title = course.title ?? 'Untitled'; const provider = course.provider ?? (course.author || 'Unknown'); const rating = (course.rating ?? course.stars ?? 0).toFixed ? (Number(course.rating).toFixed(1)) : (Number(course.rating||0).toFixed(1)); const level = course.level ?? 'All levels'; const duration = course.duration ?? course.length ?? null; const price = course.price ?? 'Free'; const img = course.image || course.cover || ''; const safeAlt = (course.imageAlt || (title+' cover image')); const wrap = document.createElement('article'); wrap.className = 'rounded-2xl border border-slate-200 dark:border-slate-800 overflow-hidden bg-white dark:bg-slate-900 shadow-sm hover:shadow-md transition-all duration-300 t1m9p'; wrap.setAttribute('data-id', String(id)); wrap.innerHTML = `
    ${img ? `${safeAlt}` : `Abstract placeholder cover for ${title}`}

    ${title}

    ${Number(rating||0).toFixed(1)}
    ${provider} ${fmtDuration(Number(duration||0))} ${level}
    ${price}

    ${course.description || 'No description provided.'}

    `; return wrap; } function render(){ if(!grid) return; grid.innerHTML = ''; const q = (searchEl?.value || '').trim().toLowerCase(); let list = favorites.map(id => catalog.find(c => String(c.id)===String(id))).filter(Boolean); // Sorting const sort = sortEl?.value || 'recent'; if(sort==='title'){ list.sort((a,b)=> String(a.title||'').localeCompare(String(b.title||''))); }else if(sort==='rating'){ list.sort((a,b)=> Number(b.rating||0) - Number(a.rating||0)); }else if(sort==='duration'){ list.sort((a,b)=> Number(a.duration||0) - Number(b.duration||0)); } // recent = keep original order saved if(q){ list = list.filter(c=>{ const hay = (c.title+' '+(c.provider||c.author||'')+' '+(c.level||'')).toLowerCase(); return hay.includes(q); }); } if(list.length===0){ emptyState?.classList.remove('hidden'); }else{ emptyState?.classList.add('hidden'); } list.forEach((c,i)=> grid.appendChild(card(c,i))); if(countEl) countEl.textContent = String(favorites.length); // Bind remove buttons $$('[data-remove]').forEach(btn=>{ btn.addEventListener('click', (e)=>{ const id = btn.getAttribute('data-remove'); const idx = favorites.findIndex(v => String(v)===String(id)); if(idx>-1){ favorites.splice(idx,1); writeFavorites(); render(); } }); }); } async function load(){ readFavorites(); try{ const res = await fetch('./catalog.json', {cache:'no-store'}); if(!res.ok) throw new Error('Network error'); const data = await res.json(); catalog = Array.isArray(data) ? data : (Array.isArray(data?.items) ? data.items : []); }catch(e){ catalog = favorites.map(id=>({id, title:'Unknown', provider:'—', rating:0, level:'—', duration:null, price:'—', description:'We could not load catalog details offline.'})); } render(); } // Export dialog function openExport(){ if(!EXPORT_DIALOG) return; const list = favorites.map(id=> catalog.find(c=>String(c.id)===String(id))).filter(Boolean); const text = list.map((c,i)=> `${i+1}. ${c.title} — ${c.provider || 'Unknown'} (ID: ${c.id})`).join('\n') || 'No favorites to export.'; EXPORT_TEXT.value = text; try{ const blob = new Blob([text], {type: 'text/plain;charset=utf-8'}); const url = URL.createObjectURL(blob); DL_BTN.setAttribute('href', url); }catch(e){} if(typeof EXPORT_DIALOG.showModal==='function'){ EXPORT_DIALOG.showModal(); } } exportBtn?.addEventListener('click', openExport); COPY_BTN?.addEventListener('click', async ()=>{ try{ await navigator.clipboard.writeText(EXPORT_TEXT.value); COPY_BTN.textContent = 'Copied!'; setTimeout(()=> COPY_BTN.textContent = 'Copy', 1200); }catch(e){ COPY_BTN.textContent = 'Copy failed'; setTimeout(()=> COPY_BTN.textContent = 'Copy', 1500); } }); // Clear all clearBtn?.addEventListener('click', ()=>{ if(!favorites.length) return; const ok = confirm('Remove all favorites? This cannot be undone.'); if(!ok) return; favorites = []; writeFavorites(); render(); }); // Search + sort searchEl?.addEventListener('input', ()=>{ render(); }); sortEl?.addEventListener('change', ()=>{ render(); }); // Feedback dialog OPEN_FB_BTNS.forEach(b=> b.addEventListener('click', ()=> { if(typeof FB_DIALOG.showModal==='function'){ FB_DIALOG.showModal(); } })); FB_SUBMIT?.addEventListener('click', (e)=>{ // custom validation const name = FB_NAME.value.trim(); const email = FB_EMAIL.value.trim(); const msg = FB_MSG.value.trim(); const consent = FB_CONSENT.checked; const emailOk = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); if(name.length<2 || !emailOk || msg.length<10 || !consent){ e.preventDefault(); [FB_NAME, FB_EMAIL, FB_MSG].forEach(inp=>{ inp.classList.toggle('ring-2', false); inp.classList.toggle('ring-red-500', false); }); if(name.length<2) { FB_NAME.classList.add('ring-2','ring-red-500'); } if(!emailOk) { FB_EMAIL.classList.add('ring-2','ring-red-500'); } if(msg.length<10) { FB_MSG.classList.add('ring-2','ring-red-500'); } return; } e.preventDefault(); // Simulate submit success FB_SUBMIT.disabled = true; FB_SUBMIT.textContent = 'Sending...'; setTimeout(()=>{ FB_SUBMIT.textContent = 'Sent!'; setTimeout(()=> FB_DIALOG.close(), 800); setTimeout(()=>{ FB_SUBMIT.disabled = false; FB_SUBMIT.textContent = 'Send'; FB_NAME.value=''; FB_EMAIL.value=''; FB_MSG.value=''; FB_CONSENT.checked=false; }, 1000); }, 800); }); // Kickoff load(); })();