// 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 && (
Этот эфир — для участников PRO: живой разбор в небольшой группе. Запись появится в базе знаний.{' '} { ev.preventDefault(); track('pro:from_event:' + e.id); nav ? nav('tariff') : (location.href = '/os/?view=tariff'); }}>Перейти на PRO →
)}
{!e.past && (
{e.has_link && !closed && e.join_open && } {!closed && !e.join_open && Ссылка на эфир появится здесь за 15 минут до начала} track('ics:' + e.id)}>{Icons.cal()} В календарь
)}
); } 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 });