// month.jsx — неделя 3, блоки главной и профиля:
// MonthSummary — «Итоги месяца» (CJM 3.1): за прошлый календарный месяц по МСК — материалы, эфиры,
// записи, кейсы, баллы и место в рейтинге месяца. На главной — первые 7 дней нового
// месяца, карточку можно скрыть; в профиле — всегда, с листанием месяцев.
// Правка 05.10.2026: пока у участника нет ни одного прошлого месяца с баллами,
// на главной вместо итогов — велкам (текст из «Команда» → «Тексты»), скрывается крестиком.
// People — «С кем пообщаться» (онбординг 2.5): до 3 участников по цели, сфере, городу;
// «Показать других» — следующая тройка. В профиле — переключатель показа себя.
// Считает и подбирает сервер (/api/summary, /api/people), здесь только рисуем.
const sumNoun = { materials: ['материал', 'материала', 'материалов'], events: ['эфир', 'эфира', 'эфиров'],
recordings: ['запись', 'записи', 'записей'], cases: ['кейс', 'кейса', 'кейсов'] };
// глагол согласуем с числом: 1 материал дочитан, 2 материала дочитано
const SUM_TILES = [['materials', 'дочитан', 'дочитано'], ['events', 'посещён', 'посещено'], ['recordings', 'просмотрена', 'просмотрено'], ['cases', 'опубликован', 'опубликовано']];
const verbOf = (n, one, many) => (Math.abs(n) % 10 === 1 && Math.abs(n) % 100 !== 11 ? one : many);
function SummaryBody({ s, nav }) {
const mon = MONTH_PREP[monthIdx(s.period)];
if (s.empty) return (
В {mon} ты только присматривался к сообществу, и это нормально. Начни с одного материала по цели или приходи на ближайший эфир: в следующих итогах будет что посчитать.
);
const place = s.team ? 'Команда вне рейтинга' : s.place ? <>{s.place} место из {s.ranked} в рейтинге {MONTH_GEN[monthIdx(s.period)]}> : null;
return (
<>
{fmtNum(s.stats.points)} {ballov(s.stats.points)} за {MONTH_NOM[monthIdx(s.period)]}
{place && }
);
}
function People({ nav, profile = false }) {
const [page, setPage] = React.useState(0);
const [d, reload] = useApi('/api/people?page=' + page);
const [vis, setVis] = React.useState(null);
React.useEffect(() => { if (d) setVis(d.visible); }, [d]);
if (!d) return null;
if (!profile && (!d.has_profile || !d.people.length)) return null;
const toggle = async (v) => {
setVis(v);
const r = await putJSON('/api/prefs/match', { visible: v });
if (!r.ok) setVis(!v); else toast(v ? 'Тебя снова видно в рекомендациях' : 'Тебя больше не показываем в рекомендациях');
};
const more = d.pages > 1 && (
);
const grid = d.people.length
?
{d.people.map(p => )}
:
{d.has_profile ? 'Пока не нашли участников с похожей целью, сферой или городом. Загляни через неделю.' : 'Заполни анкету: по цели, сфере и городу подберём, с кем пообщаться.'}
;
if (!profile) return (
С кем пообщаться
{more}
Участники с похожей целью, сферой или из твоего города. Подборка обновляется по понедельникам.
{grid}
);
return (
С кем пообщаться
{more}
{grid}
);
}
Object.assign(window, { MonthSummary, People });