Changes: - Revert to using profiles!user_id (name, avatar_url) JOIN for reviews - Remove reviewer_name storage from ReviewModal (no longer needed) - Add avatar display to ReviewCard component - Reviews now sync automatically with profile changes - Public queries safely expose only name + avatar via RLS This ensures: - Name/avatar changes update across all reviews automatically - No frozen/outdated reviewer data - Only public profile fields exposed (secure) - Reviews serve as live, credible social proof 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
57 lines
1.5 KiB
TypeScript
57 lines
1.5 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { supabase } from '@/integrations/supabase/client';
|
|
import { ReviewCard } from './ReviewCard';
|
|
|
|
interface Review {
|
|
id: string;
|
|
rating: number;
|
|
title: string;
|
|
body: string;
|
|
created_at: string;
|
|
profiles: { name: string | null; avatar_url: string | null } | null;
|
|
}
|
|
|
|
export function TestimonialsSection() {
|
|
const [reviews, setReviews] = useState<Review[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
fetchReviews();
|
|
}, []);
|
|
|
|
const fetchReviews = async () => {
|
|
const { data } = await supabase
|
|
.from('reviews')
|
|
.select('id, rating, title, body, created_at, profiles!user_id (name, avatar_url)')
|
|
.eq('is_approved', true)
|
|
.order('created_at', { ascending: false })
|
|
.limit(6);
|
|
|
|
if (data) {
|
|
setReviews(data as unknown as Review[]);
|
|
}
|
|
setLoading(false);
|
|
};
|
|
|
|
if (loading || reviews.length === 0) return null;
|
|
|
|
return (
|
|
<section className="container mx-auto px-4 py-16">
|
|
<h2 className="text-3xl font-bold text-center mb-8">Apa Kata Mereka</h2>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
{reviews.map((review) => (
|
|
<ReviewCard
|
|
key={review.id}
|
|
rating={review.rating}
|
|
title={review.title}
|
|
body={review.body}
|
|
authorName={review.profiles?.name || 'Anonymous'}
|
|
authorAvatar={review.profiles?.avatar_url}
|
|
date={review.created_at}
|
|
/>
|
|
))}
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|