feat: blog pubblico con filtri, paginazione e articolo
This commit is contained in:
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
import { getDb } from '../../lib/db';
|
||||||
|
import { listRecent, listArchiveMonths } from '../../lib/posts';
|
||||||
|
import { CATEGORIES } from '../../lib/blog-const';
|
||||||
|
const db = getDb();
|
||||||
|
const recent = listRecent(db, 4);
|
||||||
|
const months = listArchiveMonths(db);
|
||||||
|
const monthLabel = (m: string) => new Intl.DateTimeFormat('it-IT', { month: 'long', year: 'numeric' }).format(new Date(`${m}-01`));
|
||||||
|
---
|
||||||
|
<aside class="bside">
|
||||||
|
<h3>Categorie</h3>
|
||||||
|
{CATEGORIES.map((c) => <p><a href={`/blog?categoria=${encodeURIComponent(c)}`}>{c}</a></p>)}
|
||||||
|
<h3>Ultimi articoli</h3>
|
||||||
|
{recent.map((p) => (
|
||||||
|
<p><a href={`/blog/${p.slug}`}>{p.title}</a></p>
|
||||||
|
))}
|
||||||
|
<h3>Archivio</h3>
|
||||||
|
{months.map((m) => <p class="bside__month">{monthLabel(m.month)} ({m.count})</p>)}
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.bside h3 { font-size: .85rem; text-transform: uppercase; letter-spacing: .16em; margin-top: 30px; }
|
||||||
|
.bside a { text-decoration: none; color: var(--c-text); }
|
||||||
|
.bside a:hover { color: var(--c-accent-dark); }
|
||||||
|
.bside__month { text-transform: capitalize; }
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export const CATEGORIES = [
|
||||||
|
'Articoli tecnici & scientifici',
|
||||||
|
'Contenuti educativi',
|
||||||
|
'LinkedIn / Insight',
|
||||||
|
'Eventi & Presidi',
|
||||||
|
] as const;
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
import Base from '../../layouts/Base.astro';
|
||||||
|
import Sidebar from '../../components/blog/Sidebar.astro';
|
||||||
|
import { getDb } from '../../lib/db';
|
||||||
|
import { getPublishedBySlug } from '../../lib/posts';
|
||||||
|
export const prerender = false;
|
||||||
|
|
||||||
|
const post = getPublishedBySlug(getDb(), Astro.params.slug!);
|
||||||
|
if (!post) return Astro.redirect('/404');
|
||||||
|
const fmt = new Intl.DateTimeFormat('it-IT', { dateStyle: 'long' });
|
||||||
|
---
|
||||||
|
<Base title={post.title} description={post.excerpt}>
|
||||||
|
<section class="section">
|
||||||
|
<div class="container art">
|
||||||
|
<article>
|
||||||
|
{post.cover && <img class="art__cover" src={post.cover} alt="" />}
|
||||||
|
<p class="art__meta">{post.published_at && fmt.format(new Date(post.published_at))} · {post.category}</p>
|
||||||
|
<h1>{post.title}</h1>
|
||||||
|
<div class="art__body" set:html={post.body_html} />
|
||||||
|
</article>
|
||||||
|
<Sidebar />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</Base>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.art { display: grid; grid-template-columns: 2.4fr 1fr; gap: 60px; }
|
||||||
|
.art__cover { width: 100%; margin-bottom: 24px; }
|
||||||
|
.art__meta { font-size: .8rem; color: var(--c-text-light); }
|
||||||
|
.art__body :global(img) { margin-block: 20px; }
|
||||||
|
.art__body :global(blockquote) { border-left: 3px solid var(--c-accent); margin: 24px 0; padding: 6px 0 6px 20px; font-style: italic; color: var(--c-heading); }
|
||||||
|
@media (max-width: 991px) { .art { grid-template-columns: 1fr; } }
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
---
|
||||||
|
import Base from '../../layouts/Base.astro';
|
||||||
|
import PageHero from '../../components/PageHero.astro';
|
||||||
|
import { getDb } from '../../lib/db';
|
||||||
|
import { listPublished } from '../../lib/posts';
|
||||||
|
import { CATEGORIES } from '../../lib/blog-const';
|
||||||
|
export const prerender = false;
|
||||||
|
|
||||||
|
const PER_PAGE = 9;
|
||||||
|
const url = Astro.url;
|
||||||
|
const category = url.searchParams.get('categoria') ?? undefined;
|
||||||
|
const page = Math.max(1, Number(url.searchParams.get('pagina') ?? '1') || 1);
|
||||||
|
const { items, total } = listPublished(getDb(), {
|
||||||
|
category: category && (CATEGORIES as readonly string[]).includes(category) ? category : undefined,
|
||||||
|
page, perPage: PER_PAGE,
|
||||||
|
});
|
||||||
|
const pages = Math.max(1, Math.ceil(total / PER_PAGE));
|
||||||
|
const fmt = (d: string | null) => d ? new Intl.DateTimeFormat('it-IT', { dateStyle: 'long' }).format(new Date(d)) : '';
|
||||||
|
const linkFor = (cat?: string, p = 1) => {
|
||||||
|
const q = new URLSearchParams();
|
||||||
|
if (cat) q.set('categoria', cat);
|
||||||
|
if (p > 1) q.set('pagina', String(p));
|
||||||
|
const s = q.toString();
|
||||||
|
return `/blog${s ? `?${s}` : ''}`;
|
||||||
|
};
|
||||||
|
---
|
||||||
|
<Base title="Blog & Edugo" description="Edugo è lo spazio di InsanityLab dedicato alla conoscenza: articoli, guide e approfondimenti su allenamento, nutrizione e salute.">
|
||||||
|
<PageHero title="Blog & Edugo" />
|
||||||
|
<section class="section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-heading">
|
||||||
|
<h2>Blog & Edugo</h2>
|
||||||
|
<p>Edugo è lo spazio di InsanityLab dedicato alla conoscenza: articoli, guide e approfondimenti su allenamento, nutrizione e salute per capire il corpo e migliorare il proprio benessere. Perché la conoscenza è il primo passo del cambiamento.</p>
|
||||||
|
</div>
|
||||||
|
<nav class="bfilters" aria-label="categorie">
|
||||||
|
<a class:list={['bfilters__item', { 'is-active': !category }]} href={linkFor()}>Mostra tutto</a>
|
||||||
|
{CATEGORIES.map((c) => (
|
||||||
|
<a class:list={['bfilters__item', { 'is-active': category === c }]} href={linkFor(c)}>{c}</a>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
{items.length === 0 ? (
|
||||||
|
<p style="text-align:center">Nessun articolo pubblicato{category ? ' in questa categoria' : ''}, torna a trovarci presto.</p>
|
||||||
|
) : (
|
||||||
|
<div class="bgrid">
|
||||||
|
{items.map((post) => (
|
||||||
|
<article class="bcard">
|
||||||
|
{post.cover && <a href={`/blog/${post.slug}`}><img src={post.cover} alt="" loading="lazy" /></a>}
|
||||||
|
<p class="bcard__date">{fmt(post.published_at)}</p>
|
||||||
|
<h3><a href={`/blog/${post.slug}`}>{post.title}</a></h3>
|
||||||
|
<p>{post.excerpt}</p>
|
||||||
|
<a class="bcard__more" href={`/blog/${post.slug}`}>Per saperne di più →</a>
|
||||||
|
</article>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{pages > 1 && (
|
||||||
|
<nav class="bpages" aria-label="paginazione">
|
||||||
|
{Array.from({ length: pages }, (_, i) => (
|
||||||
|
<a class:list={['bpages__n', { 'is-active': page === i + 1 }]} href={linkFor(category, i + 1)}>{i + 1}</a>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</Base>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.bfilters { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; margin-bottom: 50px; }
|
||||||
|
.bfilters__item { font-family: var(--font-heading); font-size: .7rem; letter-spacing: .16em; text-transform: uppercase; text-decoration: none; color: var(--c-text); padding: 10px 16px; }
|
||||||
|
.bfilters__item.is-active { background: var(--c-accent); color: #fff; }
|
||||||
|
.bgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 50px 40px; }
|
||||||
|
.bcard img { aspect-ratio: 4/3; object-fit: cover; width: 100%; margin-bottom: 14px; }
|
||||||
|
.bcard__date { font-size: .78rem; color: var(--c-text-light); margin-bottom: 4px; }
|
||||||
|
.bcard h3 a { text-decoration: none; }
|
||||||
|
.bcard__more { font-family: var(--font-heading); font-size: .7rem; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; text-decoration: none; color: var(--c-heading); }
|
||||||
|
.bpages { display: flex; gap: 8px; justify-content: center; margin-top: 50px; }
|
||||||
|
.bpages__n { font-family: var(--font-heading); text-decoration: none; padding: 8px 14px; color: var(--c-text); }
|
||||||
|
.bpages__n.is-active { background: var(--c-dark); color: #fff; }
|
||||||
|
@media (max-width: 991px) { .bgrid { grid-template-columns: 1fr 1fr; } }
|
||||||
|
@media (max-width: 600px) { .bgrid { grid-template-columns: 1fr; } }
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user