// News list + article + fundraising campaigns function NewsPage() { const { go } = CTSCShell.useNav(); const { data } = CTSCStore.useStore(); const { fmtDate } = CTSCStore; const { Photo } = CTSCShell; const [cat, setCat] = React.useState('All'); const approved = [...data.news].filter(n => n.status === 'Approved').sort((a, b) => b.date.localeCompare(a.date)); const cats = ['All', ...Array.from(new Set(approved.map(n => n.category)))]; const list = approved.filter(n => cat === 'All' || n.category === cat); const [feat, ...rest] = list; return (
{cats.map(c => )}
{feat && ( )}
{rest.map(n => ( ))}
); } function ArticleDetail({ id }) { const { go } = CTSCShell.useNav(); const { data } = CTSCStore.useStore(); const { fmtDate, fedName } = CTSCStore; const { Photo } = CTSCShell; const n = data.news.find(x => x.id === id); if (!n) return ; return (
{n.category} · {fmtDate(n.date)} · {fedName(data, n.fedId)}

{n.title}

{n.excerpt}

{n.body}

); } function FundraisingPage() { const { go } = CTSCShell.useNav(); const { data } = CTSCStore.useStore(); const { fmtMoney, fmtDate, fedName } = CTSCStore; const list = data.fundraising.filter(c => c.status === 'Approved'); return (
{list.map(c => { const pct = Math.min(100, Math.round(c.raised / c.goal * 100)); return (

{c.title}

{c.desc}

{fmtMoney(c.raised)} of {fmtMoney(c.goal)} · {pct}%
Closes {fmtDate(c.deadline)}
); })}
); } window.NewsPage = NewsPage; window.ArticleDetail = ArticleDetail; window.FundraisingPage = FundraisingPage;