From db32f142fa64507a3292f10544d4e1aefc1161be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=B3=D0=BE=D1=80=D1=8C=20=D0=9D=D0=B0=D0=B4=D0=B5?= =?UTF-8?q?=D0=B8=D0=BD?= Date: Wed, 11 Feb 2026 12:29:38 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=BA=D0=B0=20=D1=81=D1=82=D1=80=D0=BE=D0=BA=D0=BE=D0=B2?= =?UTF-8?q?=D1=8B=D1=85=20ID=20legacy=20=D0=BA=D0=B0=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D0=B4=D0=B0=D1=80=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Проблема: parseInt('legacy-company') возвращал NaN Решение: - Не парсим calendarId при toggle visibility - Проверяем /^\d+$/ перед parseInt - только для числовых ID - Добавлена валидация isNaN перед subscribe/unsubscribe - Legacy календари (строковые ID) работают корректно - Новые календари (числовые ID) конвертируются в number Fixes: calendar_id=NaN запросы к API --- .../static/js/components/calendarManager.js | 214 ++++++++++-------- 1 file changed, 119 insertions(+), 95 deletions(-) diff --git a/backend/static/js/components/calendarManager.js b/backend/static/js/components/calendarManager.js index d287d0f6..d839f32c 100644 --- a/backend/static/js/components/calendarManager.js +++ b/backend/static/js/components/calendarManager.js @@ -7,8 +7,8 @@ import { getMyCalendars, subscribeToCalendar, unsubscribeFromCalendar, - invalidateCalendarsCache -} from '../api/calendarsApi.js'; + invalidateCalendarsCache, +} from "../api/calendarsApi.js"; /** * Инициализация менеджера календарей @@ -20,9 +20,9 @@ import { */ export function initCalendarManager(options = {}) { const { - containerId = 'calendarListContainer', + containerId = "calendarListContainer", onCalendarToggle = () => {}, - onCalendarsChange = () => {} + onCalendarsChange = () => {}, } = options; const container = document.getElementById(containerId); @@ -42,58 +42,58 @@ export function initCalendarManager(options = {}) { function createLegacyCalendars() { const legacyCalendars = [ { - id: 'legacy-company', - title: 'Компания', - description: 'Корпоративные события', - color: '#dc3545', - calendar_type: 'company', + id: "legacy-company", + title: "Компания", + description: "Корпоративные события", + color: "#dc3545", + calendar_type: "company", is_legacy: true, is_global: true, user_can_edit: false, user_can_view: true, - is_subscribed: true + is_subscribed: true, }, { - id: 'legacy-personal', - title: 'Личный календарь', - description: 'Мои личные события', - color: '#0d6efd', - calendar_type: 'personal', + id: "legacy-personal", + title: "Личный календарь", + description: "Мои личные события", + color: "#0d6efd", + calendar_type: "personal", is_legacy: true, is_personal: true, user_can_edit: true, user_can_view: true, - is_subscribed: true - } + is_subscribed: true, + }, ]; - + // Добавить календари отделов из calendarWidget const deptObjects = window.calendarWidget?.getDepartments?.() || []; - console.log('[CalendarManager] Department objects:', deptObjects); - + console.log("[CalendarManager] Department objects:", deptObjects); + deptObjects.forEach((dept) => { const deptId = dept?.id || dept?.pk || dept?.department_id; if (!deptId) { - console.warn('[CalendarManager] Department object missing ID:', dept); + console.warn("[CalendarManager] Department object missing ID:", dept); return; } - + legacyCalendars.push({ id: `legacy-dept-${deptId}`, title: dept.name || `Отдел ${deptId}`, - description: 'События отдела', - color: '#198754', - calendar_type: 'department', + description: "События отдела", + color: "#198754", + calendar_type: "department", department_id: parseInt(deptId, 10), is_legacy: true, is_department: true, user_can_edit: false, user_can_view: true, - is_subscribed: true + is_subscribed: true, }); }); - - console.log('[CalendarManager] Created legacy calendars:', legacyCalendars); + + console.log("[CalendarManager] Created legacy calendars:", legacyCalendars); return legacyCalendars; } @@ -104,33 +104,36 @@ export function initCalendarManager(options = {}) { try { // Загрузить новые Calendar записи const newCalendars = await getMyCalendars(); - + // Добавить legacy календари const legacyCalendars = createLegacyCalendars(); - + // Объединить оба списка calendars = [...legacyCalendars, ...newCalendars]; - - console.log('[CalendarManager] Loaded calendars:', { + + console.log("[CalendarManager] Loaded calendars:", { legacy: legacyCalendars.length, new: newCalendars.length, total: calendars.length, - calendarIds: calendars.map(c => ({ id: c.id, title: c.title })) + calendarIds: calendars.map((c) => ({ id: c.id, title: c.title })), }); - + // По умолчанию все календари видимы (фильтруем null/undefined) visibleCalendarIds = new Set( calendars - .map(cal => cal.id) - .filter(id => id !== null && id !== undefined) + .map((cal) => cal.id) + .filter((id) => id !== null && id !== undefined), ); - - console.log('[CalendarManager] Visible calendar IDs:', Array.from(visibleCalendarIds)); - + + console.log( + "[CalendarManager] Visible calendar IDs:", + Array.from(visibleCalendarIds), + ); + render(); onCalendarsChange(calendars, Array.from(visibleCalendarIds)); } catch (error) { - console.error('[CalendarManager] Failed to load calendars:', error); + console.error("[CalendarManager] Failed to load calendars:", error); container.innerHTML = `
@@ -150,7 +153,7 @@ export function initCalendarManager(options = {}) { } else { visibleCalendarIds.add(calendarId); } - + render(); onCalendarToggle(calendarId, visibleCalendarIds.has(calendarId)); } @@ -164,8 +167,8 @@ export function initCalendarManager(options = {}) { await subscribeToCalendar(calendarId); await loadCalendars(); // Перезагрузить список } catch (error) { - console.error('[CalendarManager] Subscribe failed:', error); - alert('Не удалось подписаться на календарь'); + console.error("[CalendarManager] Subscribe failed:", error); + alert("Не удалось подписаться на календарь"); } } @@ -174,14 +177,14 @@ export function initCalendarManager(options = {}) { * @param {number} calendarId */ async function unsubscribe(calendarId) { - if (!confirm('Отписаться от этого календаря?')) return; - + if (!confirm("Отписаться от этого календаря?")) return; + try { await unsubscribeFromCalendar(calendarId); await loadCalendars(); // Перезагрузить список } catch (error) { - console.error('[CalendarManager] Unsubscribe failed:', error); - alert('Не удалось отписаться от календаря'); + console.error("[CalendarManager] Unsubscribe failed:", error); + alert("Не удалось отписаться от календаря"); } } @@ -214,7 +217,7 @@ export function initCalendarManager(options = {}) { } else if (calendar.is_global) { return `Общий`; } - return ''; + return ""; } /** @@ -231,19 +234,20 @@ export function initCalendarManager(options = {}) { return; } - const html = calendars.map(calendar => { - const isVisible = visibleCalendarIds.has(calendar.id); - const canEdit = calendar.user_can_edit; - const isSubscribed = calendar.is_subscribed; - - return ` + const html = calendars + .map((calendar) => { + const isVisible = visibleCalendarIds.has(calendar.id); + const canEdit = calendar.user_can_edit; + const isSubscribed = calendar.is_subscribed; + + return `
-
- +
- ${canEdit ? ` - - ` : ''} - - ${!isSubscribed ? ` - - ` : ` - - `} + ` + }
`; - }).join(''); + }) + .join(""); container.innerHTML = html; attachEventListeners(); @@ -296,37 +309,48 @@ export function initCalendarManager(options = {}) { */ function attachEventListeners() { // Переключение видимости - container.querySelectorAll('.calendar-visibility-toggle').forEach(checkbox => { - checkbox.addEventListener('change', (e) => { - const calendarId = parseInt(e.target.dataset.calendarId); - toggleCalendarVisibility(calendarId); + container + .querySelectorAll(".calendar-visibility-toggle") + .forEach((checkbox) => { + checkbox.addEventListener("change", (e) => { + // НЕ парсим ID - он может быть строкой (legacy) или числом (новые календари) + const calendarId = e.target.dataset.calendarId; + // Конвертируем в число ТОЛЬКО если это число + const id = /^\d+$/.test(calendarId) ? parseInt(calendarId, 10) : calendarId; + toggleCalendarVisibility(id); + }); + }); + + // Подписка (только для новых календарей с числовым ID) + container.querySelectorAll(".calendar-subscribe-btn").forEach((btn) => { + btn.addEventListener("click", (e) => { + const calendarId = parseInt(e.currentTarget.dataset.calendarId, 10); + if (!isNaN(calendarId)) { + subscribe(calendarId); + } }); }); - // Подписка - container.querySelectorAll('.calendar-subscribe-btn').forEach(btn => { - btn.addEventListener('click', (e) => { - const calendarId = parseInt(e.currentTarget.dataset.calendarId); - subscribe(calendarId); - }); - }); - - // Отписка - container.querySelectorAll('.calendar-unsubscribe-btn').forEach(btn => { - btn.addEventListener('click', (e) => { - const calendarId = parseInt(e.currentTarget.dataset.calendarId); - unsubscribe(calendarId); + // Отписка (только для новых календарей с числовым ID) + container.querySelectorAll(".calendar-unsubscribe-btn").forEach((btn) => { + btn.addEventListener("click", (e) => { + const calendarId = parseInt(e.currentTarget.dataset.calendarId, 10); + if (!isNaN(calendarId)) { + unsubscribe(calendarId); + } }); }); // Редактирование (делегируем наверх) - container.querySelectorAll('.calendar-edit-btn').forEach(btn => { - btn.addEventListener('click', (e) => { - const calendarId = parseInt(e.currentTarget.dataset.calendarId); - const calendar = calendars.find(c => c.id === calendarId); + container.querySelectorAll(".calendar-edit-btn").forEach((btn) => { + btn.addEventListener("click", (e) => { + const calendarId = parseInt(e.currentTarget.dataset.calendarId, 10); + const calendar = calendars.find((c) => c.id === calendarId); if (calendar) { // Dispatch custom event для открытия модального окна - document.dispatchEvent(new CustomEvent('calendar:edit', { detail: calendar })); + document.dispatchEvent( + new CustomEvent("calendar:edit", { detail: calendar }), + ); } }); }); @@ -366,6 +390,6 @@ export function initCalendarManager(options = {}) { refresh, getVisibleCalendarIds, setVisibleCalendars, - loadCalendars + loadCalendars, }; }