diff --git a/frontend/admin/static/script.js b/frontend/admin/static/script.js index 165c985..d25e91b 100644 --- a/frontend/admin/static/script.js +++ b/frontend/admin/static/script.js @@ -19,56 +19,153 @@ function getWeekStart(dateStr = null) { if (!dateStr) { dateStr = getTodayInLondon(); } - const date = new Date(dateStr + 'T00:00:00'); - const day = date.getDay(); - const diff = date.getDate() - day + (day === 0 ? -6 : 1); // Понедельник = 1 - const monday = new Date(date.setDate(diff)); - return monday.toISOString().split('T')[0]; + // Создаем дату из строки YYYY-MM-DD в локальном времени + const [year, month, day] = dateStr.split('-').map(Number); + const date = new Date(year, month - 1, day); + // getDay() возвращает 0=воскресенье, 1=понедельник, ..., 6=суббота + // Находим понедельник текущей недели + const dayOfWeek = date.getDay(); + // Если воскресенье (0), идем на 6 дней назад, иначе на (dayOfWeek-1) дней назад + const diff = dayOfWeek === 0 ? -6 : -(dayOfWeek - 1); + const monday = new Date(date); + monday.setDate(date.getDate() + diff); + // Проверяем, что получился понедельник + if (monday.getDay() !== 1) { + console.error('Error: getWeekStart did not return Monday!', monday.getDay()); + } + // Возвращаем в формате YYYY-MM-DD + const yearStr = monday.getFullYear(); + const monthStr = String(monday.getMonth() + 1).padStart(2, '0'); + const dayStr = String(monday.getDate()).padStart(2, '0'); + return `${yearStr}-${monthStr}-${dayStr}`; } function formatDate(dateStr) { - const date = new Date(dateStr + 'T00:00:00'); - return `${weekdaysShort[date.getDay()]}, ${date.getDate()}`; + // Создаем дату из строки YYYY-MM-DD в локальном времени + const [year, month, day] = dateStr.split('-').map(Number); + const date = new Date(year, month - 1, day); + // getDay() возвращает 0=воскресенье, 1=понедельник, ..., 6=суббота + // Конвертируем в формат где 0=понедельник, 6=воскресенье + const jsDay = date.getDay(); + const weekdayIndex = jsDay === 0 ? 6 : jsDay - 1; // 0=пн, 6=вс + return `${weekdaysShort[weekdayIndex]}, ${date.getDate()}`; } function formatDateFull(dateStr) { - const date = new Date(dateStr + 'T00:00:00'); + // Создаем дату из строки YYYY-MM-DD в локальном времени + const [year, month, day] = dateStr.split('-').map(Number); + const date = new Date(year, month - 1, day); const months = ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря']; - return `${weekdays[date.getDay()]}, ${date.getDate()} ${months[date.getMonth()]}`; + // getDay() возвращает 0=воскресенье, 1=понедельник, ..., 6=суббота + // Конвертируем в формат где 0=понедельник, 6=воскресенье + const jsDay = date.getDay(); + const weekdayIndex = jsDay === 0 ? 6 : jsDay - 1; // 0=пн, 6=вс + return `${weekdays[weekdayIndex]}, ${date.getDate()} ${months[date.getMonth()]}`; } function getWeekDates(startDate) { + // startDate должен быть понедельником (в формате YYYY-MM-DD) const dates = []; - const start = new Date(startDate + 'T00:00:00'); + // Создаем дату из строки в локальном времени + const [year, month, day] = startDate.split('-').map(Number); + const start = new Date(year, month - 1, day); + // Убеждаемся, что это понедельник + const dayOfWeek = start.getDay(); + if (dayOfWeek !== 1) { + // Если не понедельник, находим понедельник + const diff = dayOfWeek === 0 ? -6 : -(dayOfWeek - 1); + start.setDate(start.getDate() + diff); + } + // Возвращаем 7 дней: понедельник - воскресенье + const baseDate = new Date(start); for (let i = 0; i < 7; i++) { - const date = new Date(start); - date.setDate(start.getDate() + i); - dates.push(date.toISOString().split('T')[0]); + const date = new Date(baseDate); + date.setDate(baseDate.getDate() + i); + // Форматируем в YYYY-MM-DD + const yearStr = date.getFullYear(); + const monthStr = String(date.getMonth() + 1).padStart(2, '0'); + const dayStr = String(date.getDate()).padStart(2, '0'); + dates.push(`${yearStr}-${monthStr}-${dayStr}`); + } + // Проверяем, что первый день - понедельник, последний - воскресенье + const [firstYear, firstMonth, firstDayNum] = dates[0].split('-').map(Number); + const firstDate = new Date(firstYear, firstMonth - 1, firstDayNum); + const [lastYear, lastMonth, lastDayNum] = dates[6].split('-').map(Number); + const lastDate = new Date(lastYear, lastMonth - 1, lastDayNum); + const firstDay = firstDate.getDay(); + const lastDay = lastDate.getDay(); + if (firstDay !== 1 || lastDay !== 0) { + console.error('Error: Week should start on Monday and end on Sunday!', { + first: dates[0], firstDay, + last: dates[6], lastDay + }); } return dates; } async function loadSchedule() { + // Убеждаемся, что currentWeekStart - это понедельник + if (!currentWeekStart) { + currentWeekStart = getWeekStart(); + } else { + currentWeekStart = getWeekStart(currentWeekStart); + } + const weekDates = getWeekDates(currentWeekStart); const fromDate = weekDates[0]; const toDate = weekDates[6]; - console.log('Loading schedule from', fromDate, 'to', toDate); + // Проверяем, что неделя начинается с понедельника и заканчивается воскресеньем + const [mondayYear, mondayMonth, mondayDay] = weekDates[0].split('-').map(Number); + const monday = new Date(mondayYear, mondayMonth - 1, mondayDay); + const [sundayYear, sundayMonth, sundayDay] = weekDates[6].split('-').map(Number); + const sunday = new Date(sundayYear, sundayMonth - 1, sundayDay); + if (monday.getDay() !== 1 || sunday.getDay() !== 0) { + console.error('Week should start on Monday and end on Sunday! Fixing...', { + monday: weekDates[0], mondayDay: monday.getDay(), + sunday: weekDates[6], sundayDay: sunday.getDay() + }); + // Исправляем автоматически без рекурсии + currentWeekStart = getWeekStart(); + const correctedWeekDates = getWeekDates(currentWeekStart); + // Используем исправленные даты напрямую + const correctedFromDate = correctedWeekDates[0]; + const correctedToDate = correctedWeekDates[6]; + + try { + const response = await fetch(`/api/schedule?from_date=${correctedFromDate}&to_date=${correctedToDate}`); + const data = await response.json(); + renderSchedule(correctedWeekDates, data.items); + document.getElementById('week-range').textContent = + `${formatDateFull(correctedWeekDates[0])} - ${formatDateFull(correctedWeekDates[6])}`; + updateWeekNavigationButtons(); + } catch (error) { + console.error('Error loading schedule:', error); + } + return; + } try { const response = await fetch(`/api/schedule?from_date=${fromDate}&to_date=${toDate}`); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } const data = await response.json(); - console.log('Received items:', data.items); + if (!data.items) { + console.error('Invalid response format:', data); + return; + } renderSchedule(weekDates, data.items); // Обновляем заголовок недели - const startDate = new Date(weekDates[0] + 'T00:00:00'); - const endDate = new Date(weekDates[6] + 'T00:00:00'); document.getElementById('week-range').textContent = `${formatDateFull(weekDates[0])} - ${formatDateFull(weekDates[6])}`; + + // Обновляем кнопки навигации + updateWeekNavigationButtons(); } catch (error) { console.error('Error loading schedule:', error); } @@ -76,21 +173,14 @@ async function loadSchedule() { function renderSchedule(weekDates, items) { const grid = document.getElementById('schedule-grid'); + if (!grid) { + console.error('Schedule grid not found!'); + return; + } grid.innerHTML = ''; - console.log('Rendering schedule for dates:', weekDates); - console.log('All items:', items); - weekDates.forEach(dateStr => { - const dayItems = items.filter(item => { - const match = item.date === dateStr; - if (!match && items.length > 0) { - console.log(`Item date ${item.date} !== ${dateStr}`); - } - return match; - }); - console.log(`Date ${dateStr}: ${dayItems.length} items`); - const date = new Date(dateStr + 'T00:00:00'); + const dayItems = items.filter(item => item.date === dateStr); const column = document.createElement('div'); column.className = 'day-column'; @@ -107,14 +197,12 @@ function renderSchedule(weekDates, items) { } function renderDayItems(items) { - console.log('Rendering items for day:', items); if (items.length === 0) { return '
Нет записей
'; } const tasks = items.filter(item => item.kind === 'task'); const events = items.filter(item => item.kind === 'event'); - console.log('Tasks:', tasks, 'Events:', events); let html = ''; @@ -168,10 +256,19 @@ function closeModal() { } function showAddTaskModal(selectedDate = null) { - const weekDates = getWeekDates(currentWeekStart); const today = getTodayInLondon(); - const todayDate = new Date(today + 'T00:00:00'); - const selectedDateObj = selectedDate ? new Date(selectedDate + 'T00:00:00') : todayDate; + const todayWeekStart = getWeekStart(today); + const nextWeekStart = new Date(todayWeekStart + 'T00:00:00'); + nextWeekStart.setDate(nextWeekStart.getDate() + 7); + const nextWeekStartStr = nextWeekStart.toISOString().split('T')[0]; + + // Ограничиваем выбор дат: только текущая и следующая неделя + const minDate = todayWeekStart; + const maxDate = new Date(nextWeekStartStr + 'T00:00:00'); + maxDate.setDate(maxDate.getDate() + 6); // Воскресенье следующей недели + const maxDateStr = maxDate.toISOString().split('T')[0]; + + const selectedDateObj = selectedDate ? new Date(selectedDate + 'T00:00:00') : new Date(today + 'T00:00:00'); // getDay() возвращает 0=воскресенье, 1=понедельник, ..., 6=суббота // Конвертируем в формат 0=понедельник, 6=воскресенье const jsWeekday = selectedDateObj.getDay(); @@ -188,7 +285,7 @@ function showAddTaskModal(selectedDate = null) {
- +
@@ -253,7 +350,10 @@ function showAddTaskModal(selectedDate = null) { const result = await response.json(); console.log('Task created:', result); closeModal(); - await loadSchedule(); + // Небольшая задержка перед обновлением, чтобы БД успела сохранить + setTimeout(() => { + loadSchedule().catch(err => console.error('Error reloading schedule:', err)); + }, 100); } else { const error = await response.json(); console.error('Error creating task:', error); @@ -268,12 +368,23 @@ function showAddTaskModal(selectedDate = null) { function showAddEventModal(selectedDate = null) { const today = getTodayInLondon(); + const todayWeekStart = getWeekStart(today); + const nextWeekStart = new Date(todayWeekStart + 'T00:00:00'); + nextWeekStart.setDate(nextWeekStart.getDate() + 7); + const nextWeekStartStr = nextWeekStart.toISOString().split('T')[0]; + + // Ограничиваем выбор дат: только текущая и следующая неделя + const minDate = todayWeekStart; + const maxDate = new Date(nextWeekStartStr + 'T00:00:00'); + maxDate.setDate(maxDate.getDate() + 6); // Воскресенье следующей недели + const maxDateStr = maxDate.toISOString().split('T')[0]; + const content = `

Добавить занятие

- +
@@ -352,7 +463,10 @@ function showAddEventModal(selectedDate = null) { const result = await response.json(); console.log('Event created:', result); closeModal(); - await loadSchedule(); + // Небольшая задержка перед обновлением, чтобы БД успела сохранить + setTimeout(() => { + loadSchedule().catch(err => console.error('Error reloading schedule:', err)); + }, 100); } else { const error = await response.json(); console.error('Error creating event:', error); @@ -379,32 +493,126 @@ async function deleteItem(id, kind) { }); if (response.ok) { - loadSchedule(); + // Небольшая задержка перед обновлением + setTimeout(() => { + loadSchedule().catch(err => console.error('Error reloading schedule:', err)); + }, 100); } else { - alert('Ошибка при удалении'); + const error = await response.json(); + alert('Ошибка при удалении: ' + (error.detail || 'Неизвестная ошибка')); } } catch (error) { - alert('Ошибка при удалении'); + alert('Ошибка при удалении: ' + error.message); console.error(error); } } +function updateWeekNavigationButtons() { + const today = getTodayInLondon(); + const todayWeekStart = getWeekStart(today); + + // Вычисляем следующую неделю + const [year, month, day] = todayWeekStart.split('-').map(Number); + const nextWeekStartDate = new Date(year, month - 1, day); + nextWeekStartDate.setDate(nextWeekStartDate.getDate() + 7); + const nextWeekStartStr = `${nextWeekStartDate.getFullYear()}-${String(nextWeekStartDate.getMonth() + 1).padStart(2, '0')}-${String(nextWeekStartDate.getDate()).padStart(2, '0')}`; + + // Создаем даты для сравнения + const [currYear, currMonth, currDay] = currentWeekStart.split('-').map(Number); + const currentWeekStartDate = new Date(currYear, currMonth - 1, currDay); + + const [todayYear, todayMonth, todayDay] = todayWeekStart.split('-').map(Number); + const todayWeekStartDate = new Date(todayYear, todayMonth - 1, todayDay); + + const [nextYear, nextMonth, nextDay] = nextWeekStartStr.split('-').map(Number); + const nextWeekStartDateObj = new Date(nextYear, nextMonth - 1, nextDay); + + // Отключаем кнопку "предыдущая неделя", если уже на текущей неделе + const prevButton = document.getElementById('prev-week'); + if (currentWeekStartDate.getTime() <= todayWeekStartDate.getTime()) { + prevButton.disabled = true; + prevButton.style.opacity = '0.5'; + prevButton.style.cursor = 'not-allowed'; + } else { + prevButton.disabled = false; + prevButton.style.opacity = '1'; + prevButton.style.cursor = 'pointer'; + } + + // Отключаем кнопку "следующая неделя", если уже на следующей неделе + const nextButton = document.getElementById('next-week'); + if (currentWeekStartDate.getTime() >= nextWeekStartDateObj.getTime()) { + nextButton.disabled = true; + nextButton.style.opacity = '0.5'; + nextButton.style.cursor = 'not-allowed'; + } else { + nextButton.disabled = false; + nextButton.style.opacity = '1'; + nextButton.style.cursor = 'pointer'; + } +} + // Инициализация document.addEventListener('DOMContentLoaded', () => { currentWeekStart = getWeekStart(); loadSchedule(); + updateWeekNavigationButtons(); document.getElementById('prev-week').addEventListener('click', () => { - const date = new Date(currentWeekStart + 'T00:00:00'); - date.setDate(date.getDate() - 7); - currentWeekStart = date.toISOString().split('T')[0]; + const today = getTodayInLondon(); + const todayWeekStart = getWeekStart(today); + + // Создаем даты для сравнения + const [currYear, currMonth, currDay] = currentWeekStart.split('-').map(Number); + const currentWeekStartDate = new Date(currYear, currMonth - 1, currDay); + + const [todayYear, todayMonth, todayDay] = todayWeekStart.split('-').map(Number); + const todayWeekStartDate = new Date(todayYear, todayMonth - 1, todayDay); + + // Разрешаем только текущую и следующую неделю + // Если текущая неделя - это сегодняшняя неделя, не разрешаем идти назад + if (currentWeekStartDate.getTime() <= todayWeekStartDate.getTime()) { + console.log('Already on current week, cannot go back'); + return; // Уже на текущей неделе, нельзя идти назад + } + + // Переходим на предыдущую неделю + const prevWeekDate = new Date(currYear, currMonth - 1, currDay); + prevWeekDate.setDate(prevWeekDate.getDate() - 7); + currentWeekStart = `${prevWeekDate.getFullYear()}-${String(prevWeekDate.getMonth() + 1).padStart(2, '0')}-${String(prevWeekDate.getDate()).padStart(2, '0')}`; + // Убеждаемся, что это понедельник + currentWeekStart = getWeekStart(currentWeekStart); + console.log('Moving to previous week:', currentWeekStart); loadSchedule(); }); document.getElementById('next-week').addEventListener('click', () => { - const date = new Date(currentWeekStart + 'T00:00:00'); - date.setDate(date.getDate() + 7); - currentWeekStart = date.toISOString().split('T')[0]; + const today = getTodayInLondon(); + const todayWeekStart = getWeekStart(today); + + // Вычисляем следующую неделю от текущей + const [year, month, day] = currentWeekStart.split('-').map(Number); + const nextWeekDate = new Date(year, month - 1, day); + nextWeekDate.setDate(nextWeekDate.getDate() + 7); + const nextWeekStartStr = `${nextWeekDate.getFullYear()}-${String(nextWeekDate.getMonth() + 1).padStart(2, '0')}-${String(nextWeekDate.getDate()).padStart(2, '0')}`; + + // Вычисляем максимально допустимую следующую неделю (от сегодня) + const [todayYear, todayMonth, todayDay] = todayWeekStart.split('-').map(Number); + const maxNextWeekDate = new Date(todayYear, todayMonth - 1, todayDay); + maxNextWeekDate.setDate(maxNextWeekDate.getDate() + 7); + const maxNextWeekStr = `${maxNextWeekDate.getFullYear()}-${String(maxNextWeekDate.getMonth() + 1).padStart(2, '0')}-${String(maxNextWeekDate.getDate()).padStart(2, '0')}`; + + // Разрешаем только текущую и следующую неделю + if (nextWeekStartStr > maxNextWeekStr) { + console.log('Already on next week, cannot go further'); + return; // Уже на следующей неделе, нельзя идти дальше + } + + // Переходим на следующую неделю + currentWeekStart = nextWeekStartStr; + // Убеждаемся, что это понедельник + currentWeekStart = getWeekStart(currentWeekStart); + console.log('Moving to next week:', currentWeekStart); loadSchedule(); });