mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-24 23:51:13 -05:00
Add blog page components
This commit is contained in:
140
src/pages/BlogPost.tsx
Normal file
140
src/pages/BlogPost.tsx
Normal file
@@ -0,0 +1,140 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useParams, Link } from 'react-router-dom';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { supabase } from '@/integrations/supabase/client';
|
||||
import { MarkdownRenderer } from '@/components/blog/MarkdownRenderer';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ArrowLeft, Calendar, Eye } from 'lucide-react';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import { getCloudflareImageUrl } from '@/lib/cloudflareImageUtils';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { Header } from '@/components/layout/Header';
|
||||
import { Footer } from '@/components/layout/Footer';
|
||||
|
||||
export default function BlogPost() {
|
||||
const { slug } = useParams<{ slug: string }>();
|
||||
|
||||
const { data: post, isLoading } = useQuery({
|
||||
queryKey: ['blog-post', slug],
|
||||
queryFn: async () => {
|
||||
const { data, error } = await supabase
|
||||
.from('blog_posts')
|
||||
.select(`
|
||||
*,
|
||||
author:profiles(
|
||||
username,
|
||||
display_name,
|
||||
avatar_url,
|
||||
avatar_image_id
|
||||
)
|
||||
`)
|
||||
.eq('slug', slug)
|
||||
.eq('status', 'published')
|
||||
.single();
|
||||
|
||||
if (error) throw error;
|
||||
return data;
|
||||
},
|
||||
enabled: !!slug,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (slug) {
|
||||
supabase.rpc('increment_blog_view_count', { post_slug: slug });
|
||||
}
|
||||
}, [slug]);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<Header />
|
||||
<div className="container mx-auto px-4 py-12 max-w-4xl">
|
||||
<Skeleton className="h-12 w-32 mb-8" />
|
||||
<Skeleton className="h-16 w-full mb-4" />
|
||||
<Skeleton className="h-8 w-2/3 mb-8" />
|
||||
<Skeleton className="h-[400px] w-full mb-8" />
|
||||
<Skeleton className="h-96 w-full" />
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!post) {
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<Header />
|
||||
<div className="container mx-auto px-4 py-12 max-w-4xl text-center">
|
||||
<h1 className="text-2xl font-bold mb-4">Post Not Found</h1>
|
||||
<Link to="/blog">
|
||||
<Button variant="outline">
|
||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
||||
Back to Blog
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<Header />
|
||||
|
||||
<article className="container mx-auto px-4 py-12 max-w-4xl">
|
||||
<Link to="/blog" className="inline-block mb-8">
|
||||
<Button variant="ghost" size="sm">
|
||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
||||
Back to Blog
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
<h1 className="text-5xl font-bold mb-6 leading-tight">
|
||||
{post.title}
|
||||
</h1>
|
||||
|
||||
<div className="flex items-center justify-between mb-8 pb-6 border-b">
|
||||
<div className="flex items-center gap-3">
|
||||
<Avatar className="w-12 h-12">
|
||||
<AvatarImage src={post.author.avatar_url} />
|
||||
<AvatarFallback>
|
||||
{post.author.display_name?.[0] || post.author.username[0]}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div>
|
||||
<p className="font-medium">
|
||||
{post.author.display_name || post.author.username}
|
||||
</p>
|
||||
<div className="flex items-center gap-3 text-sm text-muted-foreground">
|
||||
<div className="flex items-center gap-1">
|
||||
<Calendar className="w-3 h-3" />
|
||||
{formatDistanceToNow(new Date(post.published_at!), { addSuffix: true })}
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Eye className="w-3 h-3" />
|
||||
{post.view_count} views
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{post.featured_image_id && (
|
||||
<div className="mb-12 rounded-lg overflow-hidden shadow-2xl">
|
||||
<img
|
||||
src={getCloudflareImageUrl(post.featured_image_id, 'public')}
|
||||
alt={post.title}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<MarkdownRenderer content={post.content} />
|
||||
</article>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user