import Link from 'next/link'; import Image from 'next/image'; import type { Metadata } from 'next'; import { type JournalEntrySummary } from '@/lib/journal'; import { listJournalEntries } from '@/lib/admin/journal-service'; import { isAdminAuthenticated } from '@/lib/admin/auth'; import { immich } from '@/lib/immich'; import { imageUrl, assetPlaceholder } from '@/lib/urls'; import { BackLink } from '@/components/BackLink'; import './journal.css'; export const dynamic = 'force-dynamic'; export const metadata: Metadata = { title: 'Journal', description: 'Photo essays, travel stories, and behind-the-scenes journals.', }; interface EnrichedJournalEntry extends JournalEntrySummary { coverUrl?: string; blurDataURL?: string; dominantColor?: string; } export default async function JournalIndexPage() { const isAuthedAdmin = await isAdminAuthenticated(); const allEntries = await listJournalEntries(); // Non-admins only see published entries const visibleEntries = isAuthedAdmin ? allEntries : allEntries.filter((e) => !e.frontmatter.draft); // Fetch cover image placeholders and URLs const enrichedEntries: EnrichedJournalEntry[] = await Promise.all( visibleEntries.map(async (entry) => { if (!entry.frontmatter.coverAssetId) { return entry; } try { const asset = await immich.getAssetInfo(entry.frontmatter.coverAssetId); const ph = asset ? assetPlaceholder(asset) : null; return { ...entry, coverUrl: imageUrl(entry.frontmatter.coverAssetId, 'preview'), blurDataURL: ph?.blurDataURL, dominantColor: ph?.dominantColor, }; } catch { return { ...entry, coverUrl: imageUrl(entry.frontmatter.coverAssetId, 'preview'), }; } }), ); return (

Journal

Photo essays, visual stories, and field notes.

{enrichedEntries.length === 0 ? (

No journal entries published yet.

) : (
{enrichedEntries.map((entry) => { const dateStr = entry.frontmatter.date ? new Date(entry.frontmatter.date).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', }) : null; return (
{entry.coverUrl ? ( {entry.frontmatter.title ) : (
📖
)}
{dateStr && {dateStr}} {dateStr && entry.readingTimeMinutes && •} {entry.readingTimeMinutes && {entry.readingTimeMinutes} min read} {entry.frontmatter.draft && ( Draft )}

{entry.frontmatter.title || entry.slug}

{entry.frontmatter.subtitle && (

{entry.frontmatter.subtitle}

)} {/* extractExcerpt() falls back to the subtitle when there is one, so rendering both printed the same sentence twice. */} {entry.excerpt && entry.excerpt !== entry.frontmatter.subtitle && (

{entry.excerpt}

)}
Read Story → {entry.frontmatter.author && ( by {entry.frontmatter.author} )}
); })}
)}
); }