let programs = {}; async function loadPrograms() { try { const response = await fetch('programs.json'); programs = await response.json(); renderPrograms(); } catch (error) { console.error('Error loading programs:', error); } } function formatDeadline(deadlineStr) { if (!deadlineStr) return ''; const deadline = new Date(deadlineStr); const now = new Date(); const diffTime = deadline - now; const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); if (diffDays < 0) return 'Ended'; if (diffDays === 0) return 'Ends today'; if (diffDays === 1) return 'Ends tomorrow'; if (diffDays <= 7) return `${diffDays} days left`; if (diffDays <= 30) { const weeks = Math.floor(diffDays / 7); return `${weeks} week${weeks > 1 ? 's' : ''} left`; } return `Ends ${deadline.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: deadline.getFullYear() !== new Date().getFullYear() ? 'numeric' : undefined })}`; } function getDeadlineClass(deadlineStr) { if (!deadlineStr) return ''; const deadline = new Date(deadlineStr); const now = new Date(); const diffTime = deadline - now; const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); if (diffDays <= 2) return 'very-urgent'; if (diffDays <= 7) return 'urgent'; return ''; } function createProgramCard(program) { const deadlineText = formatDeadline(program.deadline); const deadlineClass = getDeadlineClass(program.deadline); return `

${program.name}

${program.status}

${program.description}

${program.deadline ? `
${deadlineText}
` : ''}
`; } function countActivePrograms() { let count = 0; Object.values(programs).forEach(category => { count += category.filter(program => program.status === 'active').length; }); return count; } function renderPrograms() { const container = document.getElementById('programs-container'); const activeCount = countActivePrograms(); document.getElementById('active-count').textContent = activeCount; for (const [category, programsList] of Object.entries(programs)) { const section = document.createElement('section'); section.className = 'category-section'; section.innerHTML = `

${category.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase())}

${programsList.map(program => createProgramCard(program)).join('')}
`; container.appendChild(section); } } function filterPrograms(category) { const sections = document.querySelectorAll('.category-section'); const buttons = document.querySelectorAll('.filter-btn'); buttons.forEach(btn => { btn.classList.toggle('active', btn.dataset.category === category); }); sections.forEach(section => { const programCards = section.querySelectorAll('.program-card'); programCards.forEach(card => { const statusElement = card.querySelector('.program-status'); const status = statusElement.textContent; if (category === 'all') { card.classList.remove('hidden-by-filter'); } else { card.classList.toggle('hidden-by-filter', status !== category); } }); const hasVisibleCards = Array.from(programCards) .some(card => !card.classList.contains('hidden-by-filter') && !card.classList.contains('hidden-by-search')); section.classList.toggle('hidden', !hasVisibleCards); }); } function searchPrograms(searchTerm) { const programCards = document.querySelectorAll('.program-card'); searchTerm = searchTerm.toLowerCase().trim(); programCards.forEach(card => { const name = card.querySelector('h3').textContent.toLowerCase(); const description = card.querySelector('p').textContent.toLowerCase(); const slackChannel = card.querySelector('.program-links')?.textContent.toLowerCase() || ''; const matches = name.includes(searchTerm) || description.includes(searchTerm) || slackChannel.includes(searchTerm); card.classList.toggle('hidden-by-search', !matches); }); const sections = document.querySelectorAll('.category-section'); sections.forEach(section => { const hasVisibleCards = Array.from(section.querySelectorAll('.program-card')) .some(card => !card.classList.contains('hidden-by-filter') && !card.classList.contains('hidden-by-search')); section.classList.toggle('hidden', !hasVisibleCards); }); } function toggleTheme() { const body = document.body; const toggleBtn = document.getElementById('theme-toggle'); const isDark = body.classList.toggle('dark-theme'); toggleBtn.textContent = isDark ? '☀️' : '🌙'; localStorage.setItem('theme', isDark ? 'dark' : 'light'); } function initializeTheme() { const savedTheme = localStorage.getItem('theme'); const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; const toggleBtn = document.getElementById('theme-toggle'); if (savedTheme === 'dark' || (!savedTheme && prefersDark)) { document.body.classList.add('dark-theme'); toggleBtn.textContent = '☀️'; } } function updateDeadlines() { const deadlineElements = document.querySelectorAll('.program-deadline'); deadlineElements.forEach(element => { const card = element.closest('.program-card'); const programName = card.querySelector('h3').textContent; const program = Object.values(programs) .flat() .find(p => p.name === programName); if (program?.deadline) { const deadlineText = formatDeadline(program.deadline); const deadlineClass = getDeadlineClass(program.deadline); element.textContent = deadlineText; element.className = `program-deadline ${deadlineClass}`; } }); } document.addEventListener('DOMContentLoaded', () => { loadPrograms().then(() => { const searchInput = document.getElementById('program-search'); searchInput.addEventListener('input', (e) => searchPrograms(e.target.value)); document.querySelectorAll('.filter-btn').forEach(button => { button.addEventListener('click', () => { filterPrograms(button.dataset.category); searchPrograms(searchInput.value); }); }); initializeTheme(); document.getElementById('theme-toggle').addEventListener('click', toggleTheme); setInterval(updateDeadlines, 60000); }); });