Fix blog post foreign key

This commit is contained in:
gpt-engineer-app[bot]
2025-10-10 23:00:04 +00:00
parent 6127f902e9
commit bc08d44f4c
5 changed files with 41 additions and 32 deletions

View File

@@ -117,7 +117,22 @@ export type Database = {
updated_at?: string | null updated_at?: string | null
view_count?: number | null view_count?: number | null
} }
Relationships: [] Relationships: [
{
foreignKeyName: "blog_posts_author_id_fkey"
columns: ["author_id"]
isOneToOne: false
referencedRelation: "filtered_profiles"
referencedColumns: ["user_id"]
},
{
foreignKeyName: "blog_posts_author_id_fkey"
columns: ["author_id"]
isOneToOne: false
referencedRelation: "profiles"
referencedColumns: ["user_id"]
},
]
} }
companies: { companies: {
Row: { Row: {

View File

@@ -15,6 +15,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { UppyPhotoUpload } from '@/components/upload/UppyPhotoUpload'; import { UppyPhotoUpload } from '@/components/upload/UppyPhotoUpload';
import { generateSlugFromName } from '@/lib/slugUtils'; import { generateSlugFromName } from '@/lib/slugUtils';
import { extractCloudflareImageId } from '@/lib/cloudflareImageUtils';
import { Edit, Trash2, Eye, Plus } from 'lucide-react'; import { Edit, Trash2, Eye, Plus } from 'lucide-react';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { formatDistanceToNow } from 'date-fns'; import { formatDistanceToNow } from 'date-fns';
@@ -284,12 +285,15 @@ export default function AdminBlog() {
<div className="space-y-2"> <div className="space-y-2">
<Label>Featured Image</Label> <Label>Featured Image</Label>
<UppyPhotoUpload <UppyPhotoUpload
onUploadComplete={(results) => { onUploadComplete={(urls) => {
if (results.length > 0) { if (urls.length > 0) {
const result = results[0]; const url = urls[0];
handleImageUpload(result.cloudflareImageId, result.cloudflareImageUrl); const imageId = extractCloudflareImageId(url);
if (imageId) {
handleImageUpload(imageId, url);
toast.success('Image uploaded'); toast.success('Image uploaded');
} }
}
}} }}
maxFiles={1} maxFiles={1}
/> />

View File

@@ -20,14 +20,7 @@ export default function BlogIndex() {
queryFn: async () => { queryFn: async () => {
let query = supabase let query = supabase
.from('blog_posts') .from('blog_posts')
.select(` .select('*, profiles!inner(username, display_name, avatar_url)', { count: 'exact' })
*,
author:profiles(
username,
display_name,
avatar_url
)
`, { count: 'exact' })
.eq('status', 'published') .eq('status', 'published')
.order('published_at', { ascending: false }) .order('published_at', { ascending: false })
.range((page - 1) * POSTS_PER_PAGE, page * POSTS_PER_PAGE - 1); .range((page - 1) * POSTS_PER_PAGE, page * POSTS_PER_PAGE - 1);
@@ -36,9 +29,9 @@ export default function BlogIndex() {
query = query.or(`title.ilike.%${searchQuery}%,content.ilike.%${searchQuery}%`); query = query.or(`title.ilike.%${searchQuery}%,content.ilike.%${searchQuery}%`);
} }
const { data, count, error } = await query; const { data: posts, count, error } = await query;
if (error) throw error; if (error) throw error;
return { posts: data, totalCount: count || 0 }; return { posts, totalCount: count || 0 };
}, },
}); });
@@ -92,9 +85,9 @@ export default function BlogIndex() {
content={post.content} content={post.content}
featuredImageId={post.featured_image_id} featuredImageId={post.featured_image_id}
author={{ author={{
username: post.author.username, username: post.profiles.username,
displayName: post.author.display_name, displayName: post.profiles.display_name,
avatarUrl: post.author.avatar_url, avatarUrl: post.profiles.avatar_url,
}} }}
publishedAt={post.published_at!} publishedAt={post.published_at!}
viewCount={post.view_count} viewCount={post.view_count}

View File

@@ -18,21 +18,14 @@ export default function BlogPost() {
const { data: post, isLoading } = useQuery({ const { data: post, isLoading } = useQuery({
queryKey: ['blog-post', slug], queryKey: ['blog-post', slug],
queryFn: async () => { queryFn: async () => {
const { data, error } = await supabase const query = supabase
.from('blog_posts') .from('blog_posts')
.select(` .select('*, profiles!inner(username, display_name, avatar_url, avatar_image_id)')
*,
author:profiles(
username,
display_name,
avatar_url,
avatar_image_id
)
`)
.eq('slug', slug) .eq('slug', slug)
.eq('status', 'published') .eq('status', 'published')
.single(); .single();
const { data, error } = await query;
if (error) throw error; if (error) throw error;
return data; return data;
}, },
@@ -98,14 +91,14 @@ export default function BlogPost() {
<div className="flex items-center justify-between mb-8 pb-6 border-b"> <div className="flex items-center justify-between mb-8 pb-6 border-b">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Avatar className="w-12 h-12"> <Avatar className="w-12 h-12">
<AvatarImage src={post.author.avatar_url} /> <AvatarImage src={post.profiles.avatar_url} />
<AvatarFallback> <AvatarFallback>
{post.author.display_name?.[0] || post.author.username[0]} {post.profiles.display_name?.[0] || post.profiles.username[0]}
</AvatarFallback> </AvatarFallback>
</Avatar> </Avatar>
<div> <div>
<p className="font-medium"> <p className="font-medium">
{post.author.display_name || post.author.username} {post.profiles.display_name || post.profiles.username}
</p> </p>
<div className="flex items-center gap-3 text-sm text-muted-foreground"> <div className="flex items-center gap-3 text-sm text-muted-foreground">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">

View File

@@ -0,0 +1,4 @@
-- Fix blog_posts foreign key to reference profiles
ALTER TABLE blog_posts DROP CONSTRAINT IF EXISTS blog_posts_author_id_fkey;
ALTER TABLE blog_posts ADD CONSTRAINT blog_posts_author_id_fkey
FOREIGN KEY (author_id) REFERENCES profiles(user_id) ON DELETE CASCADE;