212 lines
7.7 KiB
TypeScript
212 lines
7.7 KiB
TypeScript
import { json } from "@remix-run/node";
|
|
import { useLoaderData } from "@remix-run/react";
|
|
import { Users, UserCheck, UserX, MoreHorizontal, Eye, Edit, Trash2 } from "lucide-react";
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/components/ui/card";
|
|
import { Button } from "~/components/ui/button";
|
|
import { Badge } from "~/components/ui/badge";
|
|
import { Avatar, AvatarFallback, AvatarImage } from "~/components/ui/avatar";
|
|
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from "~/components/ui/table";
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuTrigger,
|
|
} from "~/components/ui/dropdown-menu";
|
|
|
|
export const loader = async () => {
|
|
const userData = {
|
|
summary: {
|
|
totalUsers: 1234,
|
|
activeUsers: 1100,
|
|
pendingVerification: 15,
|
|
collectors: 45
|
|
},
|
|
users: [
|
|
{
|
|
id: 1,
|
|
name: "Ahmad Rizki",
|
|
email: "ahmad.rizki@example.com",
|
|
role: "Masyarakat",
|
|
status: "active",
|
|
joinDate: "2024-01-15",
|
|
totalTransactions: 25,
|
|
avatar: ""
|
|
},
|
|
{
|
|
id: 2,
|
|
name: "Siti Nurhaliza",
|
|
email: "siti.nurhaliza@example.com",
|
|
role: "Pengepul",
|
|
status: "pending",
|
|
joinDate: "2024-06-20",
|
|
totalTransactions: 0,
|
|
avatar: ""
|
|
},
|
|
{
|
|
id: 3,
|
|
name: "Budi Santoso",
|
|
email: "budi.santoso@example.com",
|
|
role: "Masyarakat",
|
|
status: "active",
|
|
joinDate: "2024-03-10",
|
|
totalTransactions: 42,
|
|
avatar: ""
|
|
}
|
|
]
|
|
};
|
|
|
|
return json({ userData });
|
|
};
|
|
|
|
export default function UserManagement() {
|
|
const { userData } = useLoaderData<typeof loader>();
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Header */}
|
|
<div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
|
<div>
|
|
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">
|
|
Manajemen Pengguna
|
|
</h1>
|
|
<p className="text-gray-600 dark:text-gray-400 mt-1">
|
|
Kelola semua pengguna platform RIjig
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Summary Cards */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
|
<CardTitle className="text-sm font-medium">Total Pengguna</CardTitle>
|
|
<Users className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">{userData.summary.totalUsers.toLocaleString()}</div>
|
|
<p className="text-xs text-muted-foreground">terdaftar di platform</p>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
|
<CardTitle className="text-sm font-medium">Pengguna Aktif</CardTitle>
|
|
<UserCheck className="h-4 w-4 text-green-600" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold text-green-600">{userData.summary.activeUsers.toLocaleString()}</div>
|
|
<p className="text-xs text-muted-foreground">pengguna terverifikasi</p>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
|
<CardTitle className="text-sm font-medium">Menunggu Verifikasi</CardTitle>
|
|
<UserX className="h-4 w-4 text-orange-600" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold text-orange-600">{userData.summary.pendingVerification}</div>
|
|
<p className="text-xs text-muted-foreground">perlu ditinjau</p>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
|
<CardTitle className="text-sm font-medium">Total Pengepul</CardTitle>
|
|
<Users className="h-4 w-4 text-blue-600" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold text-blue-600">{userData.summary.collectors}</div>
|
|
<p className="text-xs text-muted-foreground">pengepul aktif</p>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
{/* Users Table */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Daftar Pengguna</CardTitle>
|
|
<CardDescription>Semua pengguna yang terdaftar di platform</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead>Pengguna</TableHead>
|
|
<TableHead>Email</TableHead>
|
|
<TableHead>Role</TableHead>
|
|
<TableHead>Status</TableHead>
|
|
<TableHead>Tanggal Bergabung</TableHead>
|
|
<TableHead>Total Transaksi</TableHead>
|
|
<TableHead className="text-right">Aksi</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{userData.users.map((user) => (
|
|
<TableRow key={user.id}>
|
|
<TableCell>
|
|
<div className="flex items-center gap-3">
|
|
<Avatar className="h-8 w-8">
|
|
<AvatarImage src={user.avatar} alt={user.name} />
|
|
<AvatarFallback>
|
|
{user.name.split(' ').map(n => n[0]).join('')}
|
|
</AvatarFallback>
|
|
</Avatar>
|
|
<div>
|
|
<div className="font-medium">{user.name}</div>
|
|
</div>
|
|
</div>
|
|
</TableCell>
|
|
<TableCell className="text-gray-600">{user.email}</TableCell>
|
|
<TableCell>
|
|
<Badge variant="outline">{user.role}</Badge>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Badge
|
|
variant={user.status === "active" ? "default" : "secondary"}
|
|
className={user.status === "active" ? "bg-green-100 text-green-800" : "bg-orange-100 text-orange-800"}
|
|
>
|
|
{user.status === "active" ? "Aktif" : "Pending"}
|
|
</Badge>
|
|
</TableCell>
|
|
<TableCell>{new Date(user.joinDate).toLocaleDateString('id-ID')}</TableCell>
|
|
<TableCell>{user.totalTransactions}</TableCell>
|
|
<TableCell className="text-right">
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button variant="ghost" className="h-8 w-8 p-0">
|
|
<MoreHorizontal className="h-4 w-4" />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end">
|
|
<DropdownMenuItem className="gap-2">
|
|
<Eye className="h-4 w-4" />
|
|
Lihat Detail
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem className="gap-2">
|
|
<Edit className="h-4 w-4" />
|
|
Edit
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem className="gap-2 text-red-600">
|
|
<Trash2 className="h-4 w-4" />
|
|
Hapus
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
} |