const { useState, useEffect, useMemo } = React; const PER_PAGE = 5; const CAT_LABELS = { info: 'お知らせ', media: 'メディア掲載', press: 'プレスリリース', blog: '社内ブログ' }; const CAT_STYLES = { press: { fontSize: 11.5, fontWeight: 600, color: '#3331A4', background: 'rgba(51,49,164,.07)', padding: '3px 10px', borderRadius: 5 }, media: { fontSize: 11.5, fontWeight: 600, color: '#1a6b3a', background: 'rgba(26,107,58,.07)', padding: '3px 10px', borderRadius: 5 }, info: { fontSize: 11.5, fontWeight: 600, color: '#5E5E63', background: '#F7F6F4', padding: '3px 10px', borderRadius: 5 }, blog: { fontSize: 11.5, fontWeight: 600, color: '#9A6B00', background: 'rgba(154,107,0,.08)', padding: '3px 10px', borderRadius: 5 }, }; function formatNewsDate(dateStr) { const d = new Date(dateStr); return `${d.getFullYear()}.${String(d.getMonth() + 1).padStart(2, '0')}.${String(d.getDate()).padStart(2, '0')}`; } function stripTags(html) { return (html || '').replace(/<[^>]+>/g, '').trim(); } function FilterTabs({ filter, onChange }) { const options = [ { key: 'all', label: 'すべて' }, { key: 'info', label: 'お知らせ' }, { key: 'media', label: 'メディア掲載' }, { key: 'press', label: 'プレスリリース' }, { key: 'blog', label: '社内ブログ' }, ]; return (
{options.map((o) => { const active = filter === o.key; return ( ); })}
); } function NewsRow({ item }) { return (
{item.date} {item.tag}

{item.title}

); } function Pagination({ page, totalPages, onChange }) { if (totalPages <= 1) return null; const pages = Array.from({ length: totalPages }, (_, i) => i + 1); const btnStyle = (active, disabled) => ({ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', height: 38, minWidth: 38, padding: '0 14px', borderRadius: 8, fontSize: 14, fontWeight: active ? 700 : 500, background: active ? '#111111' : '#F7F6F4', color: disabled ? '#CBCAC6' : (active ? '#fff' : '#111111'), border: active ? 'none' : '1px solid #ECEAE6', cursor: disabled ? 'default' : 'pointer', fontFamily: 'inherit', }); return (
{pages.map((p) => ( ))}
); } function NewsApp() { const [posts, setPosts] = useState(null); const [filter, setFilter] = useState('all'); const [page, setPage] = useState(1); useEffect(() => { Promise.all([ fetch(`${WP_API}/wp/v2/news?per_page=100&orderby=date&order=desc&_fields=id,date,link,title,news_category`) .then((r) => (r.ok ? r.json() : [])), fetch(`${WP_API}/wp/v2/news_category?per_page=100&_fields=id,slug,name`) .then((r) => (r.ok ? r.json() : [])), ]) .then(([data, terms]) => { const termById = {}; (terms || []).forEach((t) => { termById[t.id] = t; }); const mapped = (data || []).map((item) => { const catTerm = termById[(item.news_category || [])[0]]; return { id: item.id, date: formatNewsDate(item.date), cat: catTerm ? catTerm.slug : 'info', tag: catTerm ? catTerm.name : 'お知らせ', title: stripTags(item.title.rendered), link: item.link, }; }); setPosts(mapped); }) .catch(() => setPosts([])); }, []); const filtered = useMemo(() => { if (!posts) return []; return filter === 'all' ? posts : posts.filter((p) => p.cat === filter); }, [posts, filter]); const totalPages = Math.max(1, Math.ceil(filtered.length / PER_PAGE)); const currentPage = Math.min(page, totalPages); const pageItems = filtered.slice((currentPage - 1) * PER_PAGE, currentPage * PER_PAGE); const handleFilterChange = (key) => { setFilter(key); setPage(1); }; if (posts === null) { return
読み込み中…
; } return ( <>
{pageItems.length === 0 ? (
該当するお知らせはありません。
) : ( pageItems.map((item) => ) )}
); } ReactDOM.createRoot(document.getElementById('news-app')).render();