// schedule.jsx — «Расписание» (?view=schedule, план Насти 2.5 «Планирование»): события текущего и
// следующего месяца по неделям, время МСК, PRO-замок, «Войти в эфир» в окне эфира и
// «Добавить в календарь» (.ics с сервера: /api/events/:id/ics, без ссылки на эфир — только страница
// события здесь, ?view=schedule&id=). Данные — /api/schedule.
// «5–11 октября»; неделя из одного дня — «1 октября»
const rangeText = (from, to) => (from === to ? fmtDay(from) : `${Number(from.slice(8, 10))}–${fmtDay(to)}`);
const weekday = (iso) => new Date(iso + 'T00:00:00').toLocaleDateString('ru-RU', { weekday: 'short' });
function SchedEvent({ e, attend, focus, nav }) {
const tier = accessTier();
const closed = e.access === 'pro' && tier !== 'pro';
const ref = React.useRef(null);
React.useEffect(() => { if (focus && ref.current) setTimeout(() => ref.current.scrollIntoView({ block: 'center' }), 120); }, [focus]);
return (
{fmtDay(e.date).replace(/^(\d+ [а-я]{3}).*$/, '$1')}
{weekday(e.date)} · {e.time} МСК
{EV_KIND_LABEL[e.kind] || 'Событие'}
{e.access === 'pro' && }
{e.past && прошло}
{e.title}
{[e.host, e.note].filter(Boolean).join(' · ')}{!e.past && (closed ? ' · в тарифе PRO' : ` · +${attend} ${ballov(attend)} за эфир`)}
{/* правка Насти 23.10: анонс в TG ведёт сюда (?view=schedule&id=…); участнику Enthusiast — мягко, без «доступ запрещён» */}
{closed && !e.past && (
)}
{!e.past && (
)}
);
}
function Schedule({ nav, params = {} }) {
const [d] = useApi('/api/schedule');
const total = d ? d.months.reduce((n, m) => n + m.weeks.reduce((k, w) => k + w.events.length, 0), 0) : 0;
return (
Расписание
Расписание на месяц вперёд
Эфиры, Q&A и встречи этого и следующего месяца. Время московское. «В календарь» скачивает событие
для Google, Apple или Outlook, ссылка на эфир появится здесь за 15 минут до начала.
{d === undefined &&
Загружаем расписание…
}
{d === null &&
Расписание не загрузилось. Обнови страницу через минуту.
}
{d && !total &&
Событий на ближайшие два месяца пока нет. Как только появятся, они будут здесь.
}
{d && total > 0 && d.months.map(m => (
{m.title}
{m.weeks.length ? m.weeks.map(w => (
{rangeText(w.from, w.to)}{w.current && эта неделя}
{w.events.map((e, i) => )}
)) : В {MONTH_PREP[monthIdx(m.period)]} событий пока нет.
}
))}
);
}
Object.assign(window, { Schedule });