Sign in to your workspace
Create a learner profile
Course system / 02
Choose your assembly pathway
Filter by practical focus, compare learning depth, save a course to Fallows, or add it to your cart for a structured checkout review.
`;
footer.innerHTML=``;
initTheme();
initMobileMenu();
initLoginModal();
updateCartCount();
initCookieBanner();
}
function initTheme(){
const btn=document.getElementById('theme-toggle');
const saved=localStorage.getItem('spartanNestTheme');
if(saved==='light'){
document.documentElement.classList.add('light');
document.body.classList.remove('bg-slate-950');
document.body.classList.add('bg-slate-100','text-slate-900');
}
btn.addEventListener('click',()=>{
if(document.documentElement.classList.contains('light')){
document.documentElement.classList.remove('light');
document.body.classList.remove('bg-slate-100','text-slate-900');
document.body.classList.add('bg-slate-950','text-slate-100');
localStorage.setItem('spartanNestTheme','dark');
}else{
document.documentElement.classList.add('light');
document.body.classList.remove('bg-slate-950','text-slate-100');
document.body.classList.add('bg-slate-100','text-slate-900');
localStorage.setItem('spartanNestTheme','light');
}
});
}
function initMobileMenu(){
const openBtn=document.getElementById('mobile-menu-open');
const menu=document.getElementById('mobile-menu');
openBtn.addEventListener('click',()=>{menu.classList.toggle('hidden');});
}
function initLoginModal(){
const loginBtn=document.getElementById('login-open');
const regBtn=document.getElementById('register-open');
const modalHTML=`Sign in to your workspace
`;
document.body.insertAdjacentHTML('beforeend',modalHTML);
const modal=document.getElementById('login-modal');
loginBtn.onclick=()=>modal.classList.remove('hidden');
regBtn.onclick=()=>modal.classList.remove('hidden');
modal.querySelector('#close-login').onclick=()=>modal.classList.add('hidden');
modal.onclick=e=>{if(e.target===modal)modal.classList.add('hidden');};
const form=modal.querySelector('#login-form');
form.onsubmit=function(e){e.preventDefault();modal.classList.add('hidden');alert('Signed in successfully.');};
}
function updateCartCount(){
const countEl=document.getElementById('cart-count');
let cart=JSON.parse(localStorage.getItem('spartanNestCart')||'{}');
let total=0;
for(let k in cart)total+=cart[k].quantity;
countEl.textContent=total;
}
function initCookieBanner(){
const banner=document.getElementById('cookie-banner');
const accept=document.getElementById('cookie-accept');
if(!localStorage.getItem('spartanNestCookies'))banner.classList.remove('hidden');
accept.onclick=function(){localStorage.setItem('spartanNestCookies','accepted');banner.classList.add('hidden');};
}
async function fetchCourses(){
const status=document.getElementById('catalog-status');
status.textContent='Loading catalog...';
try{
const res=await fetch('./catalog.json');
courses=await res.json();
filteredCourses=[...courses];
status.textContent='';
buildFilters();
renderGrid();
renderPagination();
const params=new URLSearchParams(window.location.search);
if(params.has('item'))openModalById(params.get('item'));
}catch(e){
status.textContent='Unable to load catalog data.';
}
}
function buildFilters(){
const catSelect=document.getElementById('category');
const cats=[...new Set(courses.map(c=>c.category))].sort();
catSelect.innerHTML='';
cats.forEach(c=>{const o=document.createElement('option');o.value=c;o.textContent=c;catSelect.appendChild(o);});
document.getElementById('search').oninput=applyFilters;
catSelect.onchange=applyFilters;
document.getElementById('sort').onchange=applyFilters;
}
function applyFilters(){
const q=document.getElementById('search').value.toLowerCase();
const cat=document.getElementById('category').value;
const sort=document.getElementById('sort').value;
filteredCourses=courses.filter(c=>{
const matchQ=!q||c.title.toLowerCase().includes(q)||c.summary.toLowerCase().includes(q);
const matchCat=!cat||c.category===cat;
return matchQ&&matchCat;
});
if(sort==='price-low')filteredCourses.sort((a,b)=>a.price-b.price);
else if(sort==='price-high')filteredCourses.sort((a,b)=>b.price-a.price);
currentPage=1;
renderGrid();
renderPagination();
}
function renderGrid(){
const grid=document.getElementById('product-grid');
grid.innerHTML='';
const start=(currentPage-1)*perPage;
const pageItems=filteredCourses.slice(start,start+perPage);
if(pageItems.length===0){
grid.innerHTML='No courses match your filters.
';
return;
}
pageItems.forEach(course=>{
const favs=JSON.parse(localStorage.getItem('spartanNestFavorites')||'[]');
const isFav=favs.includes(course.id);
const card=document.createElement('div');
card.className='rounded-2xl border border-slate-800 bg-slate-900 p-6 flex flex-col';
card.innerHTML=`
${course.level} • ${course.duration}
${course.title}
${course.summary}
${course.format} • ${course.currency}${course.price}
`;
grid.appendChild(card);
const favBtn=card.querySelector('[data-fav]');
favBtn.onclick=function(){
let favArray=JSON.parse(localStorage.getItem('spartanNestFavorites')||'[]');
if(favArray.includes(course.id)){
favArray=favArray.filter(x=>x!==course.id);
favBtn.textContent='☆';
}else{
favArray.push(course.id);
favBtn.textContent='★';
}
localStorage.setItem('spartanNestFavorites',JSON.stringify(favArray));
}
card.querySelector('[data-cart]').onclick=function(){
let cart=JSON.parse(localStorage.getItem('spartanNestCart')||'{}');
if(!cart[course.id])cart[course.id]={quantity:1,title:course.title};
else cart[course.id].quantity++;
localStorage.setItem('spartanNestCart',JSON.stringify(cart));
updateCartCount();
}
});
}
function renderPagination(){
const nav=document.getElementById('pagination');
nav.innerHTML='';
const totalPages=Math.ceil(filteredCourses.length/perPage);
for(let i=1;i<=totalPages;i++){
const link=document.createElement('a');
link.href='javascript:void(0)';
link.textContent=i;
link.className='px-4 py-2 rounded-xl border border-slate-700 text-sm '+(i===currentPage?'bg-indigo-600 text-white':'hover:border-violet-400');
link.onclick=function(){currentPage=i;renderGrid();renderPagination();};
nav.appendChild(link);
}
}
function openModalById(id){
const course=courses.find(c=>c.id===id);
if(!course)return;
const modal=document.getElementById('detail-modal');
document.getElementById('detail-title').textContent=course.title;
document.getElementById('detail-description').textContent=course.description;
const modUl=document.getElementById('detail-modules');
modUl.innerHTML='';
course.modules.forEach(m=>{const li=document.createElement('li');li.textContent='• '+m;modUl.appendChild(li);});
const outUl=document.getElementById('detail-outcomes');
outUl.innerHTML='';
course.outcomes.forEach(o=>{const li=document.createElement('li');li.textContent='• '+o;outUl.appendChild(li);});
modal.classList.remove('hidden');
document.getElementById('close-modal').onclick=()=>modal.classList.add('hidden');
modal.onclick=e=>{if(e.target===modal)modal.classList.add('hidden');};
document.onkeydown=function(ev){if(ev.key==='Escape')modal.classList.add('hidden');};
}
function initDetailFromUrl(){
const params=new URLSearchParams(window.location.search);
if(params.has('item')){
setTimeout(()=>openModalById(params.get('item')),300);
}
}
function initAll(){
loadHeaderFooter();
document.getElementById('detail-modal').classList.add('hidden');
fetchCourses();
initDetailFromUrl();
}
initAll();