// home.jsx — Главная участника: приветствие и тариф, «Продолжить» по цели из анкеты, // ближайшие события, разделы базы. Не участнику вместо неё — экран «Доступ по подписке» // (paywall.jsx), незалогиненному — вход (login.jsx). Выбор делает app.jsx. const useFetch = (url, deps = []) => { const [data, setData] = React.useState(null); React.useEffect(()=>{ let alive = true; fetch(url, { credentials:'same-origin' }).then(r => r.json()).then(j => alive && setData(j)).catch(() => alive && setData({ ok: false })); return () => { alive = false; }; }, deps); return data; }; window.useFetch = useFetch; const EV_KIND_LABEL = { webinar:'Вебинар', qa:'Q&A', mastermind:'Мастермайнд', offline:'Офлайн' }; function EventsList({ events }) { const tier = ME().member.tier; if (!events) return
Загружаем расписание…
; if (!events.length) return
Ближайших событий пока нет — расписание месяца появится здесь.
; return (
{events.slice(0, 4).map((e, i) => { const closed = e.access === 'pro' && tier !== 'pro'; return (
{fmtDay(e.date)}
{e.time} МСК
{EV_KIND_LABEL[e.kind] || 'Событие'} {e.access === 'pro' && }
{e.title}
{[e.host, e.note].filter(Boolean).join(' · ')}{closed ? ' · в тарифе PRO' : ''}
); })}
); } window.EventsList = EventsList; function Home({ nav }) { const { user, member, profile } = ME(); const prof = useFetch('/api/profile'); const ev = useFetch('/api/events'); const kb = useKB(); const goal = profile && ((CFG().goals || []).find(g => g.id === profile.goal)); const goalTitle = profile ? (profile.goal === 'other' ? profile.goal_custom : goal && goal.title) : null; const rec = (prof && prof.recommended) || []; const next = rec.find(r => !r.locked) || rec[0]; const today = new Date().toLocaleDateString('ru-RU', { weekday:'long', day:'numeric', month:'long' }); const clusters = (kb && kb.clusters) || CFG().clusters || []; const cnt = (id) => ((kb && kb.items) || []).filter(i => i.cluster === id).length; return (
{today}

{firstName(user.name) ? `Привет, ${firstName(user.name)}.` : 'Привет.'}

Вы в сообществе хакку · тариф{' '} {TIER_LABEL[member.tier]} {member.paid_until && !member.admin ? `, оплачено до ${fmtDate(member.paid_until).replace(/\.$/, '')}` : ''}.

{/* «Продолжить» по цели из анкеты */}
{goalTitle ? `Ваша цель · ${goalTitle}` : 'С чего начать'}
{next ? ( <>

{next.title}

{next.summary}

) : ( <>

Расскажите, зачем вы здесь.

Две минуты на анкету — и подберём первые материалы под вашу цель.

)}
{rec.length > 1 && (

Подобрали под вашу цель

{ e.preventDefault(); nav('kb'); }}>Вся база →
{rec.map(r => )}
)}

Ближайшие события

{/* TODO: календарь месяца и ссылки на подключение — после синка с расписанием */}
Сообщество

Чат участников, Random Coffee и записи эфиров — в сообществе hakku.

hakku.ai/community ↗
); } window.Home = Home;