calendar / index.html
sergotsev's picture
Add 1 files
0dbae82 verified
Raw
History Blame Contribute Delete
45.8 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PlanSync - Unified Task Planner</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--personal: #42A5F5;
--shared: #66BB6A;
--important: #EF5350;
--background: #f8f9fa;
--text: #212529;
--card-bg: #ffffff;
--border: #dee2e6;
--shadow: 0 2px 10px rgba(0,0,0,0.05);
}
[data-theme="dark"] {
--background: #121212;
--text: #e1e1e1;
--card-bg: #1e1e1e;
--border: #333;
--shadow: 0 2px 10px rgba(0,0,0,0.3);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
transition: background-color 0.3s, color 0.3s;
}
body {
background-color: var(--background);
color: var(--text);
min-height: 100vh;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
position: relative;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
}
.logo {
font-size: 24px;
font-weight: 600;
}
.logo span {
color: var(--shared);
}
.theme-toggle {
background: none;
border: none;
color: var(--text);
font-size: 20px;
cursor: pointer;
}
.date-controls {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.current-date {
font-size: 18px;
font-weight: 600;
}
.nav-btn {
background: none;
border: none;
color: var(--text);
font-size: 18px;
cursor: pointer;
padding: 8px;
}
.view-switcher {
display: flex;
margin-bottom: 20px;
background-color: var(--card-bg);
border-radius: 10px;
padding: 5px;
box-shadow: var(--shadow);
}
.view-btn {
flex: 1;
padding: 10px;
border: none;
background: none;
text-align: center;
font-weight: 500;
color: var(--text);
border-radius: 8px;
cursor: pointer;
}
.view-btn.active {
background-color: var(--shared);
color: white;
}
.calendar-header {
display: grid;
grid-template-columns: repeat(7, 1fr);
text-align: center;
font-weight: 600;
margin-bottom: 10px;
}
.calendar-days {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 5px;
margin-bottom: 30px;
}
.calendar-day {
aspect-ratio: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border-radius: 8px;
cursor: pointer;
position: relative;
}
.calendar-day:hover {
background-color: rgba(0,0,0,0.05);
}
.calendar-day.today {
font-weight: bold;
color: var(--shared);
}
.calendar-day.has-events::after {
content: '';
width: 6px;
height: 6px;
background-color: var(--personal);
border-radius: 50%;
position: absolute;
bottom: 5px;
}
.calendar-day.has-shared::after {
background-color: var(--shared);
}
.calendar-day.has-important::after {
background-color: var(--important);
}
.tabs {
display: flex;
border-bottom: 1px solid var(--border);
margin-bottom: 20px;
}
.tab {
padding: 10px 20px;
cursor: pointer;
font-weight: 500;
position: relative;
}
.tab.active {
color: var(--shared);
}
.tab.active::after {
content: '';
position: absolute;
bottom: -1px;
left: 0;
right: 0;
height: 2px;
background-color: var(--shared);
}
.tasks-container {
margin-bottom: 80px;
}
.task-card {
background-color: var(--card-bg);
border-radius: 12px;
padding: 15px;
margin-bottom: 15px;
box-shadow: var(--shadow);
position: relative;
overflow: hidden;
border-left: 6px solid var(--personal);
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
}
.task-card:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.task-card.shared {
border-left-color: var(--shared);
}
.task-card.important {
border-left-color: var(--important);
}
.task-header {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
}
.task-title {
font-weight: 600;
font-size: 16px;
}
.task-time {
color: var(--text);
opacity: 0.8;
font-size: 14px;
}
.task-desc {
font-size: 14px;
margin-bottom: 10px;
color: var(--text);
opacity: 0.7;
}
.task-participants {
display: flex;
align-items: center;
gap: 5px;
margin-top: 10px;
}
.participant {
width: 28px;
height: 28px;
border-radius: 50%;
background-color: var(--shared);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 12px;
}
.task-actions {
position: absolute;
top: 15px;
right: 15px;
display: none;
}
.task-card:hover .task-actions {
display: flex;
gap: 10px;
}
.action-btn {
background: none;
border: none;
color: var(--text);
cursor: pointer;
opacity: 0.7;
}
.action-btn:hover {
opacity: 1;
}
.add-btn {
position: fixed;
bottom: 20px;
right: 20px;
width: 60px;
height: 60px;
border-radius: 50%;
background-color: var(--shared);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
border: none;
box-shadow: 0 4px 15px rgba(102, 187, 106, 0.3);
cursor: pointer;
z-index: 100;
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0,0,0,0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s;
}
.modal-overlay.active {
opacity: 1;
pointer-events: all;
}
.modal {
background-color: var(--card-bg);
border-radius: 12px;
width: 90%;
max-width: 500px;
max-height: 90vh;
overflow-y: auto;
padding: 20px;
transform: translateY(20px);
transition: transform 0.3s;
}
.modal-overlay.active .modal {
transform: translateY(0);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.modal-title {
font-size: 20px;
font-weight: 600;
}
.close-btn {
background: none;
border: none;
font-size: 24px;
color: var(--text);
cursor: pointer;
opacity: 0.7;
}
.close-btn:hover {
opacity: 1;
}
.form-group {
margin-bottom: 15px;
}
.form-label {
display: block;
margin-bottom: 5px;
font-size: 14px;
font-weight: 500;
}
.form-input {
width: 100%;
padding: 10px 15px;
border-radius: 8px;
border: 1px solid var(--border);
background-color: var(--card-bg);
color: var(--text);
font-size: 14px;
}
.form-select {
width: 100%;
padding: 10px 15px;
border-radius: 8px;
border: 1px solid var(--border);
background-color: var(--card-bg);
color: var(--text);
font-size: 14px;
appearance: none;
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right 10px center;
background-size: 1em;
}
.checkbox-group {
display: flex;
align-items: center;
}
.checkbox-label {
margin-left: 8px;
font-size: 14px;
}
.btn {
padding: 10px 20px;
border-radius: 8px;
border: none;
font-weight: 500;
cursor: pointer;
font-size: 14px;
}
.btn-primary {
background-color: var(--shared);
color: white;
}
.btn-outline {
background: none;
border: 1px solid var(--border);
color: var(--text);
}
.category-options {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
margin-bottom: 15px;
}
.category-option {
padding: 10px;
border-radius: 8px;
border: 1px solid var(--border);
text-align: center;
cursor: pointer;
transition: all 0.2s;
display: flex;
flex-direction: column;
align-items: center;
}
.category-option:hover {
border-color: var(--personal);
}
.category-option.selected {
border-color: var(--personal);
background-color: rgba(66, 165, 245, 0.1);
}
.category-option.shared.selected {
border-color: var(--shared);
background-color: rgba(102, 187, 106, 0.1);
}
.category-icon {
font-size: 20px;
margin-bottom: 5px;
}
.voice-btn {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
padding: 10px;
background-color: var(--card-bg);
border: 1px dashed var(--border);
border-radius: 8px;
margin-top: 10px;
color: var(--text);
cursor: pointer;
}
.empty-state {
text-align: center;
padding: 40px 0;
color: var(--text);
opacity: 0.5;
}
.empty-state i {
font-size: 50px;
margin-bottom: 15px;
}
.completed {
text-decoration: line-through;
opacity: 0.6;
}
@media (max-width: 600px) {
.container {
padding: 15px;
}
}
/* Animation classes */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.fade-in {
animation: fadeIn 0.3s ease-in-out;
}
@keyframes slideIn {
from { transform: translateY(20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.slide-in {
animation: slideIn 0.3s ease-in-out;
}
</style>
</head>
<body>
<div class="container">
<header>
<div class="logo">Plan<span>Sync</span></div>
<button class="theme-toggle" id="themeToggle">
<i class="fas fa-moon"></i>
</button>
</header>
<div class="date-controls">
<button class="nav-btn" id="prevDay"><i class="fas fa-chevron-left"></i></button>
<div class="current-date" id="currentDate">Today, June 15, 2023</div>
<button class="nav-btn" id="nextDay"><i class="fas fa-chevron-right"></i></button>
</div>
<div class="view-switcher">
<button class="view-btn active" data-view="day">Day</button>
<button class="view-btn" data-view="week">Week</button>
<button class="view-btn" data-view="month">Month</button>
</div>
<div class="calendar-header slide-in">
<div>S</div>
<div>M</div>
<div>T</div>
<div>W</div>
<div>T</div>
<div>F</div>
<div>S</div>
</div>
<div class="calendar-days" id="calendarDays">
<!-- Calendar days will be populated by JavaScript -->
</div>
<div class="tabs slide-in">
<div class="tab active" data-tab="personal">Personal Tasks</div>
<div class="tab" data-tab="shared">Shared Meetings</div>
</div>
<div class="tasks-container">
<div class="task-list" id="personalTasks">
<!-- Personal tasks will be populated by JavaScript -->
</div>
<div class="task-list" id="sharedTasks" style="display: none;">
<!-- Shared tasks will be populated by JavaScript -->
</div>
</div>
<button class="add-btn" id="addTaskBtn">
<i class="fas fa-plus"></i>
</button>
</div>
<!-- Add Task Modal -->
<div class="modal-overlay" id="taskModal">
<div class="modal">
<div class="modal-header">
<div class="modal-title">Add New Task</div>
<button class="close-btn" id="closeModal">&times;</button>
</div>
<form id="taskForm">
<div class="category-options">
<div class="category-option selected" data-type="personal">
<i class="fas fa-user category-icon"></i>
Personal
</div>
<div class="category-option" data-type="shared">
<i class="fas fa-users category-icon"></i>
Shared
</div>
</div>
<div class="form-group">
<label class="form-label" for="taskTitle">Task Title</label>
<input type="text" id="taskTitle" class="form-input" placeholder="What needs to be done?" required>
</div>
<div class="form-group">
<label class="form-label" for="taskTime">Time</label>
<input type="datetime-local" id="taskTime" class="form-input" required>
</div>
<div class="form-group">
<label class="form-label" for="taskDescription">Description (Optional)</label>
<textarea id="taskDescription" class="form-input" rows="3" placeholder="Add details..."></textarea>
</div>
<div class="form-group" id="participantsContainer" style="display: none;">
<label class="form-label">Participants</label>
<div class="participant-input">
<input type="text" id="participantEmail" class="form-input" placeholder="Enter email">
<button type="button" class="btn btn-outline" id="addParticipant" style="margin-top: 5px;">Add</button>
</div>
<div class="task-participants" id="participantsList" style="margin-top: 10px;">
<!-- Participants will be added here -->
</div>
</div>
<div class="form-group">
<div class="checkbox-group">
<input type="checkbox" id="isImportant">
<label class="checkbox-label" for="isImportant">Mark as Important</label>
</div>
</div>
<button type="button" class="voice-btn" id="voiceInputBtn">
<i class="fas fa-microphone"></i> Voice Input
</button>
<div class="form-group" style="margin-top: 20px;">
<button type="submit" class="btn btn-primary" style="width: 100%;">Add Task</button>
</div>
</form>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// DOM Elements
const themeToggle = document.getElementById('themeToggle');
const prevDayBtn = document.getElementById('prevDay');
const nextDayBtn = document.getElementById('nextDay');
const currentDateEl = document.getElementById('currentDate');
const viewSwitcherBtns = document.querySelectorAll('.view-btn');
const calendarDaysEl = document.getElementById('calendarDays');
const tabBtns = document.querySelectorAll('.tab');
const personalTasksEl = document.getElementById('personalTasks');
const sharedTasksEl = document.getElementById('sharedTasks');
const addTaskBtn = document.getElementById('addTaskBtn');
const taskModal = document.getElementById('taskModal');
const closeModalBtn = document.getElementById('closeModal');
const categoryOptions = document.querySelectorAll('.category-option');
const participantsContainer = document.getElementById('participantsContainer');
const participantEmail = document.getElementById('participantEmail');
const addParticipantBtn = document.getElementById('addParticipant');
const participantsList = document.getElementById('participantsList');
const taskForm = document.getElementById('taskForm');
const voiceInputBtn = document.getElementById('voiceInputBtn');
// App State
let darkMode = localStorage.getItem('darkMode') === 'true';
let currentDate = new Date();
let activeView = 'day';
let activeTab = 'personal';
let taskType = 'personal';
let tasks = [
{
id: 1,
title: 'Team meeting',
description: 'Discuss project milestones',
time: '2023-06-15T10:00',
type: 'shared',
important: true,
completed: false,
participants: ['jane@example.com', 'mike@example.com']
},
{
id: 2,
title: 'Gym session',
description: 'Cardio and weight training',
time: '2023-06-15T07:00',
type: 'personal',
important: false,
completed: true,
participants: []
},
{
id: 3,
title: 'Doctor appointment',
description: 'Annual checkup',
time: '2023-06-16T14:30',
type: 'personal',
important: true,
completed: false,
participants: []
},
{
id: 4,
title: 'Project deadline',
description: 'Submit final report',
time: '2023-06-18T17:00',
type: 'personal',
important: true,
completed: false,
participants: []
},
{
id: 5,
title: 'Birthday party',
description: 'Alex is turning 30!',
time: '2023-06-17T19:00',
type: 'shared',
important: false,
completed: false,
participants: ['alex@example.com', 'sarah@example.com']
},
{
id: 6,
title: 'Grocery shopping',
description: 'Milk, eggs, bread, fruits',
time: '2023-06-15T18:00',
type: 'personal',
important: false,
completed: false,
participants: []
}
];
// Initialize app
init();
function init() {
// Set theme
updateTheme();
// Set current date display
updateDateDisplay();
// Generate calendar days
generateCalendarDays();
// Render tasks
renderTasks();
// Set up event listeners
setupEventListeners();
}
function setupEventListeners() {
// Theme toggle
themeToggle.addEventListener('click', toggleTheme);
// Date navigation
prevDayBtn.addEventListener('click', () => {
if (activeView === 'day') {
currentDate.setDate(currentDate.getDate() - 1);
} else if (activeView === 'week') {
currentDate.setDate(currentDate.getDate() - 7);
} else if (activeView === 'month') {
currentDate.setMonth(currentDate.getMonth() - 1);
}
updateDateDisplay();
generateCalendarDays();
});
nextDayBtn.addEventListener('click', () => {
if (activeView === 'day') {
currentDate.setDate(currentDate.getDate() + 1);
} else if (activeView === 'week') {
currentDate.setDate(currentDate.getDate() + 7);
} else if (activeView === 'month') {
currentDate.setMonth(currentDate.getMonth() + 1);
}
updateDateDisplay();
generateCalendarDays();
});
// View switcher
viewSwitcherBtns.forEach(btn => {
btn.addEventListener('click', () => {
viewSwitcherBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
activeView = btn.dataset.view;
updateDateDisplay();
generateCalendarDays();
});
});
// Tab switcher
tabBtns.forEach(btn => {
btn.addEventListener('click', () => {
tabBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
activeTab = btn.dataset.tab;
if (activeTab === 'personal') {
personalTasksEl.style.display = 'block';
sharedTasksEl.style.display = 'none';
} else {
personalTasksEl.style.display = 'none';
sharedTasksEl.style.display = 'block';
}
});
});
// Task modal
addTaskBtn.addEventListener('click', () => {
taskModal.classList.add('active');
});
closeModalBtn.addEventListener('click', () => {
taskModal.classList.remove('active');
});
// Category selection
categoryOptions.forEach(option => {
option.addEventListener('click', () => {
categoryOptions.forEach(opt => opt.classList.remove('selected'));
option.classList.add('selected');
taskType = option.dataset.type;
if (taskType === 'shared') {
participantsContainer.style.display = 'block';
option.classList.add('shared');
option.classList.remove('selected');
option.classList.add('shared.selected');
} else {
participantsContainer.style.display = 'none';
}
});
});
// Add participant
addParticipantBtn.addEventListener('click', () => {
const email = participantEmail.value.trim();
if (email && isValidEmail(email)) {
const participant = document.createElement('div');
participant.className = 'participant';
participant.textContent = email.charAt(0).toUpperCase();
participant.title = email;
const removeBtn = document.createElement('span');
removeBtn.className = 'action-btn';
removeBtn.innerHTML = '&times;';
removeBtn.style.marginLeft = '5px';
removeBtn.style.fontSize = '12px';
removeBtn.addEventListener('click', () => {
participant.remove();
});
participant.appendChild(removeBtn);
participantsList.appendChild(participant);
participantEmail.value = '';
}
});
// Form submission
taskForm.addEventListener('submit', (e) => {
e.preventDefault();
const title = document.getElementById('taskTitle').value;
const time = document.getElementById('taskTime').value;
const description = document.getElementById('taskDescription').value;
const isImportant = document.getElementById('isImportant').checked;
// Get participants
const participantElements = participantsList.querySelectorAll('.participant');
const participants = Array.from(participantElements).map(el => el.title);
// Create new task
const newTask = {
id: Date.now(),
title,
description,
time,
type: taskType,
important: isImportant,
completed: false,
participants
};
tasks.push(newTask);
renderTasks();
// Reset form
taskForm.reset();
participantsList.innerHTML = '';
taskType = 'personal';
categoryOptions[0].classList.add('selected');
categoryOptions[1].classList.remove('selected');
participantsContainer.style.display = 'none';
// Close modal
taskModal.classList.remove('active');
});
// Voice input (simulated, as we can't actually use speech recognition APIs in this example)
voiceInputBtn.addEventListener('click', () => {
alert('In a real app, this would activate the speech recognition API. For this demo, we simulate adding a sample task.');
// Simulate voice input by auto-filling the form
document.getElementById('taskTitle').value = 'Meeting with client';
document.getElementById('taskTime').value = '2023-06-16T15:00';
document.getElementById('taskDescription').value = 'Discuss new project requirements';
});
}
function toggleTheme() {
darkMode = !darkMode;
localStorage.setItem('darkMode', darkMode);
updateTheme();
}
function updateTheme() {
if (darkMode) {
document.documentElement.setAttribute('data-theme', 'dark');
themeToggle.innerHTML = '<i class="fas fa-sun"></i>';
} else {
document.documentElement.removeAttribute('data-theme');
themeToggle.innerHTML = '<i class="fas fa-moon"></i>';
}
}
function updateDateDisplay() {
const options = {
weekday: 'long',
month: 'long',
day: 'numeric',
year: 'numeric'
};
if (activeView === 'day') {
currentDateEl.textContent = currentDate.toLocaleDateString('en-US', options);
} else if (activeView === 'week') {
const weekStart = new Date(currentDate);
weekStart.setDate(currentDate.getDate() - currentDate.getDay());
const weekEnd = new Date(weekStart);
weekEnd.setDate(weekStart.getDate() + 6);
currentDateEl.textContent = `${weekStart.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })} - ${weekEnd.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}`;
} else if (activeView === 'month') {
currentDateEl.textContent = currentDate.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
}
}
function generateCalendarDays() {
calendarDaysEl.innerHTML = '';
if (activeView === 'day') {
// For day view, show just the selected day
const day = new Date(currentDate);
const dayElement = createCalendarDayElement(day);
calendarDaysEl.appendChild(dayElement);
} else if (activeView === 'week') {
// For week view, show all days of the current week
const weekStart = new Date(currentDate);
weekStart.setDate(currentDate.getDate() - currentDate.getDay());
for (let i = 0; i < 7; i++) {
const day = new Date(weekStart);
day.setDate(weekStart.getDate() + i);
const dayElement = createCalendarDayElement(day);
calendarDaysEl.appendChild(dayElement);
}
} else if (activeView === 'month') {
// For month view, show the entire month in a grid
const firstDay = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1);
const lastDay = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0);
// Get day of week for first day (0 = Sunday, 6 = Saturday)
const startDay = firstDay.getDay();
// Calculate total days to display (including days from previous/following months)
const daysInMonth = lastDay.getDate();
const daysToShow = Math.ceil((daysInMonth + startDay) / 7) * 7;
// Display calendar days
for (let i = 0; i < daysToShow; i++) {
const day = new Date(firstDay);
if (i < startDay) {
// Days from previous month
day.setDate(1 - (startDay - i));
} else if (i >= startDay + daysInMonth) {
// Days from next month
day.setDate(1 + (i - (startDay + daysInMonth)));
} else {
// Days from current month
day.setDate(1 + (i - startDay));
}
const dayElement = createCalendarDayElement(day);
calendarDaysEl.appendChild(dayElement);
}
}
}
function createCalendarDayElement(date) {
const today = new Date();
today.setHours(0, 0, 0, 0);
const dateCopy = new Date(date);
dateCopy.setHours(0, 0, 0, 0);
// Check if this day has any tasks
const hasTasks = tasks.some(task => {
const taskDate = new Date(task.time);
taskDate.setHours(0, 0, 0, 0);
return taskDate.getTime() === dateCopy.getTime();
});
const hasPersonalTasks = tasks.some(task => {
const taskDate = new Date(task.time);
taskDate.setHours(0, 0, 0, 0);
return taskDate.getTime() === dateCopy.getTime() && task.type === 'personal';
});
const hasSharedMeetings = tasks.some(task => {
const taskDate = new Date(task.time);
taskDate.setHours(0, 0, 0, 0);
return taskDate.getTime() === dateCopy.getTime() && task.type === 'shared';
});
const hasImportantTasks = tasks.some(task => {
const taskDate = new Date(task.time);
taskDate.setHours(0, 0, 0, 0);
return taskDate.getTime() === dateCopy.getTime() && task.important;
});
// Create day element
const dayElement = document.createElement('div');
dayElement.className = 'calendar-day';
if (dateCopy.getTime() === today.getTime()) {
dayElement.classList.add('today');
}
// Mark if day has tasks
if (hasTasks) {
dayElement.classList.add('has-events');
if (hasPersonalTasks && hasSharedMeetings) {
// Special case for both types
dayElement.classList.add('has-shared');
} else if (hasSharedMeetings) {
dayElement.classList.add('has-shared');
}
if (hasImportantTasks) {
dayElement.classList.add('has-important');
}
}
dayElement.textContent = date.getDate();
// Highlight the currently selected day in month/week view
if (activeView !== 'day' && dateCopy.getTime() === currentDate.getTime()) {
dayElement.style.backgroundColor = 'rgba(102, 187, 106, 0.2)';
}
// Add click event to navigate to this day
dayElement.addEventListener('click', () => {
currentDate = new Date(date);
if (activeView !== 'day') {
// If not in day view, switch to day view when clicking a date
viewSwitcherBtns.forEach(b => b.classList.remove('active'));
document.querySelector('.view-btn[data-view="day"]').classList.add('active');
activeView = 'day';
}
updateDateDisplay();
generateCalendarDays();
});
return dayElement;
}
function renderTasks() {
// Clear existing tasks
personalTasksEl.innerHTML = '';
sharedTasksEl.innerHTML = '';
// Filter tasks for the current view
let filteredTasks = [];
if (activeView === 'day') {
const startOfDay = new Date(currentDate);
startOfDay.setHours(0, 0, 0, 0);
const endOfDay = new Date(currentDate);
endOfDay.setHours(23, 59, 59, 999);
filteredTasks = tasks.filter(task => {
const taskDate = new Date(task.time);
return taskDate >= startOfDay && taskDate <= endOfDay;
});
} else if (activeView === 'week') {
const weekStart = new Date(currentDate);
weekStart.setDate(currentDate.getDate() - currentDate.getDay());
weekStart.setHours(0, 0, 0, 0);
const weekEnd = new Date(weekStart);
weekEnd.setDate(weekStart.getDate() + 7);
weekEnd.setHours(0, 0, 0, 0);
filteredTasks = tasks.filter(task => {
const taskDate = new Date(task.time);
return taskDate >= weekStart && taskDate < weekEnd;
});
} else if (activeView === 'month') {
const firstDay = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1);
const lastDay = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0);
filteredTasks = tasks.filter(task => {
const taskDate = new Date(task.time);
return taskDate >= firstDay && taskDate <= lastDay;
});
}
// Sort tasks by time
filteredTasks.sort((a, b) => new Date(a.time) - new Date(b.time));
// Separate personal and shared tasks
const personalTasks = filteredTasks.filter(task => task.type === 'personal');
const sharedTasks = filteredTasks.filter(task => task.type === 'shared');
// Render personal tasks
if (personalTasks.length === 0) {
personalTasksEl.innerHTML = `
<div class="empty-state">
<i class="fas fa-tasks"></i>
<p>No personal tasks for this period</p>
</div>
`;
} else {
personalTasks.forEach(task => {
personalTasksEl.appendChild(createTaskCard(task));
});
}
// Render shared tasks
if (sharedTasks.length === 0) {
sharedTasksEl.innerHTML = `
<div class="empty-state">
<i class="fas fa-users"></i>
<p>No shared meetings for this period</p>
</div>
`;
} else {
sharedTasks.forEach(task => {
sharedTasksEl.appendChild(createTaskCard(task));
});
}
}
function createTaskCard(task) {
const taskDate = new Date(task.time);
const timeStr = taskDate.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
const dateStr = taskDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' });
const card = document.createElement('div');
card.className = `task-card ${task.type} ${task.important ? 'important' : ''} ${task.completed ? 'completed' : ''} slide-in`;
card.innerHTML = `
<div class="task-header">
<div class="task-title">${task.title}</div>
<div class="task-time">${dateStr}${timeStr}</div>
</div>
${task.description ? `<div class="task-desc">${task.description}</div>` : ''}
${task.participants.length > 0 ? `
<div class="task-participants">
${task.participants.map(p => `<div class="participant" title="${p}">${p.charAt(0).toUpperCase()}</div>`).join('')}
</div>
` : ''}
<div class="task-actions">
<button class="action-btn complete-btn" title="Mark as ${task.completed ? 'incomplete' : 'completed'}">
<i class="fas fa-${task.completed ? 'undo' : 'check'}"></i>
</button>
<button class="action-btn delete-btn" title="Delete">
<i class="fas fa-trash"></i>
</button>
</div>
`;
// Task completion toggle
const completeBtn = card.querySelector('.complete-btn');
completeBtn.addEventListener('click', (e) => {
e.stopPropagation();
task.completed = !task.completed;
card.classList.toggle('completed', task.completed);
completeBtn.innerHTML = `<i class="fas fa-${task.completed ? 'undo' : 'check'}"></i>`;
completeBtn.title = `Mark as ${task.completed ? 'incomplete' : 'completed'}`;
});
// Task deletion
const deleteBtn = card.querySelector('.delete-btn');
deleteBtn.addEventListener('click', (e) => {
e.stopPropagation();
if (confirm('Are you sure you want to delete this task?')) {
tasks = tasks.filter(t => t.id !== task.id);
card.classList.add('fade-out');
setTimeout(() => {
card.remove();
renderTasks(); // Refresh the task list
}, 300);
}
});
// Click on card to view/edit (in a real app, this would open an edit modal)
card.addEventListener('click', () => {
alert(`Viewing task: ${task.title}\n\nWould open edit modal in a real app.`);
});
return card;
}
function isValidEmail(email) {
// Very simple email validation for demo purposes
return email.includes('@') && email.includes('.');
}
});
</script>
</body>
</html>