make this scheduling page a more traditional calendar based map with dates separated into months, and the ability to also view the week and the day if there are a greater amount of events scheduled on a specific day, but let the "landing page" of this particular section be a month. use attached images as a reference point, but change the sylistic elements
b5467a5 verified | <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Smart Scheduling | CircaSnooze</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://unpkg.com/feather-icons"></script> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap'); | |
| body { | |
| font-family: 'Space Grotesk', sans-serif; | |
| } | |
| .gradient-bg { | |
| background: linear-gradient(135deg, #4b6cb7 0%, #182848 100%); | |
| } | |
| .schedule-card { | |
| backdrop-filter: blur(10px); | |
| background-color: rgba(255, 255, 255, 0.08); | |
| } | |
| .calendar-day { | |
| transition: all 0.2s ease; | |
| } | |
| .calendar-day:hover { | |
| transform: scale(1.05); | |
| } | |
| .event-item:hover { | |
| transform: translateY(-2px); | |
| } | |
| </style> | |
| </head> | |
| <body class="min-h-screen text-white gradient-bg"> | |
| <div class="container mx-auto px-4 py-8"> | |
| <header class="flex justify-between items-center mb-12"> | |
| <a href="index.html" class="flex items-center space-x-2"> | |
| <i data-feather="moon" class="w-6 h-6"></i> | |
| <span class="text-xl font-bold">CircaSnooze</span> | |
| </a> | |
| <nav class="hidden md:flex space-x-6"> | |
| <a href="index.html" class="hover:text-[#FFA07A] transition">Home</a> | |
| <a href="sleep-calculator.html" class="hover:text-[#FFA07A] transition">Sleep</a> | |
| <a href="#" class="text-[#FFA07A] font-medium">Schedule</a> | |
| <a href="tasks.html" class="hover:text-[#FFA07A] transition">Tasks</a> | |
| <a href="research.html" class="hover:text-[#FFA07A] transition">Research</a> | |
| </nav> | |
| <button class="bg-[#FFA07A] px-4 py-2 rounded-lg text-sm font-medium hover:bg-[#ff8a5c] transition"> | |
| My Account | |
| </button> | |
| </header> | |
| <main> | |
| <div class="grid lg:grid-cols-3 gap-8"> | |
| <div class="lg:col-span-2"> | |
| <div class="schedule-card rounded-3xl p-8 mb-8"> | |
| <div class="flex justify-between items-center mb-8"> | |
| <h1 class="text-3xl font-bold">Smart Scheduling</h1> | |
| <button onclick="openAddEventModal()" class="bg-[#FFA07A] px-4 py-2 rounded-lg font-medium">Add Event</button> | |
| </div> | |
| <!-- Calendar Navigation --> | |
| <div class="flex justify-between items-center mb-6"> | |
| <div class="flex space-x-4"> | |
| <button id="monthView" class="bg-[#FFA07A] px-4 py-2 rounded-lg font-medium">Month</button> | |
| <button id="weekView" class="bg-white/10 px-4 py-2 rounded-lg font-medium hover:bg-white/20">Week</button> | |
| <button id="dayView" class="bg-white/10 px-4 py-2 rounded-lg font-medium hover:bg-white/20">Day</button> | |
| </div> | |
| <div class="flex items-center space-x-4"> | |
| <button id="prevPeriod" class="p-2 rounded-full hover:bg-white/10"> | |
| <i data-feather="chevron-left" class="w-5 h-5"></i> | |
| </button> | |
| <h2 id="currentPeriod" class="text-2xl font-bold">December 2023</h2> | |
| <button id="nextPeriod" class="p-2 rounded-full hover:bg-white/10"> | |
| <i data-feather="chevron-right" class="w-5 h-5"></i> | |
| </button> | |
| <button id="todayBtn" class="bg-white/10 px-4 py-2 rounded-lg font-medium hover:bg-white/20">Today</button> | |
| </div> | |
| </div> | |
| <!-- Month View --> | |
| <div id="monthCalendar" class="schedule-card rounded-xl p-4 mb-8"> | |
| <div class="grid grid-cols-7 gap-1 text-center text-sm mb-2 font-medium"> | |
| <div class="p-2">Sun</div><div class="p-2">Mon</div><div class="p-2">Tue</div><div class="p-2">Wed</div> | |
| <div class="p-2">Thu</div><div class="p-2">Fri</div><div class="p-2">Sat</div> | |
| </div> | |
| <div class="grid grid-cols-7 gap-1" id="monthDays"></div> | |
| </div> | |
| <!-- Week View --> | |
| <div id="weekCalendar" class="schedule-card rounded-xl p-4 mb-8 hidden"> | |
| <div class="grid grid-cols-7 gap-4"> | |
| <div class="space-y-2" id="sunColumn"></div> | |
| <div class="space-y-2" id="monColumn"></div> | |
| <div class="space-y-2" id="tueColumn"></div> | |
| <div class="space-y-2" id="wedColumn"></div> | |
| <div class="space-y-2" id="thuColumn"></div> | |
| <div class="space-y-2" id="friColumn"></div> | |
| <div class="space-y-2" id="satColumn"></div> | |
| </div> | |
| </div> | |
| <!-- Day View --> | |
| <div id="dayCalendar" class="schedule-card rounded-xl p-4 mb-8 hidden"> | |
| <h3 id="dayHeader" class="text-xl font-bold mb-4">Today's Schedule</h3> | |
| <div id="timeSlots" class="space-y-2"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="schedule-card rounded-3xl p-8"> | |
| <h2 class="text-2xl font-bold mb-6">Your Rhythm</h2> | |
| <div class="h-64 bg-white/10 rounded-xl mb-6"> | |
| <!-- Would display circadian rhythm visualization --> | |
| </div> | |
| <h3 class="font-medium mb-4">Optimal Times</h3> | |
| <div class="space-y-4"> | |
| <div class="flex items-center justify-between p-3 bg-white/10 rounded-lg"> | |
| <div class="flex items-center"> | |
| <i data-feather="zap" class="w-4 h-4 mr-2"></i> | |
| <span>Focus Time</span> | |
| </div> | |
| <span>9:00 AM - 11:30 AM</span> | |
| </div> | |
| <div class="flex items-center justify-between p-3 bg-white/10 rounded-lg"> | |
| <div class="flex items-center"> | |
| <i data-feather="clock" class="w-4 h-4 mr-2"></i> | |
| <span>Creative Work</span> | |
| </div> | |
| <span>2:00 PM - 4:00 PM</span> | |
| </div> | |
| <div class="flex items-center justify-between p-3 bg-white/10 rounded-lg"> | |
| <div class="flex items-center"> | |
| <i data-feather="moon" class="w-4 h-4 mr-2"></i> | |
| <span>Wind Down</span> | |
| </div> | |
| <span>9:30 PM</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| <!-- Add Event Modal --> | |
| <div id="addEventModal" class="hidden fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50"> | |
| <div class="schedule-card rounded-2xl p-6 w-full max-w-md"> | |
| <div class="flex justify-between items-center mb-6"> | |
| <h3 class="text-xl font-bold">Add New Event</h3> | |
| <button onclick="closeAddEventModal()" class="text-2xl">×</button> | |
| </div> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block mb-1">Event Name</label> | |
| <input type="text" id="eventName" class="w-full bg-white/10 border border-white/20 rounded-lg px-4 py-2"> | |
| </div> | |
| <div> | |
| <label class="block mb-1">Date & Time</label> | |
| <input type="datetime-local" id="eventDateTime" class="w-full bg-white/10 border border-white/20 rounded-lg px-4 py-2"> | |
| </div> | |
| <div> | |
| <label class="block mb-1">Duration (minutes)</label> | |
| <input type="number" id="eventDuration" class="w-full bg-white/10 border border-white/20 rounded-lg px-4 py-2" value="30"> | |
| </div> | |
| <div> | |
| <label class="block mb-1">Category</label> | |
| <select id="eventCategory" class="w-full bg-white/10 border border-white/20 rounded-lg px-4 py-2"> | |
| <option value="work">Work</option> | |
| <option value="personal">Personal</option> | |
| <option value="health">Health</option> | |
| <option value="social">Social</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label class="flex items-center"> | |
| <input type="checkbox" id="eventReminder" class="mr-2" checked> | |
| <span>Set Reminder (15 mins before)</span> | |
| </label> | |
| </div> | |
| <button onclick="addEvent()" class="w-full bg-[#FFA07A] py-3 rounded-lg font-bold hover:bg-[#ff8a5c] transition"> | |
| Save Event | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Initialize feather icons | |
| feather.replace(); | |
| // Event management functions | |
| let events = []; | |
| function openAddEventModal() { | |
| document.getElementById('addEventModal').classList.remove('hidden'); | |
| // Set default time to next hour | |
| const now = new Date(); | |
| now.setHours(now.getHours() + 1, 0, 0, 0); | |
| document.getElementById('eventDateTime').value = now.toISOString().slice(0, 16); | |
| } | |
| function closeAddEventModal() { | |
| document.getElementById('addEventModal').classList.add('hidden'); | |
| } | |
| function addEvent() { | |
| const name = document.getElementById('eventName').value; | |
| const dateTime = document.getElementById('eventDateTime').value; | |
| const duration = parseInt(document.getElementById('eventDuration').value); | |
| const category = document.getElementById('eventCategory').value; | |
| const hasReminder = document.getElementById('eventReminder').checked; | |
| if (name && dateTime) { | |
| const newEvent = { | |
| id: Date.now(), | |
| name, | |
| dateTime, | |
| duration, | |
| category, | |
| hasReminder | |
| }; | |
| events.push(newEvent); | |
| renderCalendar(); | |
| renderEvents(); | |
| closeAddEventModal(); | |
| // Clear form | |
| document.getElementById('eventName').value = ''; | |
| document.getElementById('eventDateTime').value = ''; | |
| document.getElementById('eventDuration').value = '30'; | |
| document.getElementById('eventCategory').value = 'work'; | |
| document.getElementById('eventReminder').checked = true; | |
| // Set reminder if enabled | |
| if (hasReminder) { | |
| setEventReminder(newEvent); | |
| } | |
| } else { | |
| alert('Please fill in all required fields'); | |
| } | |
| } | |
| function setEventReminder(event) { | |
| console.log(`Reminder set for event: ${event.name} at ${event.dateTime}`); | |
| // In production, this would use browser notifications or connect to a reminder service | |
| alert(`Reminder set for "${event.name}"`); | |
| } | |
| function renderCalendar() { | |
| const calendarDays = document.getElementById('calendarDays'); | |
| calendarDays.innerHTML = ''; | |
| // Generate calendar days (mock implementation) | |
| const daysInMonth = 31; | |
| const startDay = 5; // December 2023 starts on Friday | |
| // Empty cells for days before the 1st | |
| for (let i = 0; i < startDay; i++) { | |
| const emptyDay = document.createElement('div'); | |
| emptyDay.className = 'h-12'; | |
| calendarDays.appendChild(emptyDay); | |
| } | |
| // Days of the month | |
| for (let day = 1; day <= daysInMonth; day++) { | |
| const dayElement = document.createElement('div'); | |
| dayElement.className = 'calendar-day h-12 flex items-center justify-center rounded-lg hover:bg-white/10 cursor-pointer'; | |
| dayElement.textContent = day; | |
| // Highlight today | |
| if (day === new Date().getDate()) { | |
| dayElement.className += ' bg-[#FFA07A] text-white'; | |
| } | |
| // Show event indicators | |
| const dayEvents = events.filter(event => { | |
| const eventDate = new Date(event.dateTime); | |
| return eventDate.getDate() === day; | |
| }); | |
| if (dayEvents.length > 0) { | |
| const eventIndicator = document.createElement('div'); | |
| eventIndicator.className = 'absolute bottom-1 w-1 h-1 rounded-full bg-[#FFA07A]'; | |
| dayElement.appendChild(eventIndicator); | |
| } | |
| calendarDays.appendChild(dayElement); | |
| } | |
| } | |
| function renderEvents() { | |
| const eventsList = document.getElementById('eventsList'); | |
| eventsList.innerHTML = ''; | |
| // Filter today's events | |
| const today = new Date().toDateString(); | |
| const todaysEvents = events.filter(event => { | |
| return new Date(event.dateTime).toDateString() === today; | |
| }); | |
| if (todaysEvents.length === 0) { | |
| eventsList.innerHTML = '<p class="opacity-70 text-center py-4">No events scheduled for today</p>'; | |
| return; | |
| } | |
| todaysEvents.forEach(event => { | |
| const eventItem = document.createElement('div'); | |
| eventItem.className = 'event-item schedule-card rounded-lg p-4 flex items-center justify-between transition'; | |
| const eventDate = new Date(event.dateTime); | |
| const endTime = new Date(eventDate.getTime() + event.duration * 60000); | |
| eventItem.innerHTML = ` | |
| <div class="flex items-center"> | |
| <div class="w-10 h-10 rounded-lg ${getCategoryColor(event.category)} flex items-center justify-center mr-3"> | |
| <i data-feather="${getCategoryIcon(event.category)}" class="w-5 h-5"></i> | |
| </div> | |
| <div> | |
| <div class="font-medium">${event.name}</div> | |
| <div class="text-xs opacity-80"> | |
| ${formatTime(eventDate)} - ${formatTime(endTime)} | |
| </div> | |
| </div> | |
| </div> | |
| ${event.hasReminder ? '<i data-feather="bell" class="w-4 h-4 text-yellow-400"></i>' : ''} | |
| `; | |
| eventsList.appendChild(eventItem); | |
| }); | |
| feather.replace(); | |
| } | |
| function getCategoryColor(category) { | |
| switch(category) { | |
| case 'work': return 'bg-blue-600'; | |
| case 'personal': return 'bg-purple-600'; | |
| case 'health': return 'bg-green-600'; | |
| case 'social': return 'bg-pink-600'; | |
| default: return 'bg-gray-600'; | |
| } | |
| } | |
| function getCategoryIcon(category) { | |
| switch(category) { | |
| case 'work': return 'briefcase'; | |
| case 'personal': return 'user'; | |
| case 'health': return 'heart'; | |
| case 'social': return 'users'; | |
| default: return 'calendar'; | |
| } | |
| } | |
| function formatTime(date) { | |
| return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); | |
| } | |
| // Calendar state | |
| let currentDate = new Date(); | |
| let currentView = 'month'; | |
| // Initialize views | |
| document.getElementById('monthView').addEventListener('click', () => { | |
| currentView = 'month'; | |
| updateView(); | |
| }); | |
| document.getElementById('weekView').addEventListener('click', () => { | |
| currentView = 'week'; | |
| updateView(); | |
| }); | |
| document.getElementById('dayView').addEventListener('click', () => { | |
| currentView = 'day'; | |
| updateView(); | |
| }); | |
| document.getElementById('prevPeriod').addEventListener('click', () => { | |
| if(currentView === 'month') { | |
| currentDate.setMonth(currentDate.getMonth() - 1); | |
| } else if(currentView === 'week') { | |
| currentDate.setDate(currentDate.getDate() - 7); | |
| } else { | |
| currentDate.setDate(currentDate.getDate() - 1); | |
| } | |
| updateView(); | |
| }); | |
| document.getElementById('nextPeriod').addEventListener('click', () => { | |
| if(currentView === 'month') { | |
| currentDate.setMonth(currentDate.getMonth() + 1); | |
| } else if(currentView === 'week') { | |
| currentDate.setDate(currentDate.getDate() + 7); | |
| } else { | |
| currentDate.setDate(currentDate.getDate() + 1); | |
| } | |
| updateView(); | |
| }); | |
| document.getElementById('todayBtn').addEventListener('click', () => { | |
| currentDate = new Date(); | |
| updateView(); | |
| }); | |
| function updateView() { | |
| document.getElementById('monthCalendar').classList.add('hidden'); | |
| document.getElementById('weekCalendar').classList.add('hidden'); | |
| document.getElementById('dayCalendar').classList.add('hidden'); | |
| if(currentView === 'month') { | |
| document.getElementById('monthCalendar').classList.remove('hidden'); | |
| document.getElementById('currentPeriod').textContent = | |
| currentDate.toLocaleString('default', { month: 'long', year: 'numeric' }); | |
| renderMonthView(); | |
| } else if(currentView === 'week') { | |
| document.getElementById('weekCalendar').classList.remove('hidden'); | |
| const weekStart = new Date(currentDate); | |
| weekStart.setDate(currentDate.getDate() - currentDate.getDay()); | |
| const weekEnd = new Date(weekStart); | |
| weekEnd.setDate(weekStart.getDate() + 6); | |
| document.getElementById('currentPeriod').textContent = | |
| `${weekStart.toLocaleDateString()} - ${weekEnd.toLocaleDateString()}`; | |
| renderWeekView(); | |
| } else { | |
| document.getElementById('dayCalendar').classList.remove('hidden'); | |
| document.getElementById('currentPeriod').textContent = | |
| currentDate.toLocaleDateString(); | |
| document.getElementById('dayHeader').textContent = | |
| `${currentDate.toLocaleString('default', { weekday: 'long' })}, ${currentDate.toLocaleDateString()}`; | |
| renderDayView(); | |
| } | |
| } | |
| function renderMonthView() { | |
| const monthDays = document.getElementById('monthDays'); | |
| monthDays.innerHTML = ''; | |
| const firstDay = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1); | |
| const lastDay = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0); | |
| // Empty cells for days before the 1st | |
| for(let i = 0; i < firstDay.getDay(); i++) { | |
| const emptyCell = document.createElement('div'); | |
| emptyCell.className = 'h-16 border border-white/10 rounded-lg'; | |
| monthDays.appendChild(emptyCell); | |
| } | |
| // Days of the month | |
| for(let day = 1; day <= lastDay.getDate(); day++) { | |
| const dayCell = document.createElement('div'); | |
| dayCell.className = 'h-16 border border-white/10 rounded-lg p-1 overflow-hidden'; | |
| const date = new Date(currentDate.getFullYear(), currentDate.getMonth(), day); | |
| const dayEvents = events.filter(e => { | |
| const eDate = new Date(e.dateTime); | |
| return eDate.toDateString() === date.toDateString(); | |
| }); | |
| dayCell.innerHTML = ` | |
| <div class="text-right text-sm mb-1 ${date.toDateString() === new Date().toDateString() ? | |
| 'bg-[#FFA07A] rounded-full w-6 h-6 flex items-center justify-center ml-auto' : ''}"> | |
| ${day} | |
| </div> | |
| ${dayEvents.length > 0 ? | |
| `<div class="text-xs truncate bg-[#FFA07A]/20 rounded px-1 mb-1">${dayEvents[0].name}</div>` : | |
| '<div class="h-4"></div>'} | |
| ${dayEvents.length > 1 ? | |
| `<div class="text-xs text-white/70">+${dayEvents.length - 1} more</div>` : ''} | |
| `; | |
| dayCell.addEventListener('click', () => { | |
| currentDate = date; | |
| currentView = 'day'; | |
| updateView(); | |
| }); | |
| monthDays.appendChild(dayCell); | |
| } | |
| } | |
| function renderWeekView() { | |
| const weekStart = new Date(currentDate); | |
| weekStart.setDate(currentDate.getDate() - currentDate.getDay()); | |
| const columns = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; | |
| columns.forEach((day, index) => { | |
| const dayDate = new Date(weekStart); | |
| dayDate.setDate(weekStart.getDate() + index); | |
| const dayColumn = document.getElementById(`${day}Column`); | |
| dayColumn.innerHTML = ` | |
| <div class="font-bold text-center ${dayDate.toDateString() === new Date().toDateString() ? | |
| 'bg-[#FFA07A] rounded-lg p-1' : ''}"> | |
| ${dayDate.toLocaleString('default', { weekday: 'short' })}<br> | |
| ${dayDate.getDate()} | |
| </div> | |
| `; | |
| const dayEvents = events.filter(e => { | |
| const eDate = new Date(e.dateTime); | |
| return eDate.toDateString() === dayDate.toDateString(); | |
| }); | |
| dayEvents.forEach(event => { | |
| const eventDiv = document.createElement('div'); | |
| eventDiv.className = 'bg-[#FFA07A]/20 rounded-lg p-2 text-xs'; | |
| eventDiv.textContent = `${formatTime(new Date(event.dateTime))} ${event.name}`; | |
| dayColumn.appendChild(eventDiv); | |
| }); | |
| }); | |
| } | |
| function renderDayView() { | |
| const timeSlots = document.getElementById('timeSlots'); | |
| timeSlots.innerHTML = ''; | |
| const dayEvents = events.filter(e => { | |
| const eDate = new Date(e.dateTime); | |
| return eDate.toDateString() === currentDate.toDateString(); | |
| }).sort((a, b) => new Date(a.dateTime) - new Date(b.dateTime)); | |
| if(dayEvents.length === 0) { | |
| timeSlots.innerHTML = '<div class="text-center py-8 text-white/50">No events scheduled for today</div>'; | |
| return; | |
| } | |
| dayEvents.forEach(event => { | |
| const eventDiv = document.createElement('div'); | |
| eventDiv.className = 'bg-white/10 rounded-lg p-4 flex justify-between items-center'; | |
| const eventDate = new Date(event.dateTime); | |
| const endTime = new Date(eventDate.getTime() + event.duration * 60000); | |
| eventDiv.innerHTML = ` | |
| <div class="flex items-center"> | |
| <div class="w-10 h-10 rounded-lg ${getCategoryColor(event.category)} flex items-center justify-center mr-3"> | |
| <i data-feather="${getCategoryIcon(event.category)}" class="w-5 h-5"></i> | |
| </div> | |
| <div> | |
| <div class="font-medium">${event.name}</div> | |
| <div class="text-sm opacity-80"> | |
| ${formatTime(eventDate)} - ${formatTime(endTime)} | |
| </div> | |
| </div> | |
| </div> | |
| ${event.hasReminder ? '<i data-feather="bell" class="w-4 h-4 text-yellow-400"></i>' : ''} | |
| `; | |
| timeSlots.appendChild(eventDiv); | |
| }); | |
| feather.replace(); | |
| } | |
| // Initialize with some sample events | |
| events = [ | |
| { | |
| id: 1, | |
| name: 'Team Meeting', | |
| dateTime: '2023-12-05T10:00', | |
| duration: 60, | |
| category: 'work', | |
| hasReminder: true | |
| }, | |
| { | |
| id: 2, | |
| name: 'Yoga Session', | |
| dateTime: '2023-12-05T18:00', | |
| duration: 45, | |
| category: 'health', | |
| hasReminder: false | |
| }, | |
| { | |
| id: 3, | |
| name: 'Project Deadline', | |
| dateTime: '2023-12-12T17:00', | |
| duration: 120, | |
| category: 'work', | |
| hasReminder: true | |
| }, | |
| { | |
| id: 4, | |
| name: 'Coffee with Sarah', | |
| dateTime: '2023-12-15T09:30', | |
| duration: 30, | |
| category: 'social', | |
| hasReminder: true | |
| } | |
| ]; | |
| updateView(); | |
| </script> | |
| </body> | |
| </html> |