undefined / scheduling.html
ttcgroup's picture
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
<!DOCTYPE html>
<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">&times;</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>