diff --git a/backend/static/js/components/calendarWidget.js b/backend/static/js/components/calendarWidget.js
index 828499b3..5e432fc6 100644
--- a/backend/static/js/components/calendarWidget.js
+++ b/backend/static/js/components/calendarWidget.js
@@ -52,6 +52,10 @@ import {
apiGet,
apiDelete
} from "./calendarWidget/apiClient.js";
+import {
+ renderVertical,
+ updateWeekLists as updateWeekListsRenderer
+} from "./calendarWidget/weekListRenderer.js";
/**
* Инициализация виджета календаря
@@ -509,107 +513,15 @@ export function initCalendarWidget(options = {}) {
};
}
- function renderVertical(container, rangeLabel, events, ws, we) {
- if (!container) return;
- container.innerHTML = "";
- if (rangeLabel) {
- const df = new Intl.DateTimeFormat("ru-RU", {
- day: "2-digit",
- month: "long",
- });
- rangeLabel.textContent = `${df.format(ws)} — ${df.format(new Date(we.getTime() - dayMs))}`;
- }
- const dfDow = new Intl.DateTimeFormat("ru-RU", { weekday: "short" });
- const dfMD = new Intl.DateTimeFormat("ru-RU", {
- day: "2-digit",
- month: "2-digit",
- });
-
- const list = events
- .map(normalizeEvent)
- .filter(Boolean)
- .filter((ev) => overlaps(ev, ws, we))
- .sort((a, b) => a.start - b.start);
-
- if (!list.length) {
- const e = document.createElement("div");
- e.className = "week-vertical empty";
- e.textContent = "Нет событий на этой неделе";
- container.appendChild(e);
- return;
- }
-
- list.forEach((ev) => {
- const row = document.createElement("div");
- row.className = "week-row";
-
- // Сохраняем ID события для обработчика клика (уже нормализовано в ev.id)
- if (ev.id) {
- row.dataset.eventId = String(ev.id);
- row.style.cursor = "pointer";
- row.addEventListener("click", () => {
- console.log("[CalendarWidget] Opening event details:", ev.id);
- openEventDetailsById(ev.id);
- });
- }
-
- // Цветовая точка слева от заголовка
- const dot = document.createElement("span");
- dot.className = "color-dot";
- dot.style.backgroundColor = ev.color || DEFAULT_EVENT_COLOR;
- const badge = document.createElement("div");
- badge.className = "date-badge";
- const dow = dfDow.format(ev.start),
- md = dfMD.format(ev.start);
- badge.innerHTML = `
${dow}
${md}
`;
- const cont = document.createElement("div");
- cont.className = "content";
- const title = document.createElement("div");
- title.className = "title";
- title.textContent = ev.title || "Без названия";
- title.title = ev.title || "";
- // Строка с источником, локацией и кратким описанием
- const meta = document.createElement("div");
- meta.className = "meta";
- const dateSpan =
- ev.end - ev.start > dayMs
- ? `${dfMD.format(ev.start)} — ${dfMD.format(new Date(ev.end - 1))}`
- : `${md}`;
- const parts = [ev.sourceLabel || "—"];
- if (ev.location) parts.push(ev.location);
- if (ev.description) parts.push(truncate(ev.description, 20));
- meta.textContent = `${dateSpan} • ${parts.join(" • ")}`;
- const head = document.createElement("div");
- head.style.display = "flex";
- head.style.alignItems = "center";
- head.style.gap = "8px";
- head.append(dot, title);
- cont.append(head, meta);
- row.append(badge, cont);
- container.appendChild(row);
- });
- }
-
+ // Используем renderVertical и updateWeekLists из weekListRenderer.js
+
+ // Обёртка для updateWeekLists с передачей нужных параметров
async function updateWeekLists() {
- const now = new Date();
- const ws = startOfWeek(now),
- we = endOfWeek(now);
- const from = new Date(ws.getTime() - 3 * dayMs),
- to = new Date(we.getTime() + 3 * dayMs);
- const items = await fetchEventsAllCalendars(from, to);
- renderVertical(
- document.getElementById("weekList"),
- document.getElementById("weekRange"),
- items,
- ws,
- we,
- );
- renderVertical(
- document.getElementById("weekListMobile"),
- document.getElementById("weekRangeMobile"),
- items,
- ws,
- we,
+ await updateWeekListsRenderer(
+ fetchEventsAllCalendars,
+ normalizeEvent,
+ openEventDetailsById,
+ DEFAULT_EVENT_COLOR
);
}
diff --git a/backend/static/js/components/calendarWidget/weekListRenderer.js b/backend/static/js/components/calendarWidget/weekListRenderer.js
new file mode 100644
index 00000000..7ffe410b
--- /dev/null
+++ b/backend/static/js/components/calendarWidget/weekListRenderer.js
@@ -0,0 +1,155 @@
+/**
+ * weekListRenderer.js
+ * Рендеринг бокового списка событий по неделям
+ * @module calendarWidget/weekListRenderer
+ */
+
+import {
+ startOfWeek,
+ endOfWeek,
+ overlaps,
+ truncate,
+ dayMs
+} from "./helpers.js";
+
+/**
+ * Отрендерить вертикальный список событий недели
+ * @param {HTMLElement} container - Контейнер для списка
+ * @param {HTMLElement} rangeLabel - Элемент для отображения диапазона дат
+ * @param {Array} events - Массив событий
+ * @param {Date} ws - Начало недели
+ * @param {Date} we - Конец недели
+ * @param {Function} normalizeEvent - Функция нормализации события
+ * @param {Function} openEventDetailsById - Функция открытия детальной карточки
+ * @param {string} defaultColor - Цвет по умолчанию
+ */
+export function renderVertical(
+ container,
+ rangeLabel,
+ events,
+ ws,
+ we,
+ normalizeEvent,
+ openEventDetailsById,
+ defaultColor
+) {
+ if (!container) return;
+ container.innerHTML = "";
+ if (rangeLabel) {
+ const df = new Intl.DateTimeFormat("ru-RU", {
+ day: "2-digit",
+ month: "long",
+ });
+ rangeLabel.textContent = `${df.format(ws)} — ${df.format(new Date(we.getTime() - dayMs))}`;
+ }
+ const dfDow = new Intl.DateTimeFormat("ru-RU", { weekday: "short" });
+ const dfMD = new Intl.DateTimeFormat("ru-RU", {
+ day: "2-digit",
+ month: "2-digit",
+ });
+
+ const list = events
+ .map(normalizeEvent)
+ .filter(Boolean)
+ .filter((ev) => overlaps(ev, ws, we))
+ .sort((a, b) => a.start - b.start);
+
+ if (!list.length) {
+ const e = document.createElement("div");
+ e.className = "week-vertical empty";
+ e.textContent = "Нет событий на этой неделе";
+ container.appendChild(e);
+ return;
+ }
+
+ list.forEach((ev) => {
+ const row = document.createElement("div");
+ row.className = "week-row";
+
+ // Сохраняем ID события для обработчика клика (уже нормализовано в ev.id)
+ if (ev.id) {
+ row.dataset.eventId = String(ev.id);
+ row.style.cursor = "pointer";
+ row.addEventListener("click", () => {
+ console.log("[CalendarWidget] Opening event details:", ev.id);
+ openEventDetailsById(ev.id);
+ });
+ }
+
+ // Цветовая точка слева от заголовка
+ const dot = document.createElement("span");
+ dot.className = "color-dot";
+ dot.style.backgroundColor = ev.color || defaultColor;
+ const badge = document.createElement("div");
+ badge.className = "date-badge";
+ const dow = dfDow.format(ev.start),
+ md = dfMD.format(ev.start);
+ badge.innerHTML = `${dow}
${md}
`;
+ const cont = document.createElement("div");
+ cont.className = "content";
+ const title = document.createElement("div");
+ title.className = "title";
+ title.textContent = ev.title || "Без названия";
+ title.title = ev.title || "";
+ // Строка с источником, локацией и кратким описанием
+ const meta = document.createElement("div");
+ meta.className = "meta";
+ const dateSpan =
+ ev.end - ev.start > dayMs
+ ? `${dfMD.format(ev.start)} — ${dfMD.format(new Date(ev.end - 1))}`
+ : `${md}`;
+ const parts = [ev.sourceLabel || "—"];
+ if (ev.location) parts.push(ev.location);
+ if (ev.description) parts.push(truncate(ev.description, 20));
+ meta.textContent = `${dateSpan} • ${parts.join(" • ")}`;
+ const head = document.createElement("div");
+ head.style.display = "flex";
+ head.style.alignItems = "center";
+ head.style.gap = "8px";
+ head.append(dot, title);
+ cont.append(head, meta);
+ row.append(badge, cont);
+ container.appendChild(row);
+ });
+}
+
+/**
+ * Обновить списки событий текущей недели (десктоп и мобильная версии)
+ * @param {Function} fetchEventsAllCalendars - Функция загрузки событий
+ * @param {Function} normalizeEvent - Функция нормализации события
+ * @param {Function} openEventDetailsById - Функция открытия детальной карточки
+ * @param {string} defaultColor - Цвет по умолчанию
+ */
+export async function updateWeekLists(
+ fetchEventsAllCalendars,
+ normalizeEvent,
+ openEventDetailsById,
+ defaultColor
+) {
+ const now = new Date();
+ const ws = startOfWeek(now),
+ we = endOfWeek(now);
+ const from = new Date(ws.getTime() - 3 * dayMs),
+ to = new Date(we.getTime() + 3 * dayMs);
+ const items = await fetchEventsAllCalendars(from, to);
+ renderVertical(
+ document.getElementById("weekList"),
+ document.getElementById("weekRange"),
+ items,
+ ws,
+ we,
+ normalizeEvent,
+ openEventDetailsById,
+ defaultColor
+ );
+ renderVertical(
+ document.getElementById("weekListMobile"),
+ document.getElementById("weekRangeMobile"),
+ items,
+ ws,
+ we,
+ normalizeEvent,
+ openEventDetailsById,
+ defaultColor
+ );
+}