MIF_E31221222/sigap-website/app/_components/admin/users/user-stats.tsx

109 lines
3.1 KiB
TypeScript

"use client";
import { useQuery } from "@tanstack/react-query";
import { Card, CardContent } from "@/app/_components/ui/card";
import { Users, UserCheck, UserX } from "lucide-react";
import { fetchUsers } from "@/app/(protected)/(admin)/dashboard/user-management/action";
import { User } from "@/src/models/users/users.model";
import { useNavigations } from "@/app/_hooks/use-navigations";
import { useEffect, useState } from "react";
import { toast } from "sonner";
function calculateUserStats(users: User[]) {
const totalUsers = users.length;
const activeUsers = users.filter(
(user) => !user.banned_until && user.email_confirmed_at
).length;
const inactiveUsers = totalUsers - activeUsers;
return {
totalUsers,
activeUsers,
inactiveUsers,
activePercentage:
totalUsers > 0 ? ((activeUsers / totalUsers) * 100).toFixed(1) : "0.0",
inactivePercentage:
totalUsers > 0 ? ((inactiveUsers / totalUsers) * 100).toFixed(1) : "0.0",
};
}
export function UserStats() {
const { isLoading, setIsLoading } = useNavigations();
const [users, setUsers] = useState<User[]>([]);
useEffect(() => {
const fetchUserData = async () => {
try {
setIsLoading(true);
const fetchedUsers = await fetchUsers();
setUsers(fetchedUsers);
} catch (error) {
toast.error("Failed to fetch users");
} finally {
setIsLoading(false);
}
};
fetchUserData();
}, [setIsLoading]);
const stats = calculateUserStats(users);
if (isLoading) {
return (
<>
{[...Array(3)].map((_, i) => (
<Card key={i} className="bg-background border-border">
<CardContent className="p-6">
<div className="space-y-4 animate-pulse">
<div className="h-5 w-24 bg-muted rounded" />
<div className="h-8 w-16 bg-muted rounded" />
<div className="h-4 w-32 bg-muted rounded" />
</div>
</CardContent>
</Card>
))}
</>
);
}
const cards = [
{
title: "Total Users",
value: stats.totalUsers,
subtitle: "Updated just now",
icon: Users,
},
{
title: "Active Users",
value: stats.activeUsers,
subtitle: `${stats.activePercentage}% of total users`,
icon: UserCheck,
},
{
title: "Inactive Users",
value: stats.inactiveUsers,
subtitle: `${stats.inactivePercentage}% of total users`,
icon: UserX,
},
];
return (
<>
{cards.map((card, index) => (
<Card key={index} className="bg-background border-border">
<CardContent className="p-6">
<div className="flex items-center justify-between mb-4">
<div className="font-medium text-sm text-muted-foreground">
{card.title}
</div>
<card.icon className="h-4 w-4 text-muted-foreground" />
</div>
<div className="text-3xl font-bold mb-2">{card.value}</div>
<div className="text-sm text-muted-foreground">{card.subtitle}</div>
</CardContent>
</Card>
))}
</>
);
}