120 lines
4.6 KiB
TypeScript
120 lines
4.6 KiB
TypeScript
'use client'
|
|
import Link from 'next/link'
|
|
import { useEffect } from 'react'
|
|
import { useApi } from '@/lib/hooks/useApi'
|
|
import { LoadingState } from '@/components/ui/LoadingState'
|
|
import { ErrorState } from '@/components/ui/ErrorState'
|
|
|
|
interface BlogPost {
|
|
id: string
|
|
title: string
|
|
link: string
|
|
published_at: string
|
|
summary: string
|
|
image_url?: string
|
|
author?: string
|
|
}
|
|
|
|
interface BlogResponse {
|
|
title: string
|
|
description: string
|
|
items: BlogPost[]
|
|
}
|
|
export default function TheJournalPage() {
|
|
const { data, error, isLoading, execute } = useApi<BlogResponse>()
|
|
|
|
useEffect(() => {
|
|
execute({
|
|
url: '/blog/rss',
|
|
method: 'GET',
|
|
})
|
|
}, [])
|
|
|
|
return (
|
|
<main className="min-h-screen bg-white">
|
|
<header className="sticky top-0 z-50 bg-white shadow-sm border-b border-gray-200">
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
|
<div className="flex items-center justify-between">
|
|
<Link href="/" className="text-2xl font-bold text-gray-900">MoreThanADiagnosis</Link>
|
|
<nav className="hidden md:flex gap-8">
|
|
<Link href="/" className="text-gray-600 hover:text-blue-600">Home</Link>
|
|
<Link href="/thejournal" className="text-gray-600 hover:text-blue-600 font-semibold text-blue-600">The Journal</Link>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
|
{isLoading && <LoadingState message="Loading journal entries..." />}
|
|
|
|
{error && <ErrorState message="Failed to load journal entries." onRetry={() => window.location.reload()} />}
|
|
|
|
{!isLoading && !error && (
|
|
<div className="space-y-6">
|
|
<div>
|
|
<h1 className="text-3xl font-bold text-gray-900 dark:text-white font-heading">
|
|
The Journal
|
|
</h1>
|
|
<p className="mt-2 text-gray-600 dark:text-gray-400">
|
|
Reflections, stories, and updates from our community.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
|
|
{data?.items.map((post) => (
|
|
<article
|
|
key={post.id}
|
|
className="flex flex-col bg-white dark:bg-gray-800 rounded-lg shadow-lg overflow-hidden hover:shadow-xl transition-shadow duration-300"
|
|
>
|
|
{post.image_url && (
|
|
<div className="flex-shrink-0 h-48 w-full relative">
|
|
<img
|
|
className="h-full w-full object-cover"
|
|
src={post.image_url}
|
|
alt={post.title}
|
|
/>
|
|
</div>
|
|
)}
|
|
<div className="flex-1 p-6 flex flex-col justify-between">
|
|
<div className="flex-1">
|
|
<p className="text-sm font-medium text-primary-600">
|
|
{post.author || 'MoreThanADiagnosis'}
|
|
</p>
|
|
<a href={post.link} target="_blank" rel="noopener noreferrer" className="block mt-2">
|
|
<p className="text-xl font-semibold text-gray-900 dark:text-white">
|
|
{post.title}
|
|
</p>
|
|
<div
|
|
className="mt-3 text-base text-gray-500 dark:text-gray-400 line-clamp-3"
|
|
dangerouslySetInnerHTML={{ __html: post.summary || '' }}
|
|
/>
|
|
</a>
|
|
</div>
|
|
<div className="mt-6 flex items-center">
|
|
<div className="flex-shrink-0">
|
|
<span className="sr-only">{post.published_at}</span>
|
|
</div>
|
|
<div className="text-sm text-gray-500 dark:text-gray-400">
|
|
<time dateTime={post.published_at}>
|
|
{new Date(post.published_at).toLocaleDateString(undefined, {
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric',
|
|
})}
|
|
</time>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
<footer className="bg-gray-900 text-white py-12 border-t border-gray-800">
|
|
<div className="max-w-7xl mx-auto px-4 text-center">
|
|
<p>© 2025 More Than A Diagnosis. All rights reserved.</p>
|
|
</div>
|
|
</footer>
|
|
</main>
|
|
)
|
|
}
|