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 { IconBook } from '@/components/Icons'; import { getServerDictionary } from '@/lib/i18n/server'; import './journal.css'; export const dynamic = 'force-dynamic'; export function generateMetadata(): Metadata { const t = getServerDictionary(); return { title: t.journal.title, description: t.journal.description }; } interface EnrichedJournalEntry extends JournalEntrySummary { coverUrl?: string; blurDataURL?: string; dominantColor?: string; } export default async function JournalIndexPage() { const t = getServerDictionary(); 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 (

{t.journal.title}

{t.journal.subtitle}

{enrichedEntries.length === 0 ? (

{t.journal.empty}

) : (
{enrichedEntries.map((entry) => { const dateStr = entry.frontmatter.date ? new Date(entry.frontmatter.date).toLocaleDateString(t.dateLocale, { year: 'numeric', month: 'short', day: 'numeric', }) : null; return (
{entry.coverUrl ? ( {entry.frontmatter.title ) : (
)}
{dateStr && {dateStr}} {dateStr && entry.readingTimeMinutes && •} {entry.readingTimeMinutes && ( {t.journal.minRead(entry.readingTimeMinutes)} )} {entry.frontmatter.draft && ( {t.journal.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}

)}
{t.journal.readStory} {entry.frontmatter.author && ( {t.journal.by(entry.frontmatter.author)} )}
); })}
)}
); }