/** * @fileoverview Calendar Manager Component - управление списком календарей * @module components/calendarManager */ import { getMyCalendars, subscribeToCalendar, unsubscribeFromCalendar, invalidateCalendarsCache, } from "../api/calendarsApi.js"; import { CALENDAR_TYPES, CALENDAR_COLORS, createLegacyDeptId, } from "../constants/calendarTypes.js"; /** * Инициализация менеджера календарей * @param {Object} options - Параметры * @param {string} options.containerId - ID контейнера для списка календарей * @param {Function} options.onCalendarToggle - Callback при переключении видимости календаря * @param {Function} options.onCalendarsChange - Callback при изменении списка календарей * @returns {Object} API менеджера */ export function initCalendarManager(options = {}) { const { containerId = "calendarListContainer", onCalendarToggle = () => {}, onCalendarsChange = () => {}, } = options; const container = document.getElementById(containerId); if (!container) { console.warn(`[CalendarManager] Container #${containerId} not found`); return null; } // Состояние let calendars = []; let visibleCalendarIds = new Set(); /** * Создать псевдо-календари для legacy режима * @returns {Array} Legacy календари */ function createLegacyCalendars() { const legacyCalendars = [ { id: CALENDAR_TYPES.LEGACY_COMPANY, title: "Компания", description: "Корпоративные события", color: CALENDAR_COLORS.COMPANY, calendar_type: CALENDAR_TYPES.COMPANY, is_legacy: true, is_global: true, user_can_edit: false, user_can_view: true, is_subscribed: true, }, { id: CALENDAR_TYPES.LEGACY_PERSONAL, title: "Личный календарь", description: "Мои личные события", color: CALENDAR_COLORS.PERSONAL, calendar_type: CALENDAR_TYPES.PERSONAL, is_legacy: true, is_personal: true, user_can_edit: true, user_can_view: true, is_subscribed: true, }, ]; // Добавить календари отделов из calendarWidget const deptObjects = window.calendarWidget?.getDepartments?.() || []; 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); return; } legacyCalendars.push({ id: createLegacyDeptId(deptId), title: dept.name || `Отдел ${deptId}`, description: "События отдела", color: CALENDAR_COLORS.DEPARTMENT, calendar_type: CALENDAR_TYPES.DEPARTMENT, department_id: parseInt(deptId, 10), is_legacy: true, is_department: true, user_can_edit: false, user_can_view: true, is_subscribed: true, }); }); console.log("[CalendarManager] Created legacy calendars:", legacyCalendars); return legacyCalendars; } /** * Загрузить список календарей */ async function loadCalendars() { try { // Загрузить новые Calendar записи const newCalendars = await getMyCalendars(); // Добавить legacy календари const legacyCalendars = createLegacyCalendars(); // Объединить оба списка calendars = [...legacyCalendars, ...newCalendars]; console.log("[CalendarManager] Loaded calendars:", { legacy: legacyCalendars.length, new: newCalendars.length, total: calendars.length, 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), ); 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); container.innerHTML = `
Не удалось загрузить календари
`; } } /** * Переключить видимость календаря * @param {number} calendarId */ function toggleCalendarVisibility(calendarId) { if (visibleCalendarIds.has(calendarId)) { visibleCalendarIds.delete(calendarId); } else { visibleCalendarIds.add(calendarId); } render(); onCalendarToggle(calendarId, visibleCalendarIds.has(calendarId)); } /** * Подписаться на календарь * @param {number} calendarId */ async function subscribe(calendarId) { try { await subscribeToCalendar(calendarId); await loadCalendars(); // Перезагрузить список } catch (error) { console.error("[CalendarManager] Subscribe failed:", error); alert("Не удалось подписаться на календарь"); } } /** * Отписаться от календаря * @param {number} calendarId */ async function unsubscribe(calendarId) { if (!confirm("Отписаться от этого календаря?")) return; try { await unsubscribeFromCalendar(calendarId); await loadCalendars(); // Перезагрузить список } catch (error) { console.error("[CalendarManager] Unsubscribe failed:", error); alert("Не удалось отписаться от календаря"); } } /** * Получить иконку для типа календаря * @param {Object} calendar * @returns {string} HTML иконки */ function getCalendarIcon(calendar) { if (calendar.is_personal) { return ''; } else if (calendar.is_department) { return ''; } else if (calendar.is_global) { return ''; } return ''; } /** * Получить бейдж владельца * @param {Object} calendar * @returns {string} HTML бейджа */ function getOwnerBadge(calendar) { if (calendar.is_personal && calendar.owner_user_name) { return `${calendar.owner_user_name}`; } else if (calendar.is_department && calendar.owner_department_name) { return `${calendar.owner_department_name}`; } else if (calendar.is_global) { return `Общий`; } return ""; } /** * Рендер списка календарей */ function render() { if (calendars.length === 0) { container.innerHTML = `
Нет доступных календарей
`; 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(""); container.innerHTML = html; attachEventListeners(); } /** * Прикрепить обработчики событий */ function attachEventListeners() { // Переключение видимости 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); } }); }); // Отписка (только для новых календарей с числовым 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, 10); const calendar = calendars.find((c) => c.id === calendarId); if (calendar) { // Dispatch custom event для открытия модального окна document.dispatchEvent( new CustomEvent("calendar:edit", { detail: calendar }), ); } }); }); } /** * Обновить список календарей */ async function refresh() { invalidateCalendarsCache(); await loadCalendars(); } /** * Получить ID видимых календарей * @returns {Array} */ function getVisibleCalendarIds() { return Array.from(visibleCalendarIds); } /** * Установить видимость календарей * @param {Array} calendarIds */ function setVisibleCalendars(calendarIds) { visibleCalendarIds = new Set(calendarIds); render(); onCalendarsChange(calendars, Array.from(visibleCalendarIds)); } // Инициализация loadCalendars(); // Public API return { refresh, getVisibleCalendarIds, setVisibleCalendars, loadCalendars, }; }