Initial commit for homaroma
This commit is contained in:
commit
f2179cfe57
269 files changed
+22635
No files matched your search
@@ -0,0 +1,85 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useParams, Link } from 'react-router-dom'
|
||||
import { API_BASE, type Article, type Category } from '../types'
|
||||
import Navbar from '../components/Navbar'
|
||||
import Footer from '../components/Footer'
|
||||
import { ArrowLeft, Loader2 } from 'lucide-react'
|
||||
|
||||
export default function ArticleReaderPage() {
|
||||
const { slug } = useParams()
|
||||
const [article, setArticle] = useState<Article | null>(null)
|
||||
const [categories, setCategories] = useState<Category[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
fetch(`${API_BASE}/categories`).then(r => r.json()).then(setCategories).catch(console.error)
|
||||
fetch(`${API_BASE}/articles/${slug}`)
|
||||
.then(res => {
|
||||
if (!res.ok) throw new Error()
|
||||
return res.json()
|
||||
})
|
||||
.then(setArticle)
|
||||
.catch(() => setArticle(null))
|
||||
.finally(() => setLoading(false))
|
||||
}, [slug])
|
||||
|
||||
if (loading) return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-black">
|
||||
<Loader2 className="w-8 h-8 animate-spin text-cyan-glow" />
|
||||
</div>
|
||||
)
|
||||
|
||||
if (!article) return (
|
||||
<div className="min-h-screen flex flex-col bg-black">
|
||||
<Navbar categories={categories} />
|
||||
<div className="flex-1 flex flex-col items-center justify-center px-6 text-center space-y-4">
|
||||
<h1 className="text-4xl font-bold">Article not found</h1>
|
||||
<Link to="/scent-guide" className="btn-secondary px-6 py-3">Return to Scent Guide</Link>
|
||||
</div>
|
||||
<Footer categories={categories} />
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col bg-night-950">
|
||||
<Navbar categories={categories} />
|
||||
|
||||
<main className="flex-1 pt-24 pb-20">
|
||||
<article className="max-w-4xl mx-auto w-full animate-fade-in relative mt-8">
|
||||
|
||||
<Link to="/scent-guide" className="absolute -left-16 xl:-left-24 top-0 mt-8 hidden lg:flex p-4 glass border border-white/5 rounded-full hover:bg-white/10 transition-colors">
|
||||
<ArrowLeft className="w-5 h-5 text-slate-300" />
|
||||
</Link>
|
||||
|
||||
{/* Header */}
|
||||
<div className="text-center px-6 py-12 space-y-6">
|
||||
<Link to="/scent-guide" className="lg:hidden text-slate-400 text-sm hover:text-white inline-flex items-center gap-2 mb-4">
|
||||
<ArrowLeft className="w-4 h-4" /> Go Back
|
||||
</Link>
|
||||
<p className="text-cyan-glow uppercase tracking-[0.3em] font-bold text-xs">
|
||||
{new Date(article.createdAt).toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' })}
|
||||
</p>
|
||||
<h1 className="text-4xl md:text-5xl lg:text-6xl font-bold leading-tight max-w-3xl mx-auto font-serif">
|
||||
{article.title}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
{/* Cover Image */}
|
||||
{article.coverImage && (
|
||||
<div className="w-full aspect-[16/7] bg-night-900 md:rounded-[2rem] overflow-hidden shadow-2xl mb-16 border border-white/5">
|
||||
<img src={article.coverImage} alt={article.title} className="w-full h-full object-cover" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Content Body */}
|
||||
<div className="px-6 md:px-12 prose prose-invert prose-lg prose-cyan mx-auto max-w-3xl pb-16">
|
||||
<div dangerouslySetInnerHTML={{ __html: article.content }} className="space-y-6" />
|
||||
</div>
|
||||
|
||||
</article>
|
||||
</main>
|
||||
|
||||
<Footer categories={categories} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user