342 lines
10 KiB
JavaScript
342 lines
10 KiB
JavaScript
// Calendar functionality for Todo app
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
// Set up event listeners
|
|
const calendarBtn = document.getElementById('tab-calendar-btn');
|
|
const calendarModal = document.getElementById('calendar-modal');
|
|
const calendarCloseBtn = document.getElementById('calendar-close-btn');
|
|
|
|
if (calendarBtn) {
|
|
calendarBtn.addEventListener('click', showCalendarModal);
|
|
}
|
|
|
|
if (calendarCloseBtn) {
|
|
calendarCloseBtn.addEventListener('click', () => {
|
|
calendarModal.style.display = 'none';
|
|
});
|
|
}
|
|
|
|
// Close modal when clicking outside of it
|
|
window.addEventListener('click', (e) => {
|
|
if (e.target === calendarModal) {
|
|
calendarModal.style.display = 'none';
|
|
}
|
|
});
|
|
});
|
|
|
|
// Function to show the calendar modal
|
|
function showCalendarModal() {
|
|
const calendarModal = document.getElementById('calendar-modal');
|
|
const calendarContainer = document.getElementById('calendar-container');
|
|
|
|
if (!calendarModal || !calendarContainer) {
|
|
console.error('Calendar modal elements not found');
|
|
return;
|
|
}
|
|
|
|
// Get todos from various sources
|
|
let todos = [];
|
|
|
|
// First try to get todos from the global window.todos array
|
|
if (window.todos && Array.isArray(window.todos)) {
|
|
todos = window.todos.slice(); // Make a copy of the array
|
|
console.log('Using todos from window.todos:', todos.length);
|
|
}
|
|
|
|
// If window.todos is empty, try the localStorage
|
|
if (!todos.length) {
|
|
try {
|
|
const STORAGE_KEY = 'todos-v1'; // Match the key used in app.js
|
|
const storedTodos = localStorage.getItem(STORAGE_KEY);
|
|
if (storedTodos) {
|
|
todos = JSON.parse(storedTodos);
|
|
console.log('Using todos from localStorage:', todos.length);
|
|
}
|
|
} catch (e) {
|
|
console.error('Error loading todos from localStorage:', e);
|
|
}
|
|
}
|
|
|
|
// If we still don't have todos, try one more approach
|
|
if (!todos.length) {
|
|
// Force a load from localStorage via the app's loadTodos function if available
|
|
if (typeof loadTodos === 'function') {
|
|
try {
|
|
loadTodos();
|
|
if (window.todos && window.todos.length) {
|
|
todos = window.todos.slice();
|
|
console.log('Loaded todos using loadTodos():', todos.length);
|
|
}
|
|
} catch (e) {
|
|
console.error('Error using loadTodos():', e);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Log the todos we found
|
|
console.log('Found todos for calendar:', todos);
|
|
|
|
// Only add sample data for testing if explicitly requested
|
|
// We're not adding sample data by default now to prioritize real tasks
|
|
|
|
// Generate the calendar HTML
|
|
const calendarHTML = generateCalendarHTML(todos);
|
|
|
|
// Update the calendar container
|
|
calendarContainer.innerHTML = calendarHTML;
|
|
|
|
// Show the modal
|
|
calendarModal.style.display = 'flex';
|
|
}
|
|
|
|
// Function to generate the HTML for the calendar modal content
|
|
function generateCalendarHTML(todos) {
|
|
// Get the current date
|
|
const currentDate = new Date();
|
|
const currentMonth = currentDate.getMonth();
|
|
const currentYear = currentDate.getFullYear();
|
|
|
|
// Ensure todos is an array
|
|
todos = todos || [];
|
|
|
|
// Debug log the todos
|
|
console.log('Generating calendar with todos:', todos.length);
|
|
|
|
// Generate the calendar HTML for the modal
|
|
return `
|
|
<div class="calendar-header">
|
|
<h2 class="calendar-title">${getMonthName(currentMonth)} ${currentYear}</h2>
|
|
</div>
|
|
<div class="calendar-grid">
|
|
<div class="calendar-day-header">Mon</div>
|
|
<div class="calendar-day-header">Tue</div>
|
|
<div class="calendar-day-header">Wed</div>
|
|
<div class="calendar-day-header">Thu</div>
|
|
<div class="calendar-day-header">Fri</div>
|
|
<div class="calendar-day-header">Sat</div>
|
|
<div class="calendar-day-header">Sun</div>
|
|
${generateCalendarDays(currentMonth, currentYear, todos)}
|
|
</div>
|
|
<style>
|
|
.calendar-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 20px;
|
|
}
|
|
.calendar-title {
|
|
font-size: 24px;
|
|
font-weight: 700;
|
|
margin: 0;
|
|
}
|
|
.calendar-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(7, 1fr);
|
|
gap: 8px;
|
|
}
|
|
.calendar-day-header {
|
|
text-align: center;
|
|
font-weight: 600;
|
|
padding: 10px;
|
|
background-color: #f3f4f6;
|
|
border-radius: 5px;
|
|
}
|
|
.calendar-day {
|
|
min-height: 100px;
|
|
border: 1px solid #e5e7eb;
|
|
border-radius: 5px;
|
|
padding: 8px;
|
|
position: relative;
|
|
background-color: white;
|
|
}
|
|
.calendar-day-number {
|
|
position: absolute;
|
|
top: 5px;
|
|
right: 8px;
|
|
font-weight: 600;
|
|
color: #6b7280;
|
|
}
|
|
.calendar-day.today {
|
|
background-color: #f0f9ff;
|
|
border-color: #3b82f6;
|
|
}
|
|
.calendar-day.other-month {
|
|
background-color: #f9fafb;
|
|
color: #9ca3af;
|
|
}
|
|
.todo-item {
|
|
font-size: 13px;
|
|
margin-top: 8px;
|
|
padding: 6px 8px;
|
|
border-radius: 4px;
|
|
background-color: #e0f2fe;
|
|
border-left: 3px solid #3b82f6;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
cursor: pointer;
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
line-height: 1.3;
|
|
}
|
|
.todo-item.urgent {
|
|
background-color: #fee2e2;
|
|
border-left-color: #ef4444;
|
|
}
|
|
.todo-item.due-today {
|
|
background-color: #fef3c7;
|
|
border-left-color: #f59e0b;
|
|
}
|
|
</style>
|
|
<script>
|
|
// Function to edit a todo from the calendar
|
|
function editTodoFromCalendar(todoId) {
|
|
// Close the calendar modal
|
|
document.getElementById('calendar-modal').style.display = 'none';
|
|
|
|
// Find the todo in the todos array by ID
|
|
if (typeof window.showEditTodoModal === 'function') {
|
|
window.showEditTodoModal(null, todoId);
|
|
} else {
|
|
console.error('Edit modal function not available');
|
|
alert('Could not open edit modal. Please try again.');
|
|
}
|
|
}
|
|
</script>
|
|
`;
|
|
}
|
|
|
|
// Helper function to get month name
|
|
function getMonthName(month) {
|
|
const monthNames = [
|
|
'January', 'February', 'March', 'April', 'May', 'June',
|
|
'July', 'August', 'September', 'October', 'November', 'December'
|
|
];
|
|
return monthNames[month];
|
|
}
|
|
|
|
// Function to generate calendar days
|
|
function generateCalendarDays(month, year, todos) {
|
|
const today = new Date();
|
|
const firstDay = new Date(year, month, 1);
|
|
const lastDay = new Date(year, month + 1, 0);
|
|
const daysInMonth = lastDay.getDate();
|
|
|
|
// Adjust day of week to start from Monday (0 = Monday, 6 = Sunday)
|
|
let startingDayOfWeek = firstDay.getDay() - 1;
|
|
if (startingDayOfWeek === -1) startingDayOfWeek = 6; // Sunday becomes 6
|
|
|
|
// Get previous month's last days to fill in the first week
|
|
const prevMonth = month === 0 ? 11 : month - 1;
|
|
const prevMonthYear = month === 0 ? year - 1 : year;
|
|
const prevMonthLastDay = new Date(prevMonthYear, prevMonth + 1, 0).getDate();
|
|
|
|
let calendarHTML = '';
|
|
|
|
// Previous month's days
|
|
for (let i = 0; i < startingDayOfWeek; i++) {
|
|
const day = prevMonthLastDay - startingDayOfWeek + i + 1;
|
|
calendarHTML += `
|
|
<div class="calendar-day other-month">
|
|
<div class="calendar-day-number">${day}</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
// Current month's days
|
|
for (let day = 1; day <= daysInMonth; day++) {
|
|
const date = new Date(year, month, day);
|
|
const isToday = date.getDate() === today.getDate() &&
|
|
date.getMonth() === today.getMonth() &&
|
|
date.getFullYear() === today.getFullYear();
|
|
|
|
// Format the date as YYYY-MM-DD for comparison with todos
|
|
const formattedDate = formatDateYYYYMMDD(date);
|
|
|
|
// Filter todos for this day
|
|
const todosForDay = todos.filter(todo => {
|
|
// Debug log to check date formats
|
|
if (day === 1) {
|
|
console.log('Todo dates for debugging:', {
|
|
id: todo.id,
|
|
nextDate: todo.nextDate,
|
|
dueDate: todo.dueDate,
|
|
formattedDate: formattedDate
|
|
});
|
|
}
|
|
|
|
// Normalize date formats for comparison
|
|
const todoNextDate = todo.nextDate ? todo.nextDate.substring(0, 10) : null;
|
|
const todoDueDate = todo.dueDate ? todo.dueDate.substring(0, 10) : null;
|
|
|
|
return (
|
|
(todoNextDate && todoNextDate === formattedDate) ||
|
|
(todoDueDate && todoDueDate === formattedDate)
|
|
);
|
|
});
|
|
|
|
// Debug log for this day's todos
|
|
if (todosForDay.length > 0) {
|
|
console.log(`Todos for ${formattedDate}:`, todosForDay);
|
|
}
|
|
|
|
calendarHTML += `
|
|
<div class="calendar-day ${isToday ? 'today' : ''}">
|
|
<div class="calendar-day-number">${day}</div>
|
|
${generateTodoItemsHTML(todosForDay, formattedDate)}
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
// Next month's days to complete the grid (6 rows x 7 columns = 42 cells)
|
|
const totalCells = 42;
|
|
const remainingCells = totalCells - (startingDayOfWeek + daysInMonth);
|
|
|
|
for (let day = 1; day <= remainingCells; day++) {
|
|
calendarHTML += `
|
|
<div class="calendar-day other-month">
|
|
<div class="calendar-day-number">${day}</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
return calendarHTML;
|
|
}
|
|
|
|
// Helper function to format date as YYYY-MM-DD
|
|
function formatDateYYYYMMDD(date) {
|
|
const year = date.getFullYear();
|
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
const day = String(date.getDate()).padStart(2, '0');
|
|
return `${year}-${month}-${day}`;
|
|
}
|
|
|
|
// Function to generate HTML for todo items in a day
|
|
function generateTodoItemsHTML(todos, date) {
|
|
if (!todos.length) return '';
|
|
|
|
let html = '';
|
|
|
|
todos.forEach(todo => {
|
|
const isDueToday = todo.dueDate === date;
|
|
const isUrgent = todo.urgent >= 5;
|
|
const className = isDueToday ? 'todo-item due-today' : (isUrgent ? 'todo-item urgent' : 'todo-item');
|
|
|
|
// Make the todo item clickable to open the edit modal
|
|
html += `
|
|
<div class="${className}"
|
|
title="Click to edit this todo"
|
|
data-id="${todo.id}"
|
|
data-todo-id="${todo.id}"
|
|
onclick="editTodoFromCalendar(${todo.id})">
|
|
<strong>#${todo.id}</strong>: ${truncateText(todo.task, 25)}
|
|
</div>
|
|
`;
|
|
});
|
|
|
|
return html;
|
|
}
|
|
|
|
// Helper function to truncate text
|
|
function truncateText(text, maxLength) {
|
|
if (!text) return '';
|
|
return text.length > maxLength ? text.substring(0, maxLength) + '...' : text;
|
|
}
|