// ─── Blog section (LP埋め込み & REST API取得) ─────────────────────
function BlogSection() {
const [posts, setPosts] = React.useState([]);
const [categories, setCategories] = React.useState([]);
const [filter, setFilter] = React.useState(0); // 0 = all
const [loading, setLoading] = React.useState(true);
const [error, setError] = React.useState(false);
React.useEffect(() => {
const base = "/wp-json/wp/v2";
Promise.all([
fetch(`${base}/posts?per_page=8&orderby=date&order=desc&_embed=true`).then(r => r.json()),
fetch(`${base}/categories?hide_empty=true&per_page=100&orderby=count&order=desc`).then(r => r.json()),
])
.then(([p, c]) => {
setPosts(Array.isArray(p) ? p : []);
setCategories(Array.isArray(c) ? c.filter(cat => cat.slug !== "uncategorized") : []);
setLoading(false);
})
.catch(() => { setError(true); setLoading(false); });
}, []);
const filtered = filter === 0 ? posts : posts.filter(p =>
p.categories && p.categories.includes(filter)
);
return (
{categories.length > 0 && (
{categories.map(cat => (
))}
)}
{loading && (
LOADING...
)}
{error && (
記事の取得に失敗しました。
)}
{!loading && !error && filtered.length === 0 && (
該当する記事がありません。
)}
{!loading && !error && filtered.length > 0 && (
{filtered.map((post, i) => (
))}
)}
);
}
function BlogCard({ post, index }) {
const thumb = post._embedded?.["wp:featuredmedia"]?.[0]?.source_url;
const cat = post._embedded?.["wp:term"]?.[0]?.[0];
const dateStr = new Date(post.date).toLocaleDateString("ja-JP", {
year: "numeric", month: "long", day: "numeric"
});
const rawExcerpt = post.excerpt?.rendered?.replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim();
const excerpt = rawExcerpt && rawExcerpt.length > 72 ? rawExcerpt.slice(0, 72) + "…" : rawExcerpt;
const hue = (post.id * 47 + index * 19) % 360;
return (
{ window.location.href = post.link; }}>
{thumb
?

]+>/g, "") || ""} className="blog-card__thumb-img" loading="lazy" />
:
}
{cat &&
{cat.name}}
{dateStr}
{excerpt &&
{excerpt}
}
);
}
// ── Blog archive page ───────────────────────────────────────────
const BLOG_PER_PAGE = 12;
function BlogPage({ go }) {
const [posts, setPosts] = React.useState([]);
const [categories, setCategories] = React.useState([]);
const [filter, setFilter] = React.useState(0);
const [page, setPage] = React.useState(1);
const [totalPages, setTotalPages] = React.useState(1);
const [loading, setLoading] = React.useState(true);
const [error, setError] = React.useState(false);
React.useEffect(() => {
fetch("/wp-json/wp/v2/categories?hide_empty=true&per_page=100&orderby=count&order=desc")
.then(r => r.json())
.then(c => setCategories(Array.isArray(c) ? c.filter(cat => cat.slug !== "uncategorized") : []))
.catch(() => {});
}, []);
React.useEffect(() => {
setLoading(true);
setError(false);
const catParam = filter !== 0 ? `&categories=${filter}` : "";
fetch(`/wp-json/wp/v2/posts?per_page=${BLOG_PER_PAGE}&page=${page}&orderby=date&order=desc&_embed=true${catParam}`)
.then(r => {
setTotalPages(parseInt(r.headers.get("X-WP-TotalPages") || "1", 10));
return r.json();
})
.then(p => { setPosts(Array.isArray(p) ? p : []); setLoading(false); })
.catch(() => { setError(true); setLoading(false); });
}, [filter, page]);
const changeFilter = (catId) => { setFilter(catId); setPage(1); };
const changePage = (p) => { setPage(p); window.scrollTo({ top: 0, behavior: "smooth" }); };
return (
Web制作・保守の知識。>}
lead="WordPress保守・セキュリティ・SEO対策・Web制作に関する知識・事例を発信しています。"
/>
{categories.length > 0 && (
{categories.map(cat => (
))}
)}
{loading &&
LOADING...
}
{error &&
記事の取得に失敗しました。
}
{!loading && !error && posts.length === 0 &&
該当する記事がありません。
}
{!loading && !error && posts.length > 0 && (
{posts.map((post, i) => )}
)}
{totalPages > 1 && (
{page > 1 && (
)}
{Array.from({ length: totalPages }, (_, i) => i + 1).map(p => (
))}
{page < totalPages && (
)}
)}
);
}
Object.assign(window, { BlogSection, BlogCard, BlogPage });