142 lines
3.3 KiB
Plaintext
142 lines
3.3 KiB
Plaintext
---
|
|
export const prerender = false;
|
|
|
|
import { getEmDashCollection } from "emdash";
|
|
import Base from "../layouts/Base.astro";
|
|
import PostCard from "../components/PostCard.astro";
|
|
import { getReadingTime, extractText } from "../utils/reading-time";
|
|
|
|
const query = Astro.url.searchParams.get("q")?.trim() || "";
|
|
|
|
const { entries: allPosts } = await getEmDashCollection("posts");
|
|
|
|
// Simple search: match query against title, excerpt, and content
|
|
function matchesQuery(post: (typeof allPosts)[0], q: string): boolean {
|
|
if (!q) return false;
|
|
const lower = q.toLowerCase();
|
|
const title = (post.data.title || "").toLowerCase();
|
|
const excerpt = (post.data.excerpt || "").toLowerCase();
|
|
// Extract plain text from portable text blocks (avoids matching on _type, _key, etc.)
|
|
const content = extractText(post.data.content).toLowerCase();
|
|
return (
|
|
title.includes(lower) || excerpt.includes(lower) || content.includes(lower)
|
|
);
|
|
}
|
|
|
|
const results = query ? allPosts.filter((p) => matchesQuery(p, query)) : [];
|
|
---
|
|
|
|
<Base
|
|
title={query ? `Search: ${query}` : "Search"}
|
|
description="Search blog posts"
|
|
>
|
|
<section class="search-page">
|
|
<h1 class="search-title">Search</h1>
|
|
|
|
<form method="get" action="/search" class="search-form">
|
|
<input
|
|
type="search"
|
|
name="q"
|
|
value={query}
|
|
placeholder="Search posts..."
|
|
class="search-input"
|
|
autofocus
|
|
/>
|
|
<button type="submit" class="search-button">Search</button>
|
|
</form>
|
|
|
|
{
|
|
query && (
|
|
<p class="search-summary">
|
|
{results.length === 0
|
|
? `No results for "${query}"`
|
|
: `${results.length} result${results.length === 1 ? "" : "s"} for "${query}"`}
|
|
</p>
|
|
)
|
|
}
|
|
|
|
{
|
|
results.length > 0 && (
|
|
<div class="search-results">
|
|
{results.map((post) => (
|
|
<PostCard
|
|
title={post.data.title}
|
|
excerpt={post.data.excerpt}
|
|
featuredImage={post.data.featured_image}
|
|
href={`/posts/${post.id}`}
|
|
date={post.data.publishedAt ?? undefined}
|
|
readingTime={getReadingTime(post.data.content)}
|
|
/>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
{!query && <p class="search-hint">Enter a search term to find posts.</p>}
|
|
</section>
|
|
</Base>
|
|
|
|
<style>
|
|
.search-page {
|
|
max-width: var(--max-width);
|
|
margin: 0 auto;
|
|
padding: var(--spacing-8) var(--spacing-6) var(--spacing-16);
|
|
}
|
|
|
|
.search-title {
|
|
font-size: var(--font-size-2xl);
|
|
margin-bottom: var(--spacing-6);
|
|
}
|
|
|
|
.search-form {
|
|
display: flex;
|
|
gap: var(--spacing-2);
|
|
margin-bottom: var(--spacing-8);
|
|
}
|
|
|
|
.search-input {
|
|
flex: 1;
|
|
padding: var(--spacing-2) var(--spacing-4);
|
|
font-size: var(--font-size-base);
|
|
border: 1px solid var(--color-border);
|
|
border-radius: var(--radius);
|
|
background: var(--color-bg);
|
|
color: var(--color-text);
|
|
}
|
|
|
|
.search-input:focus {
|
|
outline: none;
|
|
border-color: var(--color-accent);
|
|
}
|
|
|
|
.search-button {
|
|
padding: var(--spacing-2) var(--spacing-6);
|
|
font-size: var(--font-size-base);
|
|
background: var(--color-accent);
|
|
color: var(--color-on-accent);
|
|
border: none;
|
|
border-radius: var(--radius);
|
|
cursor: pointer;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.search-button:hover {
|
|
opacity: 0.9;
|
|
}
|
|
|
|
.search-summary {
|
|
color: var(--color-muted);
|
|
margin-bottom: var(--spacing-6);
|
|
}
|
|
|
|
.search-hint {
|
|
color: var(--color-muted);
|
|
}
|
|
|
|
.search-results {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--spacing-8);
|
|
}
|
|
</style>
|