feat: унифицированный список календарей (legacy + новые)

- calendarManager.js: добавлена функция createLegacyCalendars() для создания pseudo-calendar объектов
- loadCalendars() теперь объединяет legacy (Компания, Личный, Отделы) и новые календари в единый массив
- calendarWidgetIntegration.js: обновлена логика fetchEventsForVisibleCalendars для работы с legacy ID
  - legacy-company: все события без фильтров
  - legacy-personal: события текущего сотрудника (employee_id)
  - legacy-dept-{id}: события конкретного отдела (department_id)
- calendar_desktop.html: удален старый dropdown, оставлен только унифицированный список с чекбоксами
- Все календари теперь в одном списке с возможностью выбора через чекбоксы
- Цвета: Компания (красный), Личный (синий), Отделы (зеленый), новые календари (свои цвета)
This commit is contained in:
admin i 2026-02-11 12:18:54 +03:00
parent 299559ab4f
commit 024d3085ea
3 changed files with 134 additions and 63 deletions

View File

@ -35,25 +35,78 @@ export function initCalendarManager(options = {}) {
let calendars = [];
let visibleCalendarIds = new Set();
/**
* Создать псевдо-календари для legacy режима
* @returns {Array} Legacy календари
*/
function createLegacyCalendars() {
const legacyCalendars = [
{
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
},
{
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
}
];
// Добавить календари отделов
const deptIds = window.calendarWidget?.getDepartments?.() || [];
deptIds.forEach((deptId, index) => {
legacyCalendars.push({
id: `legacy-dept-${deptId}`,
title: `Отдел ${deptId}`,
description: 'События отдела',
color: '#198754',
calendar_type: 'department',
department_id: deptId,
is_legacy: true,
is_department: true,
user_can_edit: false,
user_can_view: true,
is_subscribed: true
});
});
return legacyCalendars;
}
/**
* Загрузить список календарей
*/
async function loadCalendars() {
try {
calendars = await getMyCalendars();
// Загрузить новые Calendar записи
const newCalendars = await getMyCalendars();
// Если календарей нет - не обновляем состояние (используем legacy режим)
if (calendars.length === 0) {
console.log('[CalendarManager] No calendars found, keeping legacy mode');
container.innerHTML = `
<div class="text-center text-muted py-3">
<i class="bi-calendar-x mb-2" style="font-size: 2rem;"></i>
<p class="mb-0">Календари не настроены</p>
<small>События отображаются в классическом режиме</small>
</div>
`;
return;
}
// Добавить legacy календари
const legacyCalendars = createLegacyCalendars();
// Объединить оба списка
calendars = [...legacyCalendars, ...newCalendars];
console.log('[CalendarManager] Loaded calendars:', {
legacy: legacyCalendars.length,
new: newCalendars.length,
total: calendars.length
});
// По умолчанию все календари видимы
visibleCalendarIds = new Set(calendars.map(cal => cal.id));

View File

@ -41,27 +41,65 @@ export function integrateCalendarManager(calendarWidgetInstance, options = {}) {
range: `${startStr} - ${endStr}`
});
// Если нет календарей в новой системе, используем legacy режим (показываем ВСЕ события)
if (calendars.length === 0) {
console.log('[CalendarIntegration] No calendars configured, using legacy mode (showing all events)');
return await getCalendarEvents({
start: startStr,
end: endStr
});
}
// Если ни один календарь не выбран, ТОЖЕ показываем все события (legacy режим)
// Если ни один календарь не выбран, показываем пустой результат
if (visibleCalendarIds.length === 0) {
console.log('[CalendarIntegration] No calendars selected, falling back to legacy mode');
return await getCalendarEvents({
start: startStr,
end: endStr
});
console.log('[CalendarIntegration] No calendars selected, showing empty');
return [];
}
// Загружаем события для каждого видимого календаря
const eventChunks = await Promise.all(
visibleCalendarIds.map(async (calendarId) => {
// Разделяем legacy и новые календари
const legacyIds = [...visibleCalendarIds].filter(id => typeof id === 'string' && id.startsWith('legacy-'));
const newIds = [...visibleCalendarIds].filter(id => typeof id === 'number');
console.log('[CalendarIntegration] Processing calendars:', { legacyIds, newIds });
let allEvents = [];
// Загружаем события для legacy календарей
for (const legacyId of legacyIds) {
try {
let events = [];
const calendar = calendars.find(c => c.id === legacyId);
if (legacyId === 'legacy-company') {
// Компания: все события без фильтров
console.log('[CalendarIntegration] Loading company events (all)');
events = await getCalendarEvents({ start: startStr, end: endStr });
} else if (legacyId === 'legacy-personal') {
// Личный: события текущего сотрудника
console.log('[CalendarIntegration] Loading personal events for current employee');
events = await getCalendarEvents({
start: startStr,
end: endStr,
employee_id: window.currentEmployeeId || null
});
} else if (legacyId.startsWith('legacy-dept-')) {
// Отдел: события конкретного отдела
const deptId = parseInt(legacyId.replace('legacy-dept-', ''), 10);
console.log(`[CalendarIntegration] Loading department events for dept ${deptId}`);
events = await getCalendarEvents({
start: startStr,
end: endStr,
department_id: deptId
});
}
// Добавляем информацию о legacy календаре
allEvents.push(...(events || []).map(event => ({
...event,
__calendar: calendar,
color: calendar?.color || event.color
})));
console.log(`[CalendarIntegration] Loaded ${events?.length || 0} events for legacy calendar ${legacyId}`);
} catch (error) {
console.error(`[CalendarIntegration] Error loading legacy calendar ${legacyId}:`, error);
}
}
// Загружаем события для новых календарей
const newEventChunks = await Promise.all(
newIds.map(async (calendarId) => {
try {
const events = await getCalendarEvents({
start: startStr,
@ -69,14 +107,13 @@ export function integrateCalendarManager(calendarWidgetInstance, options = {}) {
calendar_id: calendarId
});
console.log(`[CalendarIntegration] Loaded ${events?.length || 0} events for calendar ${calendarId}`);
console.log(`[CalendarIntegration] Loaded ${events?.length || 0} events for new calendar ${calendarId}`);
// Добавляем информацию о календаре к каждому событию
// Добавляем информацию о календаре
const calendar = calendars.find(c => c.id === calendarId);
return (events || []).map(event => ({
...event,
__calendar: calendar,
// Переопределяем цвет события цветом календаря
color: calendar?.color || event.color
}));
} catch (error) {
@ -86,8 +123,9 @@ export function integrateCalendarManager(calendarWidgetInstance, options = {}) {
})
);
// Объединяем все события
const allEvents = eventChunks.flat();
// Объединяем события из новых календарей
allEvents.push(...newEventChunks.flat());
console.log(`[CalendarIntegration] Total events loaded: ${allEvents.length}`);
// Дедупликация по ID

View File

@ -6,28 +6,7 @@
<div class="d-flex align-items-center gap-2 flex-shrink-1 min-w-0">
<h6 class="card-title fw-semibold mb-0">Календарь</h6>
{# Выпадающий выбор: Компания / Отделы / Личный (LEGACY) #}
<div class="dropdown">
<button
class="btn btn-sm btn-outline-secondary dropdown-toggle text-truncate"
type="button"
id="calendarChooserBtn"
data-bs-toggle="dropdown"
aria-expanded="false"
style="max-width: 150px;"
>
Компания
</button>
<ul class="dropdown-menu" aria-labelledby="calendarChooserBtn" id="calendarChooserMenu">
<li><button class="dropdown-item" type="button" data-cal="personal">Личный</button></li>
<li><hr class="dropdown-divider"></li>
<li><button class="dropdown-item" type="button" data-cal="company">Компания</button></li>
<li><hr class="dropdown-divider"></li>
{# пункты отделов добавим из JS по data-dept-id #}
</ul>
</div>
{# Кнопка списка календарей (NEW) #}
{# Кнопка списка календарей #}
<button
class="btn btn-sm btn-outline-primary"
type="button"
@ -35,9 +14,10 @@
data-bs-target="#calendarListCollapse"
aria-expanded="false"
aria-controls="calendarListCollapse"
title="Мои календари"
title="Список календарей"
>
<i class="bi-list-check"></i>
<span class="ms-1">Календари</span>
</button>
</div>
@ -65,16 +45,16 @@
</div>
</div>
{# Список календарей (сворачиваемый) - НОВАЯ ФУНКЦИЯ #}
{# Список календарей (сворачиваемый) - унифицированный список legacy + новые #}
<div class="collapse" id="calendarListCollapse">
<div class="card-body border-bottom py-2">
<small class="text-muted d-block mb-2">
<i class="bi-info-circle me-1"></i>
Управление несколькими календарями
Выберите календари для отображения
</small>
<div id="calendarListContainer" class="calendar-list-container" style="max-height: 200px;">
<div id="calendarListContainer" class="calendar-list-container" style="max-height: 250px; overflow-y: auto;">
{# Здесь будет JS-генерируемый список календарей #}
{# Здесь будет JS-генерируемый список календарей (legacy + новые) #}
<div class="text-center py-3">
<div class="spinner-border spinner-border-sm text-primary" role="status">
<span class="visually-hidden">Загрузка...</span>