Yimaru-Admin/src/pages/user-management/UserManagementDashboard.tsx
2026-03-06 06:02:02 -08:00

166 lines
7.3 KiB
TypeScript

import { useEffect, useState } from "react"
import { Link } from "react-router-dom"
import {
Users,
UserPlus,
UserCheck,
TrendingUp,
ArrowRight,
List,
UsersRound,
Loader2,
} from "lucide-react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../../components/ui/card"
import { getUserSummary } from "../../api/users.api"
import type { UserSummary } from "../../types/user.types"
export function UserManagementDashboard() {
const [stats, setStats] = useState<UserSummary | null>(null)
const [statsLoading, setStatsLoading] = useState(true)
useEffect(() => {
const fetchStats = async () => {
try {
const res = await getUserSummary()
setStats(res.data.data)
} catch {
// silently fail — cards will show "—"
} finally {
setStatsLoading(false)
}
}
fetchStats()
}, [])
const formatNum = (n: number) => n.toLocaleString()
return (
<div className="space-y-8">
{/* Page Header */}
<div>
<h1 className="text-2xl font-bold text-grayScale-600">User Management</h1>
<p className="mt-1 text-sm text-grayScale-400">
Manage users, groups, and registrations.
</p>
</div>
{/* Stat Cards */}
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
<Card className="border-none bg-brand-50 shadow-sm">
<CardContent className="flex items-center gap-4 p-5">
<div className="grid h-12 w-12 shrink-0 place-items-center rounded-xl bg-brand-100 text-brand-600">
<Users className="h-6 w-6" />
</div>
<div className="min-w-0">
<p className="text-sm font-medium text-white/80">Total Users</p>
<p className="text-2xl font-bold text-white">
{statsLoading ? <Loader2 className="h-5 w-5 animate-spin text-white" /> : stats ? formatNum(stats.total_users) : "—"}
</p>
</div>
</CardContent>
</Card>
<Card className="border-none bg-brand-50 shadow-sm">
<CardContent className="flex items-center gap-4 p-5">
<div className="grid h-12 w-12 shrink-0 place-items-center rounded-xl bg-brand-100 text-brand-600">
<UserCheck className="h-6 w-6" />
</div>
<div className="min-w-0">
<p className="text-sm font-medium text-white/80">Active Users</p>
<p className="text-2xl font-bold text-white">
{statsLoading ? <Loader2 className="h-5 w-5 animate-spin text-white" /> : stats ? formatNum(stats.active_users) : "—"}
</p>
</div>
</CardContent>
</Card>
<Card className="border-none bg-brand-50 shadow-sm sm:col-span-2 lg:col-span-1">
<CardContent className="flex items-center gap-4 p-5">
<div className="grid h-12 w-12 shrink-0 place-items-center rounded-xl bg-brand-100 text-brand-600">
<TrendingUp className="h-6 w-6" />
</div>
<div className="min-w-0">
<p className="text-sm font-medium text-white/80">New This Month</p>
<p className="text-2xl font-bold text-white">
{statsLoading ? <Loader2 className="h-5 w-5 animate-spin text-white" /> : stats ? formatNum(stats.joined_this_month) : "—"}
</p>
</div>
</CardContent>
</Card>
</div>
{/* Action Cards */}
<div>
<h2 className="mb-4 text-lg font-semibold text-grayScale-600">Quick Actions</h2>
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
<Link to="/users/register" className="group">
<Card className="h-full border border-grayScale-100 shadow-sm transition-all duration-200 group-hover:border-brand-200 group-hover:shadow-md">
<CardHeader className="pb-3">
<div className="mb-3 grid h-11 w-11 place-items-center rounded-lg bg-brand-100 text-brand-600 transition-colors group-hover:bg-brand-500 group-hover:text-white">
<UserPlus className="h-5 w-5" />
</div>
<CardTitle className="text-base font-semibold text-grayScale-600">
Register User
</CardTitle>
<CardDescription className="text-sm text-grayScale-400">
Add new users to the system with role assignment.
</CardDescription>
</CardHeader>
<CardContent>
<span className="inline-flex items-center gap-1.5 text-sm font-medium text-brand-500 transition-colors group-hover:text-brand-600">
Get started
<ArrowRight className="h-4 w-4 transition-transform group-hover:translate-x-1" />
</span>
</CardContent>
</Card>
</Link>
<Link to="/users/groups" className="group">
<Card className="h-full border border-grayScale-100 shadow-sm transition-all duration-200 group-hover:border-brand-200 group-hover:shadow-md">
<CardHeader className="pb-3">
<div className="mb-3 grid h-11 w-11 place-items-center rounded-lg bg-brand-100 text-brand-600 transition-colors group-hover:bg-brand-500 group-hover:text-white">
<UsersRound className="h-5 w-5" />
</div>
<CardTitle className="text-base font-semibold text-grayScale-600">
User Groups
</CardTitle>
<CardDescription className="text-sm text-grayScale-400">
Manage groups, roles, and permission settings.
</CardDescription>
</CardHeader>
<CardContent>
<span className="inline-flex items-center gap-1.5 text-sm font-medium text-brand-500 transition-colors group-hover:text-brand-600">
Manage groups
<ArrowRight className="h-4 w-4 transition-transform group-hover:translate-x-1" />
</span>
</CardContent>
</Card>
</Link>
<Link to="/users/list" className="group sm:col-span-2 lg:col-span-1">
<Card className="h-full border border-grayScale-100 shadow-sm transition-all duration-200 group-hover:border-brand-200 group-hover:shadow-md">
<CardHeader className="pb-3">
<div className="mb-3 grid h-11 w-11 place-items-center rounded-lg bg-brand-100 text-brand-600 transition-colors group-hover:bg-brand-500 group-hover:text-white">
<List className="h-5 w-5" />
</div>
<CardTitle className="text-base font-semibold text-grayScale-600">
User List
</CardTitle>
<CardDescription className="text-sm text-grayScale-400">
Browse, search, and manage all registered users.
</CardDescription>
</CardHeader>
<CardContent>
<span className="inline-flex items-center gap-1.5 text-sm font-medium text-brand-500 transition-colors group-hover:text-brand-600">
View all users
<ArrowRight className="h-4 w-4 transition-transform group-hover:translate-x-1" />
</span>
</CardContent>
</Card>
</Link>
</div>
</div>
</div>
)
}