Code edited in Lovable Code Editor
Edited UI in Lovable
This commit is contained in:
@@ -1,17 +1,17 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from "react";
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from "react-router-dom";
|
||||||
import { supabase } from '@/integrations/supabase/client';
|
import { supabase } from "@/integrations/supabase/client";
|
||||||
import { useAuth } from '@/hooks/useAuth';
|
import { useAuth } from "@/hooks/useAuth";
|
||||||
import { AppLayout } from '@/components/AppLayout';
|
import { AppLayout } from "@/components/AppLayout";
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from "@/components/ui/button";
|
||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||||
import { Skeleton } from '@/components/ui/skeleton';
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import { formatDateTime } from '@/lib/format';
|
import { formatDateTime } from "@/lib/format";
|
||||||
import { Eye, Shield, ShieldOff } from 'lucide-react';
|
import { Eye, Shield, ShieldOff } from "lucide-react";
|
||||||
import { toast } from '@/hooks/use-toast';
|
import { toast } from "@/hooks/use-toast";
|
||||||
|
|
||||||
interface Member {
|
interface Member {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -39,46 +39,49 @@ export default function AdminMembers() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!authLoading) {
|
if (!authLoading) {
|
||||||
if (!user) navigate('/auth');
|
if (!user) navigate("/auth");
|
||||||
else if (!isAdmin) navigate('/dashboard');
|
else if (!isAdmin) navigate("/dashboard");
|
||||||
else fetchMembers();
|
else fetchMembers();
|
||||||
}
|
}
|
||||||
}, [user, isAdmin, authLoading]);
|
}, [user, isAdmin, authLoading]);
|
||||||
|
|
||||||
const fetchMembers = async () => {
|
const fetchMembers = async () => {
|
||||||
const [profilesRes, rolesRes] = await Promise.all([
|
const [profilesRes, rolesRes] = await Promise.all([
|
||||||
supabase.from('profiles').select('*').order('created_at', { ascending: false }),
|
supabase.from("profiles").select("*").order("created_at", { ascending: false }),
|
||||||
supabase.from('user_roles').select('user_id').eq('role', 'admin'),
|
supabase.from("user_roles").select("user_id").eq("role", "admin"),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const admins = new Set((rolesRes.data || []).map(r => r.user_id));
|
const admins = new Set((rolesRes.data || []).map((r) => r.user_id));
|
||||||
setAdminIds(admins);
|
setAdminIds(admins);
|
||||||
|
|
||||||
if (profilesRes.data) {
|
if (profilesRes.data) {
|
||||||
setMembers(profilesRes.data.map(p => ({ ...p, isAdmin: admins.has(p.id) })));
|
setMembers(profilesRes.data.map((p) => ({ ...p, isAdmin: admins.has(p.id) })));
|
||||||
}
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const viewMemberDetails = async (member: Member) => {
|
const viewMemberDetails = async (member: Member) => {
|
||||||
setSelectedMember(member);
|
setSelectedMember(member);
|
||||||
const { data } = await supabase
|
const { data } = await supabase.from("user_access").select("*, product:products(title)").eq("user_id", member.id);
|
||||||
.from('user_access')
|
setMemberAccess((data as unknown as UserAccess[]) || []);
|
||||||
.select('*, product:products(title)')
|
|
||||||
.eq('user_id', member.id);
|
|
||||||
setMemberAccess(data as unknown as UserAccess[] || []);
|
|
||||||
setDialogOpen(true);
|
setDialogOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleAdminRole = async (memberId: string, currentlyAdmin: boolean) => {
|
const toggleAdminRole = async (memberId: string, currentlyAdmin: boolean) => {
|
||||||
if (currentlyAdmin) {
|
if (currentlyAdmin) {
|
||||||
const { error } = await supabase.from('user_roles').delete().eq('user_id', memberId).eq('role', 'admin');
|
const { error } = await supabase.from("user_roles").delete().eq("user_id", memberId).eq("role", "admin");
|
||||||
if (error) toast({ title: 'Error', description: 'Gagal menghapus role admin', variant: 'destructive' });
|
if (error) toast({ title: "Error", description: "Gagal menghapus role admin", variant: "destructive" });
|
||||||
else { toast({ title: 'Berhasil', description: 'Role admin dihapus' }); fetchMembers(); }
|
else {
|
||||||
|
toast({ title: "Berhasil", description: "Role admin dihapus" });
|
||||||
|
fetchMembers();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const { error } = await supabase.from('user_roles').insert({ user_id: memberId, role: 'admin' });
|
const { error } = await supabase.from("user_roles").insert({ user_id: memberId, role: "admin" });
|
||||||
if (error) toast({ title: 'Error', description: error.message, variant: 'destructive' });
|
if (error) toast({ title: "Error", description: error.message, variant: "destructive" });
|
||||||
else { toast({ title: 'Berhasil', description: 'Role admin ditambahkan' }); fetchMembers(); }
|
else {
|
||||||
|
toast({ title: "Berhasil", description: "Role admin ditambahkan" });
|
||||||
|
fetchMembers();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -114,13 +117,13 @@ export default function AdminMembers() {
|
|||||||
<TableBody>
|
<TableBody>
|
||||||
{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.full_name || "-"}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
{adminIds.has(member.id) ? (
|
{adminIds.has(member.id) ? (
|
||||||
<Badge className="bg-primary">Admin</Badge>
|
<Badge className="bg-primary">Admin</Badge>
|
||||||
) : (
|
) : (
|
||||||
<Badge className="bg-secondary">Member</Badge>
|
<Badge className="bg-secondary text-primary">Member</Badge>
|
||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>{formatDateTime(member.created_at)}</TableCell>
|
<TableCell>{formatDateTime(member.created_at)}</TableCell>
|
||||||
@@ -159,9 +162,15 @@ export default function AdminMembers() {
|
|||||||
{selectedMember && (
|
{selectedMember && (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="text-sm space-y-1">
|
<div className="text-sm space-y-1">
|
||||||
<p><span className="text-muted-foreground">Email:</span> {selectedMember.email}</p>
|
<p>
|
||||||
<p><span className="text-muted-foreground">Nama:</span> {selectedMember.full_name || '-'}</p>
|
<span className="text-muted-foreground">Email:</span> {selectedMember.email}
|
||||||
<p><span className="text-muted-foreground">ID:</span> {selectedMember.id}</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<span className="text-muted-foreground">Nama:</span> {selectedMember.full_name || "-"}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span className="text-muted-foreground">ID:</span> {selectedMember.id}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="border-t border-border pt-4">
|
<div className="border-t border-border pt-4">
|
||||||
<p className="font-medium mb-2">Akses Produk:</p>
|
<p className="font-medium mb-2">Akses Produk:</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user