Todo/calendar.js
2025-05-14 19:31:21 +02:00

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;
}