Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions app/a/[slug]/BlogPostClient.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
"use client"

import Link from "next/link"
import { ArrowLeft, Calendar, User } from "lucide-react"
import Image from "next/image"
import { MarkdownRenderer } from "@/lib/markdown-renderer"
import Footer from "@/components/footer"
import { BlogPost } from "@/lib/blog"

// Conditionally set BASE_PATH based on environment
const BASE_PATH = process.env.NODE_ENV === "production" ? "/StableViewpoints" : ""

export default function BlogPostPage({ post }: { post: BlogPost }) {
return (
<div className="min-h-screen bg-gradient-to-br from-green-50 via-yellow-50 to-[#FFC517]/10">
{/* Header */}
<header className="border-b border-gradient-to-r from-[#228B22]/20 to-[#FFBF00]/20 bg-white/90 backdrop-blur-sm sticky top-0 z-50 shadow-sm">
<div className="max-w-4xl mx-auto px-4 py-4 flex justify-between items-center">
<Link
href="/"
className="inline-flex items-center text-[#228B22] hover:text-[#3E921E] transition-colors font-semibold"
>
<ArrowLeft className="w-4 h-4 mr-2" />
Back to Stable Viewpoints
</Link>
</div>
</header>

{/* Article */}
<article className="max-w-4xl mx-auto px-4 py-12">
<div className="bg-white shadow-xl overflow-hidden border border-gradient-to-r from-[#228B22]/10 to-[#FFBF00]/10">
{/* Hero Image */}
{post.image && (
<div className="relative h-64 md:h-96">
<Image
src={
post.image.startsWith("/")
? `${BASE_PATH}${post.image}`
: post.image
}
alt={post.title}
fill
className="object-cover"
priority
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/20 to-transparent" />
</div>
)}

{/* Content */}
<div className="p-8 md:p-12">
<h1 className="text-4xl md:text-5xl font-bold font-playfair bg-gradient-to-r from-[#228B22] via-[#91A511] to-[#E4B905] bg-clip-text text-transparent mb-6 leading-tight pb-2">
{post.title}
</h1>

<div className="flex items-center gap-6 text-gray-600 mb-8 pb-8 border-b border-gradient-to-r from-[#228B22]/20 to-[#FFBF00]/20">
<div className="flex items-center gap-2">
<User className="w-4 h-4 text-[#228B22]" />
<span className="font-medium">{post.author}</span>
</div>
<div className="flex items-center gap-2">
<Calendar className="w-4 h-4 text-[#E4B905]" />
<span>
{new Date(post.date).toLocaleDateString("en-AU", {
year: "numeric",
month: "long",
day: "numeric",
})}
</span>
</div>
</div>

<div className="prose prose-lg max-w-none">
<MarkdownRenderer content={post.content} />
</div>

<div className="mt-12 pt-8 border-t border-gradient-to-r from-[#228B22]/20 to-[#FFBF00]/20">
<p className="text-sm text-gray-500 italic">
© {new Date(post.date).getFullYear()} {post.author}. All rights reserved.
</p>
</div>
</div>
</div>
</article>

<Footer />
</div>
)
}
157 changes: 12 additions & 145 deletions app/a/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,152 +1,19 @@
"use client"
import { BlogPost, getPostBySlug, getAllPosts } from "@/lib/blog"
import BlogPostPage from "./BlogPostClient"
import { notFound } from "next/navigation"

import { useEffect, useState } from "react"
import { useParams } from "next/navigation"
import { getPostBySlug } from "@/lib/blog"
import Link from "next/link"
import { ArrowLeft, Calendar, User } from "lucide-react"
import Image from "next/image"
import { MarkdownRenderer } from "@/lib/markdown-renderer"
import Footer from "@/components/footer"

interface BlogPost {
slug: string
title: string
author: string
date: string
image: string
excerpt: string
content: string
featured: boolean
export async function generateStaticParams() {
const posts = getAllPosts()
return posts.map((post) => ({ slug: post.slug }))
}

export default function BlogPostPage() {
const params = useParams()
const [post, setPost] = useState<BlogPost | null>(null)
const [loading, setLoading] = useState(true)
const [notFound, setNotFound] = useState(false)

const slug = params.slug as string

useEffect(() => {
function loadPost() {
setLoading(true)
try {
const postData = getPostBySlug(slug)
if (postData) {
setPost(postData)
} else {
setNotFound(true)
}
} catch (error) {
console.error("Error loading post:", error)
setNotFound(true)
} finally {
setLoading(false)
}
}

if (slug) {
loadPost()
}
}, [slug])

if (loading) {
return (
<div className="min-h-screen bg-gradient-to-br from-green-50 via-yellow-50 to-[#FFC517]/10 flex items-center justify-center">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-[#228B22] mx-auto mb-4"></div>
<p className="text-gray-600">Loading article...</p>
</div>
</div>
)
}
export default async function Page(props: { params: { slug: string } }) {
const { slug } = props.params
const post = getPostBySlug(slug)

if (notFound || !post) {
return (
<div className="min-h-screen bg-gradient-to-br from-green-50 via-yellow-50 to-[#FFC517]/10 flex items-center justify-center">
<div className="text-center">
<h1 className="text-4xl font-bold text-gray-900 mb-4">Article Not Found</h1>
<p className="text-gray-600 mb-8">The article you're looking for doesn't exist.</p>
<Link
href="/"
className="inline-flex items-center text-[#228B22] hover:text-[#3E921E] transition-colors font-semibold"
>
<ArrowLeft className="w-4 h-4 mr-2" />
Back to Stable Viewpoints
</Link>
</div>
</div>
)
if (!post) {
notFound()
}

return (
<div className="min-h-screen bg-gradient-to-br from-green-50 via-yellow-50 to-[#FFC517]/10">
{/* Header */}
<header className="border-b border-gradient-to-r from-[#228B22]/20 to-[#FFBF00]/20 bg-white/90 backdrop-blur-sm sticky top-0 z-50 shadow-sm">
<div className="max-w-4xl mx-auto px-4 py-4 flex justify-between items-center">
<Link
href="/"
className="inline-flex items-center text-[#228B22] hover:text-[#3E921E] transition-colors font-semibold"
>
<ArrowLeft className="w-4 h-4 mr-2" />
Back to Stable Viewpoints
</Link>
</div>
</header>

{/* Article */}
<article className="max-w-4xl mx-auto px-4 py-12">
<div className="bg-white shadow-xl overflow-hidden border border-gradient-to-r from-[#228B22]/10 to-[#FFBF00]/10">
{/* Hero Image */}
{post.image && (
<div className="relative h-64 md:h-96">
<Image src={post.image || "/placeholder.svg"} alt={post.title} fill className="object-cover" />
<div className="absolute inset-0 bg-gradient-to-t from-black/20 to-transparent" />
</div>
)}

{/* Content */}
<div className="p-8 md:p-12">
{/* Title */}
<h1 className="text-4xl md:text-5xl font-bold font-playfair bg-gradient-to-r from-[#228B22] via-[#91A511] to-[#E4B905] bg-clip-text text-transparent mb-6 leading-tight pb-2">
{post.title}
</h1>

{/* Meta */}
<div className="flex items-center gap-6 text-gray-600 mb-8 pb-8 border-b border-gradient-to-r from-[#228B22]/20 to-[#FFBF00]/20">
<div className="flex items-center gap-2">
<User className="w-4 h-4 text-[#228B22]" />
<span className="font-medium">{post.author}</span>
</div>
<div className="flex items-center gap-2">
<Calendar className="w-4 h-4 text-[#E4B905]" />
<span>
{new Date(post.date).toLocaleDateString("en-AU", {
year: "numeric",
month: "long",
day: "numeric",
})}
</span>
</div>
</div>

{/* Article Content */}
<div className="prose prose-lg max-w-none">
<MarkdownRenderer content={post.content} />
</div>

{/* Copyright Statement */}
<div className="mt-12 pt-8 border-t border-gradient-to-r from-[#228B22]/20 to-[#FFBF00]/20">
<p className="text-sm text-gray-500 italic">
© {new Date(post.date).getFullYear()} {post.author}. All rights reserved.
</p>
</div>
</div>
</div>
</article>

<Footer />
</div>
)
return <BlogPostPage post={post} />
}
15 changes: 10 additions & 5 deletions components/blog-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ interface BlogCardProps {
post: BlogPost
}

// Conditionally set BASE_PATH depending on environment
const BASE_PATH = process.env.NODE_ENV === "production" ? "/StableViewpoints" : ""

export default function BlogCard({ post }: BlogCardProps) {


return (
<Link href={`/a/${post.slug}`} className="group">
<article className="bg-white shadow-lg overflow-hidden transition-all duration-300 hover:shadow-2xl hover:-translate-y-2 border border-gradient-to-r from-[#228B22]/10 to-[#FFBF00]/10 relative">
Expand All @@ -30,7 +35,11 @@ export default function BlogCard({ post }: BlogCardProps) {
{/* Image - Golden ratio aspect ratio (φ:1 ≈ 1.618:1) */}
<div className="relative overflow-hidden" style={{ aspectRatio: "1.618 / 1" }}>
<Image
src={post.image || "/placeholder.svg"}
src={
post.image.startsWith("/")
? `${BASE_PATH}${post.image}`
: post.image
}
alt={post.title}
fill
className="object-cover transition-transform duration-500 group-hover:scale-110"
Expand All @@ -53,10 +62,6 @@ export default function BlogCard({ post }: BlogCardProps) {
<User className="w-4 h-4" />
<span>{post.author}</span>
</div>
{/*<div className="flex items-center gap-1 group-hover:text-[#E4B905] transition-colors">
<Calendar className="w-4 h-4" />
<span>{new Date(post.date).toLocaleDateString()}</span>
</div> */}
</div>
</div>
</article>
Expand Down
2 changes: 0 additions & 2 deletions lib/blog.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
"use client"

import blogData from "./blog-data.json"

export interface BlogPost {
Expand Down
6 changes: 3 additions & 3 deletions next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/** @type {import('next').NextConfig} */

const nextConfig = {
output: 'export',
trailingSlash: true,
Expand All @@ -10,7 +10,7 @@ const nextConfig = {
},
images: {
unoptimized: true
}
},
}

export default nextConfig
export default nextConfig;
Loading