scheduleSon/frontend/admin/static/script.js

435 lines
17 KiB
JavaScript
Raw Normal View History

let currentWeekStart = null;
const weekdays = ['Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота', 'Воскресенье'];
const weekdaysShort = ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'];
function getTodayInLondon() {
// Получаем текущую дату в таймзоне London
const now = new Date();
const formatter = new Intl.DateTimeFormat('en-CA', {
timeZone: 'Europe/London',
year: 'numeric',
month: '2-digit',
day: '2-digit'
});
return formatter.format(now);
}
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];
}
function formatDate(dateStr) {
const date = new Date(dateStr + 'T00:00:00');
return `${weekdaysShort[date.getDay()]}, ${date.getDate()}`;
}
function formatDateFull(dateStr) {
const date = new Date(dateStr + 'T00:00:00');
const months = ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня',
'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'];
return `${weekdays[date.getDay()]}, ${date.getDate()} ${months[date.getMonth()]}`;
}
function getWeekDates(startDate) {
const dates = [];
const start = new Date(startDate + 'T00:00:00');
for (let i = 0; i < 7; i++) {
const date = new Date(start);
date.setDate(start.getDate() + i);
dates.push(date.toISOString().split('T')[0]);
}
return dates;
}
async function loadSchedule() {
const weekDates = getWeekDates(currentWeekStart);
const fromDate = weekDates[0];
const toDate = weekDates[6];
console.log('Loading schedule from', fromDate, 'to', toDate);
try {
const response = await fetch(`/api/schedule?from_date=${fromDate}&to_date=${toDate}`);
const data = await response.json();
console.log('Received items:', data.items);
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])}`;
} catch (error) {
console.error('Error loading schedule:', error);
}
}
function renderSchedule(weekDates, items) {
const grid = document.getElementById('schedule-grid');
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 column = document.createElement('div');
column.className = 'day-column';
column.innerHTML = `
<div class="day-header">${formatDateFull(dateStr)}</div>
<div class="day-items" data-date="${dateStr}">
${renderDayItems(dayItems)}
</div>
`;
grid.appendChild(column);
});
}
function renderDayItems(items) {
console.log('Rendering items for day:', items);
if (items.length === 0) {
return '<div style="color: #999; font-style: italic; padding: 10px;">Нет записей</div>';
}
const tasks = items.filter(item => item.kind === 'task');
const events = items.filter(item => item.kind === 'event');
console.log('Tasks:', tasks, 'Events:', events);
let html = '';
// Tasks
tasks.forEach(task => {
html += `
<div class="item task" data-id="${task.id}" data-kind="task">
<div class="item-title">${task.title}</div>
${task.repeat_weekly ? '<div style="font-size: 11px; color: #FF9800;">Повторяется</div>' : ''}
<div class="item-actions">
<button class="btn-edit" onclick="editItem(${task.id}, 'task')">Изменить</button>
<button class="btn-delete" onclick="deleteItem(${task.id}, 'task')">Удалить</button>
</div>
</div>
`;
});
// Events
events.sort((a, b) => a.start_time.localeCompare(b.start_time)).forEach(event => {
const endTime = calculateEndTime(event.start_time, event.duration_min);
html += `
<div class="item event" data-id="${event.id}" data-kind="event">
<div class="item-time">${event.start_time}-${endTime}</div>
<div class="item-title">${event.title}</div>
<div class="item-actions">
<button class="btn-edit" onclick="editItem(${event.id}, 'event')">Изменить</button>
<button class="btn-delete" onclick="deleteItem(${event.id}, 'event')">Удалить</button>
</div>
</div>
`;
});
return html;
}
function calculateEndTime(startTime, durationMin) {
const [hour, minute] = startTime.split(':').map(Number);
const totalMinutes = hour * 60 + minute + durationMin;
const endHour = Math.floor(totalMinutes / 60);
const endMinute = totalMinutes % 60;
return `${String(endHour).padStart(2, '0')}:${String(endMinute).padStart(2, '0')}`;
}
function showModal(content) {
document.getElementById('modal-body').innerHTML = content;
document.getElementById('modal').style.display = 'block';
}
function closeModal() {
document.getElementById('modal').style.display = 'none';
}
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;
// getDay() возвращает 0=воскресенье, 1=понедельник, ..., 6=суббота
// Конвертируем в формат 0=понедельник, 6=воскресенье
const jsWeekday = selectedDateObj.getDay();
const selectedWeekday = jsWeekday === 0 ? 6 : jsWeekday - 1; // 0=пн, 6=вс
const remainingWeekdays = [];
// Оставшиеся дни недели после выбранного (среда=2, четверг=3, пятница=4, суббота=5, воскресенье=6)
for (let i = selectedWeekday + 1; i <= 6; i++) {
remainingWeekdays.push(i);
}
const content = `
<h2>Добавить задачу</h2>
<form id="add-task-form">
<div class="form-group">
<label>Дата:</label>
<input type="date" id="task-date" value="${selectedDate || today}" required>
</div>
<div class="form-group">
<label>Название:</label>
<textarea id="task-title" required></textarea>
</div>
<div class="form-group">
<div class="checkbox-group">
<input type="checkbox" id="task-repeat-weekly">
<label for="task-repeat-weekly">Повторять каждую неделю</label>
</div>
</div>
<div class="form-group" id="copy-weekdays-group" style="display: none;">
<label>Добавить на другие дни недели:</label>
<div class="weekdays-select">
${remainingWeekdays.map(day => {
// day в формате 0=пн, 6=вс, но weekdaysShort использует JS формат (0=вс)
const jsDay = day === 6 ? 0 : day + 1;
return `
<label>
<input type="checkbox" name="copy-weekday" value="${day}">
${weekdaysShort[jsDay]}
</label>
`;
}).join('')}
</div>
</div>
<div style="margin-top: 20px;">
<button type="submit" class="btn btn-primary">Добавить</button>
<button type="button" class="btn" onclick="closeModal()">Отмена</button>
</div>
</form>
`;
showModal(content);
document.getElementById('task-repeat-weekly').addEventListener('change', function() {
document.getElementById('copy-weekdays-group').style.display =
this.checked ? 'none' : 'block';
});
document.getElementById('add-task-form').addEventListener('submit', async (e) => {
e.preventDefault();
const date = document.getElementById('task-date').value;
const title = document.getElementById('task-title').value;
const repeatWeekly = document.getElementById('task-repeat-weekly').checked;
const copyWeekdays = Array.from(document.querySelectorAll('input[name="copy-weekday"]:checked'))
.map(cb => parseInt(cb.value));
try {
const response = await fetch('/api/events?kind=task', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
date,
title,
repeat_weekly: repeatWeekly,
copy_to_weekdays: copyWeekdays.length > 0 ? copyWeekdays : null
})
});
if (response.ok) {
const result = await response.json();
console.log('Task created:', result);
closeModal();
await loadSchedule();
} else {
const error = await response.json();
console.error('Error creating task:', error);
alert('Ошибка: ' + (error.detail || 'Неизвестная ошибка'));
}
} catch (error) {
console.error('Exception creating task:', error);
alert('Ошибка при добавлении задачи: ' + error.message);
}
});
}
function showAddEventModal(selectedDate = null) {
const today = getTodayInLondon();
const content = `
<h2>Добавить занятие</h2>
<form id="add-event-form">
<div class="form-group">
<label>Дата:</label>
<input type="date" id="event-date" value="${selectedDate || today}" required>
</div>
<div class="form-group">
<label>Время начала:</label>
<div style="display: flex; gap: 10px;">
<select id="event-hour" required>
${Array.from({length: 13}, (_, i) => i + 8).map(h =>
`<option value="${String(h).padStart(2, '0')}">${String(h).padStart(2, '0')}</option>`
).join('')}
</select>
<select id="event-minute" required>
<option value="00">00</option>
<option value="15">15</option>
<option value="30">30</option>
<option value="45">45</option>
</select>
</div>
</div>
<div class="form-group">
<label>Длительность:</label>
<div style="display: flex; gap: 10px;">
<select id="event-duration-hour">
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<select id="event-duration-minute">
<option value="00">00</option>
<option value="15">15</option>
<option value="30">30</option>
<option value="45">45</option>
</select>
</div>
</div>
<div class="form-group">
<label>Название:</label>
<textarea id="event-title" required></textarea>
</div>
<div style="margin-top: 20px;">
<button type="submit" class="btn btn-primary">Добавить</button>
<button type="button" class="btn" onclick="closeModal()">Отмена</button>
</div>
</form>
`;
showModal(content);
document.getElementById('add-event-form').addEventListener('submit', async (e) => {
e.preventDefault();
const date = document.getElementById('event-date').value;
const hour = document.getElementById('event-hour').value;
const minute = document.getElementById('event-minute').value;
const durHour = parseInt(document.getElementById('event-duration-hour').value);
const durMin = parseInt(document.getElementById('event-duration-minute').value);
const title = document.getElementById('event-title').value;
const durationMin = durHour * 60 + durMin;
if (durationMin === 0) {
alert('Длительность должна быть минимум 15 минут');
return;
}
try {
const response = await fetch('/api/events?kind=event', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
date,
start_time: `${hour}:${minute}`,
duration_min: durationMin,
title
})
});
if (response.ok) {
const result = await response.json();
console.log('Event created:', result);
closeModal();
await loadSchedule();
} else {
const error = await response.json();
console.error('Error creating event:', error);
alert('Ошибка: ' + (error.detail || 'Неизвестная ошибка'));
}
} catch (error) {
console.error('Exception creating event:', error);
alert('Ошибка при добавлении занятия: ' + error.message);
}
});
}
async function editItem(id, kind) {
// TODO: Реализовать редактирование
alert('Редактирование будет реализовано позже');
}
async function deleteItem(id, kind) {
if (!confirm('Удалить эту запись?')) return;
try {
const response = await fetch(`/api/events/${id}`, {
method: 'DELETE'
});
if (response.ok) {
loadSchedule();
} else {
alert('Ошибка при удалении');
}
} catch (error) {
alert('Ошибка при удалении');
console.error(error);
}
}
// Инициализация
document.addEventListener('DOMContentLoaded', () => {
currentWeekStart = getWeekStart();
loadSchedule();
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];
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];
loadSchedule();
});
document.getElementById('add-task-btn').addEventListener('click', () => showAddTaskModal());
document.getElementById('add-event-btn').addEventListener('click', () => showAddEventModal());
document.querySelector('.close').addEventListener('click', closeModal);
window.addEventListener('click', (e) => {
const modal = document.getElementById('modal');
if (e.target === modal) {
closeModal();
}
});
// Клик по дню для добавления
document.addEventListener('click', (e) => {
if (e.target.closest('.day-items')) {
const date = e.target.closest('.day-items').dataset.date;
if (e.ctrlKey || e.metaKey) {
showAddEventModal(date);
} else if (e.shiftKey) {
showAddTaskModal(date);
}
}
});
});