This commit is contained in:
gpt-engineer-app[bot]
2025-12-21 15:38:22 +00:00
parent f4a1dee9bd
commit ed0d97bb4b
6 changed files with 27 additions and 27 deletions

View File

@@ -9,7 +9,7 @@ interface Review {
title: string; title: string;
body: string; body: string;
created_at: string; created_at: string;
profiles: { full_name: string | null } | null; profiles: { name: string | null } | null;
} }
interface ProductReviewsProps { interface ProductReviewsProps {
@@ -29,7 +29,7 @@ export function ProductReviews({ productId, type }: ProductReviewsProps) {
const fetchReviews = async () => { const fetchReviews = async () => {
let query = supabase let query = supabase
.from('reviews') .from('reviews')
.select('id, rating, title, body, created_at, profiles:user_id (full_name)') .select('id, rating, title, body, created_at, profiles:user_id (name)')
.eq('is_approved', true); .eq('is_approved', true);
if (productId) { if (productId) {
@@ -74,7 +74,7 @@ export function ProductReviews({ productId, type }: ProductReviewsProps) {
rating={review.rating} rating={review.rating}
title={review.title} title={review.title}
body={review.body} body={review.body}
authorName={review.profiles?.full_name || 'Anonymous'} authorName={review.profiles?.name || 'Anonymous'}
date={review.created_at} date={review.created_at}
/> />
))} ))}

View File

@@ -8,7 +8,7 @@ interface Review {
title: string; title: string;
body: string; body: string;
created_at: string; created_at: string;
profiles: { full_name: string | null } | null; profiles: { name: string | null } | null;
} }
export function TestimonialsSection() { export function TestimonialsSection() {
@@ -22,7 +22,7 @@ export function TestimonialsSection() {
const fetchReviews = async () => { const fetchReviews = async () => {
const { data } = await supabase const { data } = await supabase
.from('reviews') .from('reviews')
.select('id, rating, title, body, created_at, profiles:user_id (full_name)') .select('id, rating, title, body, created_at, profiles:user_id (name)')
.eq('is_approved', true) .eq('is_approved', true)
.order('created_at', { ascending: false }) .order('created_at', { ascending: false })
.limit(6); .limit(6);
@@ -45,7 +45,7 @@ export function TestimonialsSection() {
rating={review.rating} rating={review.rating}
title={review.title} title={review.title}
body={review.body} body={review.body}
authorName={review.profiles?.full_name || 'Anonymous'} authorName={review.profiles?.name || 'Anonymous'}
date={review.created_at} date={review.created_at}
/> />
))} ))}

View File

@@ -31,7 +31,7 @@ interface ConsultingSlot {
meet_link: string | null; meet_link: string | null;
created_at: string; created_at: string;
profiles?: { profiles?: {
full_name: string; name: string;
email: string; email: string;
}; };
} }
@@ -78,7 +78,7 @@ export default function AdminConsulting() {
.from('consulting_slots') .from('consulting_slots')
.select(` .select(`
*, *,
profiles:user_id (full_name, email) profiles:user_id (name, email)
`) `)
.order('date', { ascending: false }) .order('date', { ascending: false })
.order('start_time', { ascending: true }); .order('start_time', { ascending: true });
@@ -125,7 +125,7 @@ export default function AdminConsulting() {
body: { body: {
template_key: 'consulting_scheduled', template_key: 'consulting_scheduled',
recipient_email: selectedSlot.profiles.email, recipient_email: selectedSlot.profiles.email,
recipient_name: selectedSlot.profiles.full_name, recipient_name: selectedSlot.profiles.name,
variables: { variables: {
consultation_date: format(parseISO(selectedSlot.date), 'd MMMM yyyy', { locale: id }), consultation_date: format(parseISO(selectedSlot.date), 'd MMMM yyyy', { locale: id }),
consultation_time: `${selectedSlot.start_time.substring(0, 5)} - ${selectedSlot.end_time.substring(0, 5)}`, consultation_time: `${selectedSlot.start_time.substring(0, 5)} - ${selectedSlot.end_time.substring(0, 5)}`,
@@ -168,7 +168,7 @@ export default function AdminConsulting() {
start_time: selectedSlot.start_time, start_time: selectedSlot.start_time,
end_time: selectedSlot.end_time, end_time: selectedSlot.end_time,
topic: selectedSlot.topic_category, topic: selectedSlot.topic_category,
client_name: selectedSlot.profiles?.full_name || 'Client', client_name: selectedSlot.profiles?.name || 'Client',
client_email: selectedSlot.profiles?.email, client_email: selectedSlot.profiles?.email,
calendar_id: settings.integration_google_calendar_id, calendar_id: settings.integration_google_calendar_id,
}), }),
@@ -251,7 +251,7 @@ export default function AdminConsulting() {
{todaySlots.map(slot => ( {todaySlots.map(slot => (
<div key={slot.id} className="flex items-center justify-between text-sm"> <div key={slot.id} className="flex items-center justify-between text-sm">
<span> <span>
{slot.start_time.substring(0, 5)} - {slot.profiles?.full_name || 'N/A'} ({slot.topic_category}) {slot.start_time.substring(0, 5)} - {slot.profiles?.name || 'N/A'} ({slot.topic_category})
</span> </span>
{slot.meet_link ? ( {slot.meet_link ? (
<a href={slot.meet_link} target="_blank" rel="noopener noreferrer" className="text-primary underline flex items-center gap-1"> <a href={slot.meet_link} target="_blank" rel="noopener noreferrer" className="text-primary underline flex items-center gap-1">
@@ -334,7 +334,7 @@ export default function AdminConsulting() {
</TableCell> </TableCell>
<TableCell> <TableCell>
<div> <div>
<p className="font-medium">{slot.profiles?.full_name || '-'}</p> <p className="font-medium">{slot.profiles?.name || '-'}</p>
<p className="text-sm text-muted-foreground">{slot.profiles?.email}</p> <p className="text-sm text-muted-foreground">{slot.profiles?.email}</p>
</div> </div>
</TableCell> </TableCell>
@@ -425,7 +425,7 @@ export default function AdminConsulting() {
<TableRow key={slot.id}> <TableRow key={slot.id}>
<TableCell>{format(parseISO(slot.date), 'd MMM yyyy', { locale: id })}</TableCell> <TableCell>{format(parseISO(slot.date), 'd MMM yyyy', { locale: id })}</TableCell>
<TableCell>{slot.start_time.substring(0, 5)} - {slot.end_time.substring(0, 5)}</TableCell> <TableCell>{slot.start_time.substring(0, 5)} - {slot.end_time.substring(0, 5)}</TableCell>
<TableCell>{slot.profiles?.full_name || '-'}</TableCell> <TableCell>{slot.profiles?.name || '-'}</TableCell>
<TableCell><Badge variant="outline">{slot.topic_category}</Badge></TableCell> <TableCell><Badge variant="outline">{slot.topic_category}</Badge></TableCell>
<TableCell> <TableCell>
<Badge variant={statusLabels[slot.status]?.variant || 'secondary'}> <Badge variant={statusLabels[slot.status]?.variant || 'secondary'}>
@@ -462,7 +462,7 @@ export default function AdminConsulting() {
<div className="p-3 bg-muted rounded-lg text-sm space-y-1"> <div className="p-3 bg-muted rounded-lg text-sm space-y-1">
<p><strong>Tanggal:</strong> {format(parseISO(selectedSlot.date), 'd MMMM yyyy', { locale: id })}</p> <p><strong>Tanggal:</strong> {format(parseISO(selectedSlot.date), 'd MMMM yyyy', { locale: id })}</p>
<p><strong>Waktu:</strong> {selectedSlot.start_time.substring(0, 5)} - {selectedSlot.end_time.substring(0, 5)}</p> <p><strong>Waktu:</strong> {selectedSlot.start_time.substring(0, 5)} - {selectedSlot.end_time.substring(0, 5)}</p>
<p><strong>Klien:</strong> {selectedSlot.profiles?.full_name}</p> <p><strong>Klien:</strong> {selectedSlot.profiles?.name}</p>
<p><strong>Topik:</strong> {selectedSlot.topic_category}</p> <p><strong>Topik:</strong> {selectedSlot.topic_category}</p>
{selectedSlot.notes && <p><strong>Catatan:</strong> {selectedSlot.notes}</p>} {selectedSlot.notes && <p><strong>Catatan:</strong> {selectedSlot.notes}</p>}
</div> </div>

View File

@@ -16,7 +16,7 @@ import { toast } from "@/hooks/use-toast";
interface Member { interface Member {
id: string; id: string;
email: string | null; email: string | null;
full_name: string | null; name: string | null;
created_at: string; created_at: string;
isAdmin?: boolean; isAdmin?: boolean;
} }
@@ -118,7 +118,7 @@ export default function AdminMembers() {
{members.map((member) => ( {members.map((member) => (
<TableRow key={member.id}> <TableRow key={member.id}>
<TableCell>{member.email || "-"}</TableCell> <TableCell>{member.email || "-"}</TableCell>
<TableCell>{member.full_name || "-"}</TableCell> <TableCell>{member.name || "-"}</TableCell>
<TableCell> <TableCell>
{adminIds.has(member.id) ? ( {adminIds.has(member.id) ? (
<Badge className="bg-primary">Admin</Badge> <Badge className="bg-primary">Admin</Badge>
@@ -166,7 +166,7 @@ export default function AdminMembers() {
<span className="text-muted-foreground">Email:</span> {selectedMember.email} <span className="text-muted-foreground">Email:</span> {selectedMember.email}
</p> </p>
<p> <p>
<span className="text-muted-foreground">Nama:</span> {selectedMember.full_name || "-"} <span className="text-muted-foreground">Nama:</span> {selectedMember.name || "-"}
</p> </p>
<p> <p>
<span className="text-muted-foreground">ID:</span> {selectedMember.id} <span className="text-muted-foreground">ID:</span> {selectedMember.id}

View File

@@ -23,7 +23,7 @@ interface Review {
body: string; body: string;
is_approved: boolean; is_approved: boolean;
created_at: string; created_at: string;
profiles: { full_name: string | null; email: string | null } | null; profiles: { name: string | null; email: string | null } | null;
products: { title: string } | null; products: { title: string } | null;
} }
@@ -44,7 +44,7 @@ export default function AdminReviews() {
.from("reviews") .from("reviews")
.select(` .select(`
*, *,
profiles:user_id (full_name, email), profiles:user_id (name, email),
products:product_id (title) products:product_id (title)
`) `)
.order("created_at", { ascending: false }); .order("created_at", { ascending: false });
@@ -224,7 +224,7 @@ export default function AdminReviews() {
<h3 className="font-bold">{review.title}</h3> <h3 className="font-bold">{review.title}</h3>
{review.body && <p className="text-muted-foreground text-sm">{review.body}</p>} {review.body && <p className="text-muted-foreground text-sm">{review.body}</p>}
<div className="text-xs text-muted-foreground"> <div className="text-xs text-muted-foreground">
<span>{review.profiles?.full_name || review.profiles?.email || "Unknown"}</span> <span>{review.profiles?.name || review.profiles?.email || "Unknown"}</span>
{review.products && <span> {review.products.title}</span>} {review.products && <span> {review.products.title}</span>}
<span> {new Date(review.created_at).toLocaleDateString("id-ID")}</span> <span> {new Date(review.created_at).toLocaleDateString("id-ID")}</span>
</div> </div>
@@ -300,7 +300,7 @@ export default function AdminReviews() {
<h3 className="font-bold">{review.title}</h3> <h3 className="font-bold">{review.title}</h3>
{review.body && <p className="text-muted-foreground text-sm">{review.body}</p>} {review.body && <p className="text-muted-foreground text-sm">{review.body}</p>}
<div className="text-xs text-muted-foreground"> <div className="text-xs text-muted-foreground">
{review.profiles?.full_name || review.profiles?.email} {review.profiles?.name || review.profiles?.email}
{review.products && <span> {review.products.title}</span>} {review.products && <span> {review.products.title}</span>}
</div> </div>
</div> </div>

View File

@@ -15,7 +15,7 @@ import { User, LogOut, Phone } from 'lucide-react';
interface Profile { interface Profile {
id: string; id: string;
email: string | null; email: string | null;
full_name: string | null; name: string | null;
avatar_url: string | null; avatar_url: string | null;
whatsapp_number: string | null; whatsapp_number: string | null;
whatsapp_opt_in: boolean; whatsapp_opt_in: boolean;
@@ -28,7 +28,7 @@ export default function MemberProfile() {
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);
const [form, setForm] = useState({ const [form, setForm] = useState({
full_name: '', name: '',
avatar_url: '', avatar_url: '',
whatsapp_number: '', whatsapp_number: '',
whatsapp_opt_in: false, whatsapp_opt_in: false,
@@ -48,7 +48,7 @@ export default function MemberProfile() {
if (data) { if (data) {
setProfile(data); setProfile(data);
setForm({ setForm({
full_name: data.full_name || '', name: data.name || '',
avatar_url: data.avatar_url || '', avatar_url: data.avatar_url || '',
whatsapp_number: data.whatsapp_number || '', whatsapp_number: data.whatsapp_number || '',
whatsapp_opt_in: data.whatsapp_opt_in || false, whatsapp_opt_in: data.whatsapp_opt_in || false,
@@ -78,7 +78,7 @@ export default function MemberProfile() {
const { error } = await supabase const { error } = await supabase
.from('profiles') .from('profiles')
.update({ .update({
full_name: form.full_name, name: form.name,
avatar_url: form.avatar_url || null, avatar_url: form.avatar_url || null,
whatsapp_number: normalizedWA || null, whatsapp_number: normalizedWA || null,
whatsapp_opt_in: form.whatsapp_opt_in, whatsapp_opt_in: form.whatsapp_opt_in,
@@ -132,8 +132,8 @@ export default function MemberProfile() {
<div className="space-y-2"> <div className="space-y-2">
<Label>Nama Lengkap</Label> <Label>Nama Lengkap</Label>
<Input <Input
value={form.full_name} value={form.name}
onChange={(e) => setForm({ ...form, full_name: e.target.value })} onChange={(e) => setForm({ ...form, name: e.target.value })}
className="border-2" className="border-2"
placeholder="Masukkan nama lengkap" placeholder="Masukkan nama lengkap"
/> />