diff --git a/app/admin/blog-posts/page.tsx b/app/admin/blog-posts/page.tsx index 927cb89b..93d42fa4 100644 --- a/app/admin/blog-posts/page.tsx +++ b/app/admin/blog-posts/page.tsx @@ -1,12 +1,11 @@ "use client" -import { useState, useEffect, useCallback, useMemo, useRef } from "react" +import { useState, useEffect, useCallback, useMemo } from "react" import { createClient } from "@/lib/supabase/client" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" -import { Textarea } from "@/components/ui/textarea" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog" @@ -17,6 +16,7 @@ import { AlertCircle, FileText, Search, MoreHorizontal, Edit, Star, Trash2, Plus import { Alert, AlertDescription } from "@/components/ui/alert" import { categories, BlogPost } from "@/components/data/blog-posts" import { OptimizedImage } from '@/components/ui/optimized-image' +import { RichTextEditor } from '@/components/ui/rich-text-editor' // types interface BlogFormData { @@ -86,17 +86,17 @@ const useBlogPosts = () => { try { setIsLoading(true) setError(null) - + // First, fetch all blog posts const { data: postsData, error: fetchError } = await supabase .from("blogs") .select("*") .order("date", { ascending: false }) - + if (fetchError) { throw new Error(fetchError.message) } - + if (postsData) { // Fetch real like counts and ensure views are up-to-date for each blog post const postsWithRealCounts = await Promise.all( @@ -106,30 +106,30 @@ const useBlogPosts = () => { .from('blog_likes') .select('*', { count: 'exact', head: true }) .eq('blog_slug', post.slug || post.title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '')) - + if (likeError) { console.error('Error fetching likes for post:', post.title, likeError) } - + // Get the most up-to-date view count from blogs table const { data: viewData, error: viewError } = await supabase .from('blogs') .select('views') .eq('id', post.id) .single() - + if (viewError) { console.error('Error fetching views for post:', post.title, viewError) } - - return { - ...post, + + return { + ...post, likes: likeCount || 0, views: viewData?.views?.toString() || post.views || '0' } }) ) - + setBlogPosts(postsWithRealCounts as BlogPost[]) } } catch (err) { @@ -168,8 +168,8 @@ const FeaturedBadge = ({ featured }: { featured: boolean }) => ( ) ) -const BlogPostForm = ({ - formData, +const BlogPostForm = ({ + formData, onFormChange }: { formData: BlogFormData; @@ -189,9 +189,6 @@ const BlogPostForm = ({ onFormChange({ featured: checked }) } - // ref for the content textarea - const contentRef = useRef(null); - // Article image upload handler (for main blog image) const handleArticleImageUpload = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; @@ -220,48 +217,6 @@ const BlogPostForm = ({ } } - // image upload handler for inserting into content - const handleContentImageUpload = async (e: React.ChangeEvent) => { - const file = e.target.files?.[0]; - if (!file) return; - - const supabase = createClient(); - const filePath = `public/${Date.now()}-${file.name}`; - - // upload to supabase storage - const { error } = await supabase.storage - .from('blog-images') - .upload(filePath, file); - - if (error) { - alert("Image upload failed: " + error.message); - return; - } - - // Get public url - const { data: publicUrlData } = supabase.storage - .from('blog-images') - .getPublicUrl(filePath); - - if (publicUrlData?.publicUrl) { - // insert html tag at cursor in content - if (contentRef.current) { - const textarea = contentRef.current; - const start = textarea.selectionStart; - const end = textarea.selectionEnd; - const before = formData.content.slice(0, start); - const after = formData.content.slice(end); - const htmlImg = `Alt text\n`; - const newContent = before + htmlImg + after; - onFormChange({ content: newContent }); - setTimeout(() => { - textarea.focus(); - textarea.selectionStart = textarea.selectionEnd = start + htmlImg.length; - }, 0); - } - } - } - return (
{/* Article Image upload section */} @@ -312,25 +267,16 @@ const BlogPostForm = ({ />
- {/* Content image upload button */} + {/* Rich Text Editor for Content */}
-