Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Events Calendar - CommunityConnect Hub</title> | |
| <link rel="icon" type="image/x-icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>๐</text></svg>"> | |
| <link rel="stylesheet" href="style.css"> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> | |
| <script src="https://unpkg.com/feather-icons"></script> | |
| <script> | |
| tailwind.config = { | |
| theme: { | |
| extend: { | |
| colors: { | |
| primary: { | |
| 50: '#eff6ff', | |
| 100: '#dbeafe', | |
| 200: '#bfdbfe', | |
| 300: '#93c5fd', | |
| 400: '#60a5fa', | |
| 500: '#3b82f6', | |
| 600: '#2563eb', | |
| 700: '#1d4ed8', | |
| 800: '#1e40af', | |
| 900: '#1e3a8a', | |
| }, | |
| secondary: { | |
| 50: '#f0fdfa', | |
| 100: '#ccfbf1', | |
| 200: '#99f6e4', | |
| 300: '#5eead4', | |
| 400: '#2dd4bf', | |
| 500: '#14b8a6', | |
| 600: '#0d9488', | |
| 700: '#0f766e', | |
| 800: '#115e59', | |
| 900: '#134e4a', | |
| } | |
| } | |
| } | |
| } | |
| } | |
| </script> | |
| </head> | |
| <body class="bg-gray-50"> | |
| <!-- Navigation Component --> | |
| <custom-navbar></custom-navbar> | |
| <!-- Hero Section --> | |
| <section class="bg-gradient-to-r from-primary-600 to-secondary-600 text-white py-20"> | |
| <div class="container mx-auto px-6"> | |
| <h1 class="text-4xl lg:text-5xl font-bold mb-4">Events Calendar</h1> | |
| <p class="text-xl text-primary-100">Join us for exciting community events and activities.</p> | |
| </div> | |
| </section> | |
| <!-- Calendar View Toggle --> | |
| <section class="py-8 bg-white border-b"> | |
| <div class="container mx-auto px-6"> | |
| <div class="flex flex-col sm:flex-row justify-between items-center gap-4"> | |
| <div class="flex space-x-4"> | |
| <button id="list-view-btn" class="px-6 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors"> | |
| List View | |
| </button> | |
| <button id="calendar-view-btn" class="px-6 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors"> | |
| Calendar View | |
| </button> | |
| </div> | |
| <div class="flex items-center space-x-4"> | |
| <select id="month-filter" class="px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500"> | |
| <option value="">All Months</option> | |
| <option value="1">January</option> | |
| <option value="2">February</option> | |
| <option value="3">March</option> | |
| <option value="4">April</option> | |
| <option value="5">May</option> | |
| <option value="6">June</option> | |
| <option value="7">July</option> | |
| <option value="8">August</option> | |
| <option value="9">September</option> | |
| <option value="10">October</option> | |
| <option value="11">November</option> | |
| <option value="12">December</option> | |
| </select> | |
| <select id="category-filter" class="px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500"> | |
| <option value="">All Categories</option> | |
| <option value="workshop">Workshop</option> | |
| <option value="social">Social</option> | |
| <option value="volunteer">Volunteer</option> | |
| <option value="fundraiser">Fundraiser</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Events List --> | |
| <section id="events-list" class="py-12"> | |
| <div class="container mx-auto px-6"> | |
| <div id="events-container" class="grid md:grid-cols-2 lg:grid-cols-3 gap-6"> | |
| <!-- Events will be loaded here --> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Calendar View (Hidden by default) --> | |
| <section id="events-calendar" class="py-12 hidden"> | |
| <div class="container mx-auto px-6"> | |
| <div class="bg-white rounded-xl shadow-lg p-6"> | |
| <div id="calendar-header" class="flex justify-between items-center mb-6"> | |
| <h2 class="text-2xl font-bold"></h2> | |
| <div class="flex space-x-2"> | |
| <button id="prev-month" class="p-2 hover:bg-gray-100 rounded-lg"> | |
| <i data-feather="chevron-left" class="w-5 h-5"></i> | |
| </button> | |
| <button id="next-month" class="p-2 hover:bg-gray-100 rounded-lg"> | |
| <i data-feather="chevron-right" class="w-5 h-5"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div id="calendar-grid" class="grid grid-cols-7 gap-2"> | |
| <!-- Calendar will be generated here --> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Footer Component --> | |
| <custom-footer></custom-footer> | |
| <!-- Scripts --> | |
| <script src="components/navbar.js"></script> | |
| <script src="components/footer.js"></script> | |
| <script src="script.js"></script> | |
| <script src="api.js"></script> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', async () => { | |
| feather.replace(); | |
| let currentView = 'list'; | |
| let events = []; | |
| // Load all events | |
| try { | |
| events = await api.getUpcomingEvents(20); | |
| renderEventsList(events); | |
| initializeCalendar(events); | |
| } catch (error) { | |
| document.getElementById('events-container').innerHTML = '<div class="col-span-full text-center text-gray-500">Unable to load events at this time.</div>'; | |
| } | |
| // View toggle | |
| document.getElementById('list-view-btn').addEventListener('click', () => { | |
| currentView = 'list'; | |
| document.getElementById('events-list').classList.remove('hidden'); | |
| document.getElementById('events-calendar').classList.add('hidden'); | |
| document.getElementById('list-view-btn').className = 'px-6 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors'; | |
| document.getElementById('calendar-view-btn').className = 'px-6 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors'; | |
| }); | |
| document.getElementById('calendar-view-btn').addEventListener('click', () => { | |
| currentView = 'calendar'; | |
| document.getElementById('events-list').classList.add('hidden'); | |
| document.getElementById('events-calendar').classList.remove('hidden'); | |
| document.getElementById('calendar-view-btn').className = 'px-6 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors'; | |
| document.getElementById('list-view-btn').className = 'px-6 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors'; | |
| }); | |
| // Filter events | |
| function filterEvents() { | |
| const monthFilter = document.getElementById('month-filter').value; | |
| const categoryFilter = document.getElementById('category-filter').value; | |
| let filtered = events; | |
| if (monthFilter) { | |
| filtered = filtered.filter(event => new Date(event.date).getMonth() + 1 == monthFilter); | |
| } | |
| if (categoryFilter) { | |
| filtered = filtered.filter(event => event.category === categoryFilter); | |
| } | |
| renderEventsList(filtered); | |
| } | |
| document.getElementById('month-filter').addEventListener('change', filterEvents); | |
| document.getElementById('category-filter').addEventListener('change', filterEvents); | |
| function renderEventsList(eventsToRender) { | |
| const container = document.getElementById('events-container'); | |
| if (eventsToRender.length === 0) { | |
| container.innerHTML = '<div class="col-span-full text-center text-gray-500">No events found.</div>'; | |
| return; | |
| } | |
| container.innerHTML = eventsToRender.map(event => ` | |
| <div class="bg-white rounded-xl p-6 shadow-lg hover:shadow-xl transition-shadow"> | |
| <div class="flex items-start justify-between mb-4"> | |
| <div> | |
| <span class="inline-block px-3 py-1 bg-${getEventColor(event.category)}-100 text-${getEventColor(event.category)}-700 text-sm rounded-full mb-2"> | |
| ${event.category} | |
| </span> | |
| <h3 class="text-lg font-semibold">${event.title}</h3> | |
| </div> | |
| <button class="text-gray-400 hover:text-primary-600"> | |
| <i data-feather="bookmark" class="w-5 h-5"></i> | |
| </button> | |
| </div> | |
| <div class="space-y-2 text-sm text-gray-600 mb-4"> | |
| <p class="flex items-center"> | |
| <i data-feather="calendar" class="w-4 h-4 mr-2"></i> | |
| ${new Date(event.date).toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })} | |
| </p> | |
| <p class="flex items-center"> | |
| <i data-feather="clock" class="w-4 h-4 mr-2"></i> | |
| ${event.time} | |
| </p> | |
| <p class="flex items-center"> | |
| <i data-feather="map-pin" class="w-4 h-4 mr-2"></i> | |
| ${event.location} | |
| </p> | |
| <p class="flex items-center"> | |
| <i data-feather="users" class="w-4 h-4 mr-2"></i> | |
| ${event.attendees || 'Open to all'} | |
| </p> | |
| </div> | |
| <p class="text-gray-600 mb-4">${event.description}</p> | |
| <button class="w-full bg-primary-600 text-white py-2 rounded-lg hover:bg-primary-700 transition-colors"> | |
| Register Now | |
| </button> | |
| </div> | |
| `).join(''); | |
| feather.replace(); | |
| } | |
| function getEventColor(category) { | |
| const colors = { | |
| workshop: 'primary', | |
| social: 'secondary', | |
| volunteer: 'green', | |
| fundraiser: 'yellow' | |
| }; | |
| return colors[category] || 'gray'; | |
| } | |
| function initializeCalendar(eventsData) { | |
| const today = new Date(); | |
| const currentMonth = today.getMonth(); | |
| const currentYear = today.getFullYear(); | |
| function renderCalendar(month = currentMonth, year = currentYear) { | |
| const firstDay = new Date(year, month, 1).getDay(); | |
| const daysInMonth = new Date(year, month + 1, 0).getDate(); | |
| const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; | |
| document.querySelector('#calendar-header h2').textContent = `${monthNames[month]} ${year}`; | |
| let calendarHTML = ''; | |
| // Day headers | |
| const dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; | |
| dayNames.forEach(day => { | |
| calendarHTML += `<div class="text-center font-semibold text-gray-600 p-2">${day}</div>`; | |
| }); | |
| // Empty cells | |
| for (let i = 0; i < firstDay; i++) { | |
| calendarHTML += '<div></div>'; | |
| } | |
| // Days | |
| for (let day = 1; day <= daysInMonth; day++) { | |
| const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`; | |
| const dayEvents = eventsData.filter(e => e.date.startsWith(dateStr)); | |
| const hasEvents = dayEvents.length > 0; | |
| const isToday = today.getDate() === day && today.getMonth() === month && today.getFullYear() === year; | |
| calendarHTML += ` | |
| <div class="relative p-2 border rounded-lg ${hasEvents ? 'bg-primary-50 border-primary-200' : ''} ${isToday ? 'bg-primary-100 border-primary-400' : ''} hover:bg-gray-50 cursor-pointer"> | |
| <div class="font-semibold text-sm">${day}</div> | |
| ${hasEvents ? ` | |
| <div class="mt-1"> | |
| <span class="text-xs bg-primary-600 text-white px-1 py-0.5 rounded">${dayEvents.length} event${dayEvents.length > 1 ? 's' : ''}</span> | |
| </div> | |
| ` : ''} | |
| </div> | |
| `; | |
| } | |
| document.getElementById('calendar-grid').innerHTML = calendarHTML; | |
| } | |
| document.getElementById('prev-month').addEventListener('click', () => { | |
| const month = parseInt(document.querySelector('#calendar-header h2').textContent.split(' ')[0]) - 1; | |
| const year = parseInt(document.querySelector('#calendar-header h2').textContent.split(' ')[1]); | |
| renderCalendar(month - 1 < 0 ? 11 : month - 1, month - 1 < 0 ? year - 1 : year); | |
| }); | |
| document.getElementById('next-month').addEventListener('click', () => { | |
| const month = parseInt(document.querySelector('#calendar-header h2').textContent.split(' ')[0]) - 1; | |
| const year = parseInt(document.querySelector('#calendar-header h2').textContent.split(' ')[1]); | |
| renderCalendar(month + 1 > 11 ? 0 : month + 1, month + 1 > 11 ? year + 1 : year); | |
| }); | |
| renderCalendar(); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |