first commit
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
---
|
||||
/**
|
||||
* 404 Page
|
||||
*/
|
||||
|
||||
import Base from "../layouts/Base.astro";
|
||||
---
|
||||
|
||||
<Base title="Page Not Found">
|
||||
<div class="container">
|
||||
<div class="content-width" style="text-align: center; padding: 4rem 0;">
|
||||
<h1>404</h1>
|
||||
<p class="text-muted">The page you're looking for doesn't exist.</p>
|
||||
<p><a href="/">Go home</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</Base>
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
/**
|
||||
* Category Archive
|
||||
*
|
||||
* Demonstrates:
|
||||
* - getTerm for fetching taxonomy term details
|
||||
* - getEntriesByTerm for entries with a specific term
|
||||
*/
|
||||
|
||||
import { getTerm, getEntriesByTerm } from "emdash";
|
||||
import Base from "../../layouts/Base.astro";
|
||||
import PostCard from "../../components/PostCard.astro";
|
||||
|
||||
const { slug } = Astro.params;
|
||||
|
||||
const category = await getTerm("categories", slug!);
|
||||
const posts = await getEntriesByTerm("posts", "categories", slug!);
|
||||
|
||||
if (!category) {
|
||||
return Astro.redirect("/404");
|
||||
}
|
||||
---
|
||||
|
||||
<Base title={category.label}>
|
||||
<div class="container">
|
||||
<div class="content-width">
|
||||
<header class="archive-header">
|
||||
<h1>{category.label}</h1>
|
||||
</header>
|
||||
|
||||
{
|
||||
posts.length > 0 ? (
|
||||
<div class="posts-list">
|
||||
{posts.map((post) => (
|
||||
<PostCard
|
||||
title={post.data.title}
|
||||
href={`/posts/${post.data.slug || post.id}`}
|
||||
date={post.data.published_at}
|
||||
excerpt={post.data.excerpt}
|
||||
featuredImage={
|
||||
post.data.featured_image?.src
|
||||
? {
|
||||
src: post.data.featured_image.src,
|
||||
alt: post.data.featured_image.alt || post.data.title,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p class="text-muted">No posts in this category.</p>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</Base>
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
/**
|
||||
* Homepage / Blog Index
|
||||
*
|
||||
* Demonstrates:
|
||||
* - getEmDashCollection for listing entries
|
||||
* - Passing image fields correctly to components
|
||||
*/
|
||||
|
||||
import { getEmDashCollection } from "emdash";
|
||||
import Base from "../layouts/Base.astro";
|
||||
import PostCard from "../components/PostCard.astro";
|
||||
|
||||
const { entries: posts } = await getEmDashCollection("posts", {
|
||||
status: "published",
|
||||
limit: 10,
|
||||
});
|
||||
---
|
||||
|
||||
<Base>
|
||||
<div class="container">
|
||||
<div class="content-width">
|
||||
{
|
||||
posts.length > 0 ? (
|
||||
<div class="posts-list">
|
||||
{posts.map((post) => (
|
||||
<PostCard
|
||||
title={post.data.title}
|
||||
href={`/posts/${post.data.slug || post.id}`}
|
||||
date={post.data.published_at}
|
||||
excerpt={post.data.excerpt}
|
||||
{
|
||||
/*
|
||||
IMPORTANT: featured_image is { src, alt }, not a string!
|
||||
Pass the whole object, or extract src/alt explicitly.
|
||||
*/ }
|
||||
featuredImage={
|
||||
post.data.featured_image?.src
|
||||
? {
|
||||
src: post.data.featured_image.src,
|
||||
alt: post.data.featured_image.alt || post.data.title,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p class="text-muted">No posts yet.</p>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</Base>
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
/**
|
||||
* Single Page
|
||||
*/
|
||||
|
||||
import { getEmDashEntry } from "emdash";
|
||||
import { PortableText } from "emdash/ui";
|
||||
import Base from "../../layouts/Base.astro";
|
||||
|
||||
const { slug } = Astro.params;
|
||||
|
||||
const { entry: page, error } = await getEmDashEntry("pages", slug!);
|
||||
|
||||
if (error) {
|
||||
return new Response("Server error", { status: 500 });
|
||||
}
|
||||
|
||||
if (!page) {
|
||||
return Astro.redirect("/404");
|
||||
}
|
||||
---
|
||||
|
||||
<Base title={page.data.title}>
|
||||
<article class="container">
|
||||
<div class="content-width">
|
||||
<header class="page-header">
|
||||
<h1>{page.data.title}</h1>
|
||||
</header>
|
||||
|
||||
<div class="prose">
|
||||
{page.data.content && <PortableText value={page.data.content} />}
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</Base>
|
||||
@@ -0,0 +1,99 @@
|
||||
---
|
||||
/**
|
||||
* Single Post
|
||||
*
|
||||
* Demonstrates:
|
||||
* - getEmDashEntry for fetching a single entry
|
||||
* - getEntryTerms for taxonomy terms (NO db parameter!)
|
||||
* - PortableText component for rich content
|
||||
* - Proper image field access
|
||||
*/
|
||||
|
||||
import { getEmDashEntry, getEntryTerms } from "emdash";
|
||||
import { PortableText } from "emdash/ui";
|
||||
import Base from "../../layouts/Base.astro";
|
||||
|
||||
const { slug } = Astro.params;
|
||||
|
||||
const { entry: post, error } = await getEmDashEntry("posts", slug!);
|
||||
|
||||
if (error) {
|
||||
return new Response("Server error", { status: 500 });
|
||||
}
|
||||
|
||||
if (!post) {
|
||||
return Astro.redirect("/404");
|
||||
}
|
||||
|
||||
// Get taxonomy terms - NOTE: no db parameter!
|
||||
const categories = await getEntryTerms("posts", post.id, "categories");
|
||||
const tags = await getEntryTerms("posts", post.id, "tags");
|
||||
|
||||
const formattedDate = post.data.published_at
|
||||
? new Date(post.data.published_at).toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})
|
||||
: null;
|
||||
---
|
||||
|
||||
<Base title={post.data.title} description={post.data.excerpt}>
|
||||
<article class="container">
|
||||
<div class="content-width">
|
||||
<header class="post-header">
|
||||
<h1>{post.data.title}</h1>
|
||||
|
||||
<div class="post-meta">
|
||||
{
|
||||
formattedDate && (
|
||||
<time datetime={post.data.published_at}>{formattedDate}</time>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
categories.length > 0 && (
|
||||
<span class="post-categories">
|
||||
in{" "}
|
||||
{categories.map((cat, i) => (
|
||||
<>
|
||||
{i > 0 && ", "}
|
||||
<a href={`/categories/${cat.slug}`}>{cat.label}</a>
|
||||
</>
|
||||
))}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* IMPORTANT: Check .src, not just the field */}
|
||||
{
|
||||
post.data.featured_image?.src && (
|
||||
<div class="post-featured-image">
|
||||
<img
|
||||
src={post.data.featured_image.src}
|
||||
alt={post.data.featured_image.alt || post.data.title}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
<div class="prose">
|
||||
{post.data.content && <PortableText value={post.data.content} />}
|
||||
</div>
|
||||
|
||||
{
|
||||
tags.length > 0 && (
|
||||
<div class="post-tags">
|
||||
{tags.map((tag) => (
|
||||
<a href={`/tags/${tag.slug}`} class="tag">
|
||||
{tag.label}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</article>
|
||||
</Base>
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
/**
|
||||
* Posts Archive
|
||||
*/
|
||||
|
||||
import { getEmDashCollection } from "emdash";
|
||||
import Base from "../../layouts/Base.astro";
|
||||
import PostCard from "../../components/PostCard.astro";
|
||||
|
||||
const { entries: posts } = await getEmDashCollection("posts", {
|
||||
status: "published",
|
||||
});
|
||||
---
|
||||
|
||||
<Base title="Blog">
|
||||
<div class="container">
|
||||
<div class="content-width">
|
||||
<header class="archive-header">
|
||||
<h1>Blog</h1>
|
||||
</header>
|
||||
|
||||
{
|
||||
posts.length > 0 ? (
|
||||
<div class="posts-list">
|
||||
{posts.map((post) => (
|
||||
<PostCard
|
||||
title={post.data.title}
|
||||
href={`/posts/${post.data.slug || post.id}`}
|
||||
date={post.data.published_at}
|
||||
excerpt={post.data.excerpt}
|
||||
featuredImage={
|
||||
post.data.featured_image?.src
|
||||
? {
|
||||
src: post.data.featured_image.src,
|
||||
alt: post.data.featured_image.alt || post.data.title,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p class="text-muted">No posts found.</p>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</Base>
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
/**
|
||||
* Tag Archive
|
||||
*/
|
||||
|
||||
import { getTerm, getEntriesByTerm } from "emdash";
|
||||
import Base from "../../layouts/Base.astro";
|
||||
import PostCard from "../../components/PostCard.astro";
|
||||
|
||||
const { slug } = Astro.params;
|
||||
|
||||
const tag = await getTerm("tags", slug!);
|
||||
const posts = await getEntriesByTerm("posts", "tags", slug!);
|
||||
|
||||
if (!tag) {
|
||||
return Astro.redirect("/404");
|
||||
}
|
||||
---
|
||||
|
||||
<Base title={`Tagged: ${tag.label}`}>
|
||||
<div class="container">
|
||||
<div class="content-width">
|
||||
<header class="archive-header">
|
||||
<p class="text-muted">Tagged</p>
|
||||
<h1>{tag.label}</h1>
|
||||
</header>
|
||||
|
||||
{
|
||||
posts.length > 0 ? (
|
||||
<div class="posts-list">
|
||||
{posts.map((post) => (
|
||||
<PostCard
|
||||
title={post.data.title}
|
||||
href={`/posts/${post.data.slug || post.id}`}
|
||||
date={post.data.published_at}
|
||||
excerpt={post.data.excerpt}
|
||||
featuredImage={
|
||||
post.data.featured_image?.src
|
||||
? {
|
||||
src: post.data.featured_image.src,
|
||||
alt: post.data.featured_image.alt || post.data.title,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p class="text-muted">No posts with this tag.</p>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</Base>
|
||||
Reference in New Issue
Block a user