feat: modernize admin dashboard with premium sports design
This commit is contained in:
parent
fda66a7703
commit
e84d0f5f8f
@ -32,7 +32,7 @@
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 222.2 84% 4.9%;
|
||||
|
||||
--radius: 0.5rem;
|
||||
--radius: 0.75rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
|
||||
@ -25,11 +25,34 @@ export default function RootLayout({
|
||||
return (
|
||||
<ClerkProvider>
|
||||
<html lang="en">
|
||||
<body className={inter.className}>
|
||||
<div className="flex min-h-screen bg-slate-50">
|
||||
<body className={`${inter.className} bg-gradient-to-br from-slate-50 via-blue-50 to-slate-100 min-h-screen`}>
|
||||
<div className="flex min-h-screen">
|
||||
<Sidebar />
|
||||
<main className="flex-1 ml-64 p-8">
|
||||
{children}
|
||||
<main className="flex-1 ml-64">
|
||||
<div className="sticky top-0 z-40 backdrop-blur-xl bg-white/75 border-b border-slate-200/50 shadow-sm">
|
||||
<div className="px-8 py-4 flex items-center justify-between max-w-7xl mx-auto">
|
||||
<div>
|
||||
<h1 className="text-sm font-semibold text-gray-600 uppercase tracking-wide">FitAI Pro</h1>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<SignedIn>
|
||||
<UserButton
|
||||
appearance={{
|
||||
elements: {
|
||||
avatarBox: "w-10 h-10 rounded-full ring-2 ring-blue-200"
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</SignedIn>
|
||||
<SignedOut>
|
||||
<SignInButton mode="modal" />
|
||||
</SignedOut>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-8">
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@ -46,56 +46,78 @@ export default function Home() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div>
|
||||
<h2 className="text-3xl font-bold text-slate-900">Dashboard</h2>
|
||||
<p className="text-slate-500 mt-2">Welcome back, here's what's happening today.</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<StatsCard
|
||||
title="Total Users"
|
||||
value={loading ? "..." : stats.totalUsers}
|
||||
change="+12%" // Placeholder for now as we don't track historical growth yet
|
||||
trend="up"
|
||||
icon={Users}
|
||||
color="blue"
|
||||
/>
|
||||
<StatsCard
|
||||
title="Active Clients"
|
||||
value={loading ? "..." : stats.activeClients}
|
||||
change="+5%"
|
||||
trend="up"
|
||||
icon={CalendarCheck}
|
||||
color="green"
|
||||
/>
|
||||
<StatsCard
|
||||
title="Revenue"
|
||||
value={loading ? "..." : formatCurrency(stats.totalRevenue)}
|
||||
change={`${stats.revenueGrowth > 0 ? "+" : ""}${stats.revenueGrowth}%`}
|
||||
trend={stats.revenueGrowth >= 0 ? "up" : "down"}
|
||||
icon={CreditCard}
|
||||
color="purple"
|
||||
/>
|
||||
<StatsCard
|
||||
title="Growth"
|
||||
value="24%" // Placeholder
|
||||
change="-2%"
|
||||
trend="down"
|
||||
icon={TrendingUp}
|
||||
color="orange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
<div className="lg:col-span-2 bg-white rounded-xl shadow-sm border border-slate-100 p-6">
|
||||
<h3 className="text-xl font-bold text-slate-900 mb-6">Recent Activity</h3>
|
||||
<UserManagement />
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-blue-50 to-slate-100">
|
||||
<div className="space-y-8 max-w-7xl">
|
||||
{/* Hero Section */}
|
||||
<div className="pt-8 pb-4">
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="h-12 w-1 bg-gradient-to-b from-blue-600 to-cyan-600 rounded-full"></div>
|
||||
<h1 className="text-5xl font-black bg-gradient-to-r from-blue-600 via-blue-700 to-cyan-600 bg-clip-text text-transparent">
|
||||
FitAI Dashboard
|
||||
</h1>
|
||||
</div>
|
||||
<p className="text-lg text-gray-600 ml-4">Performance metrics & athlete insights</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-xl shadow-sm border border-slate-100 p-6">
|
||||
<h3 className="text-xl font-bold text-slate-900 mb-6">Quick Analytics</h3>
|
||||
<AnalyticsDashboard />
|
||||
{/* Stats Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-5">
|
||||
<StatsCard
|
||||
title="Total Users"
|
||||
value={loading ? "..." : stats.totalUsers}
|
||||
change="+12%" // Placeholder for now as we don't track historical growth yet
|
||||
trend="up"
|
||||
icon={Users}
|
||||
color="blue"
|
||||
/>
|
||||
<StatsCard
|
||||
title="Active Clients"
|
||||
value={loading ? "..." : stats.activeClients}
|
||||
change="+5%"
|
||||
trend="up"
|
||||
icon={CalendarCheck}
|
||||
color="green"
|
||||
/>
|
||||
<StatsCard
|
||||
title="Revenue"
|
||||
value={loading ? "..." : formatCurrency(stats.totalRevenue)}
|
||||
change={`${stats.revenueGrowth > 0 ? "+" : ""}${stats.revenueGrowth}%`}
|
||||
trend={stats.revenueGrowth >= 0 ? "up" : "down"}
|
||||
icon={CreditCard}
|
||||
color="purple"
|
||||
/>
|
||||
<StatsCard
|
||||
title="Growth"
|
||||
value="24%" // Placeholder
|
||||
change="-2%"
|
||||
trend="down"
|
||||
icon={TrendingUp}
|
||||
color="orange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Main Content Grid */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 pb-12">
|
||||
<div className="lg:col-span-2 bg-white rounded-2xl shadow-lg border border-slate-200/80 p-7 hover:shadow-xl transition-shadow">
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-slate-900">Active Athletes</h2>
|
||||
<p className="text-sm text-gray-500 mt-1">Manage and monitor your fitness clients</p>
|
||||
</div>
|
||||
<UserManagement />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-slate-200/80 p-7 hover:shadow-xl transition-shadow">
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-slate-900">Performance</h2>
|
||||
<p className="text-sm text-gray-500 mt-1">Quick insights & analytics</p>
|
||||
</div>
|
||||
<AnalyticsDashboard />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -67,74 +67,71 @@ export function AnalyticsDashboard() {
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex justify-center items-center h-64">
|
||||
<div className="text-lg">Loading analytics...</div>
|
||||
<div className="text-center">
|
||||
<div className="inline-block animate-spin">⚡</div>
|
||||
<p className="text-gray-600 mt-2 text-sm">Loading analytics...</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<h2 className="text-2xl font-bold">Analytics Dashboard</h2>
|
||||
{/* Key Metrics Cards */}
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
<div className="bg-gradient-to-br from-blue-50 to-cyan-50 rounded-xl p-5 border border-blue-200/30 hover:border-blue-200 transition-colors">
|
||||
<p className="text-xs uppercase tracking-wide font-semibold text-gray-600 mb-2">Total Athletes</p>
|
||||
<div className="flex items-baseline gap-2">
|
||||
<div className="text-3xl font-black bg-gradient-to-r from-blue-600 to-cyan-600 bg-clip-text text-transparent">{totalUsers}</div>
|
||||
<span className="text-sm text-gray-500">active</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Key Metrics */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<Card>
|
||||
<CardContent>
|
||||
<div className="text-center">
|
||||
<div className="text-3xl font-bold text-blue-600">{totalUsers}</div>
|
||||
<div className="text-gray-600">Total Users</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="bg-gradient-to-br from-emerald-50 to-teal-50 rounded-xl p-5 border border-emerald-200/30 hover:border-emerald-200 transition-colors">
|
||||
<p className="text-xs uppercase tracking-wide font-semibold text-gray-600 mb-2">Total Revenue</p>
|
||||
<div className="flex items-baseline gap-2">
|
||||
<div className="text-3xl font-black bg-gradient-to-r from-emerald-600 to-teal-600 bg-clip-text text-transparent">${totalRevenue.toLocaleString()}</div>
|
||||
<span className="text-sm text-gray-500">ytd</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<CardContent>
|
||||
<div className="text-center">
|
||||
<div className="text-3xl font-bold text-green-600">${totalRevenue.toLocaleString()}</div>
|
||||
<div className="text-gray-600">Total Revenue</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent>
|
||||
<div className="text-center">
|
||||
<div className="text-3xl font-bold text-purple-600">{activeMembers}</div>
|
||||
<div className="text-gray-600">Active Members</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="bg-gradient-to-br from-purple-50 to-pink-50 rounded-xl p-5 border border-purple-200/30 hover:border-purple-200 transition-colors">
|
||||
<p className="text-xs uppercase tracking-wide font-semibold text-gray-600 mb-2">Active Members</p>
|
||||
<div className="flex items-baseline gap-2">
|
||||
<div className="text-3xl font-black bg-gradient-to-r from-purple-600 to-pink-600 bg-clip-text text-transparent">{activeMembers}</div>
|
||||
<span className="text-sm text-gray-500">members</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Charts */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<h3 className="text-lg font-semibold">User Growth</h3>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<UserGrowthChart data={userGrowthData} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
{/* Charts Grid */}
|
||||
<div className="grid grid-cols-1 gap-5">
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-slate-200 p-5 hover:shadow-xl transition-shadow">
|
||||
<div className="mb-4">
|
||||
<h3 className="text-lg font-bold text-gray-900">User Growth Trend</h3>
|
||||
<p className="text-sm text-gray-500">Last 6 months performance</p>
|
||||
</div>
|
||||
<UserGrowthChart data={userGrowthData} />
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<h3 className="text-lg font-semibold">Membership Distribution</h3>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-2 gap-5">
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-slate-200 p-5 hover:shadow-xl transition-shadow">
|
||||
<div className="mb-4">
|
||||
<h3 className="text-lg font-bold text-gray-900">Membership Mix</h3>
|
||||
<p className="text-sm text-gray-500">Distribution breakdown</p>
|
||||
</div>
|
||||
<MembershipDistributionChart data={membershipData} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<h3 className="text-lg font-semibold">Monthly Revenue</h3>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<RevenueChart data={revenueData} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-slate-200 p-5 hover:shadow-xl transition-shadow">
|
||||
<div className="mb-4">
|
||||
<h3 className="text-lg font-bold text-gray-900">Revenue Stream</h3>
|
||||
<p className="text-sm text-gray-500">Monthly earnings</p>
|
||||
</div>
|
||||
<RevenueChart data={revenueData} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -65,14 +65,24 @@ export function Sidebar() {
|
||||
];
|
||||
|
||||
return (
|
||||
<aside className="w-64 bg-slate-900 text-white h-screen fixed left-0 top-0 flex flex-col border-r border-slate-800">
|
||||
<div className="p-6 border-b border-slate-800">
|
||||
<h1 className="text-2xl font-bold bg-gradient-to-r from-blue-400 to-indigo-400 bg-clip-text text-transparent">
|
||||
FitAI Admin
|
||||
</h1>
|
||||
<aside className="w-64 bg-gradient-to-b from-slate-900 via-slate-900 to-slate-950 text-white h-screen fixed left-0 top-0 flex flex-col border-r border-slate-800/50 shadow-2xl">
|
||||
{/* Logo Section */}
|
||||
<div className="p-6 border-b border-slate-800/50">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="h-10 w-10 rounded-xl bg-gradient-to-br from-blue-500 to-cyan-500 flex items-center justify-center font-bold text-white shadow-lg">
|
||||
⚡
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-lg font-black bg-gradient-to-r from-blue-400 via-cyan-400 to-blue-300 bg-clip-text text-transparent">
|
||||
FitAI
|
||||
</h1>
|
||||
<p className="text-xs text-slate-400 font-semibold tracking-wide">ADMIN PRO</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav className="flex-1 p-4 space-y-2">
|
||||
{/* Navigation */}
|
||||
<nav className="flex-1 p-4 space-y-1 overflow-y-auto">
|
||||
{menuItems.map((item) => {
|
||||
const Icon = item.icon;
|
||||
const isActive = pathname === item.href;
|
||||
@ -81,23 +91,36 @@ export function Sidebar() {
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={`flex items-center gap-3 px-4 py-3 rounded-lg transition-all duration-200 group ${isActive
|
||||
? "bg-blue-600 text-white shadow-lg shadow-blue-900/20"
|
||||
: "text-slate-400 hover:bg-slate-800 hover:text-white"}`}
|
||||
className={`flex items-center gap-3 px-4 py-3 rounded-xl transition-all duration-200 group relative overflow-hidden ${isActive
|
||||
? "bg-gradient-to-r from-blue-600 to-cyan-600 text-white shadow-lg shadow-blue-900/30"
|
||||
: "text-slate-400 hover:bg-slate-800/40 hover:text-white"}`}
|
||||
>
|
||||
<Icon size={20} className={isActive ? "text-white" : "text-slate-500 group-hover:text-white"} />
|
||||
<span className="font-medium">{label}</span>
|
||||
{isActive && <div className="absolute inset-0 bg-white/10 blur-xl"></div>}
|
||||
<Icon size={20} className={`${isActive ? "text-white" : "text-slate-500 group-hover:text-white"} relative z-10`} />
|
||||
<span className="font-semibold relative z-10">{label}</span>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
|
||||
<div className="p-4 border-t border-slate-800">
|
||||
<div className="flex items-center gap-3 px-4 py-3 rounded-lg bg-slate-800/50">
|
||||
<UserButton afterSignOutUrl="/" />
|
||||
{/* User Section */}
|
||||
<div className="p-4 border-t border-slate-800/50 bg-slate-800/20">
|
||||
<div className="flex items-center gap-3 px-3 py-3 rounded-xl bg-slate-800/50 hover:bg-slate-800/70 transition-colors">
|
||||
<div className="h-10 w-10 rounded-full bg-gradient-to-br from-blue-500 to-cyan-500 flex-shrink-0 overflow-hidden">
|
||||
<UserButton afterSignOutUrl="/" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium text-white truncate">
|
||||
{user?.fullName || "Admin User"}
|
||||
<p className="text-sm font-semibold text-white truncate">
|
||||
{user?.fullName || "Admin"}
|
||||
</p>
|
||||
<p className="text-xs text-slate-400 truncate">
|
||||
{user?.emailAddresses[0]?.emailAddress || "admin@fitai.com"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
</p>
|
||||
<p className="text-xs text-slate-400 truncate">
|
||||
{user?.primaryEmailAddress?.emailAddress}
|
||||
|
||||
@ -12,38 +12,62 @@ interface StatsCardProps {
|
||||
|
||||
export function StatsCard({ title, value, change, trend, icon: Icon, color = "blue" }: StatsCardProps) {
|
||||
const colorStyles = {
|
||||
blue: "bg-blue-50 text-blue-600",
|
||||
green: "bg-green-50 text-green-600",
|
||||
purple: "bg-purple-50 text-purple-600",
|
||||
orange: "bg-orange-50 text-orange-600",
|
||||
blue: {
|
||||
bg: "bg-gradient-to-br from-blue-600 to-blue-700",
|
||||
iconBg: "bg-blue-500/20 text-blue-300",
|
||||
light: "from-blue-50 to-blue-100/50",
|
||||
},
|
||||
green: {
|
||||
bg: "bg-gradient-to-br from-emerald-600 to-emerald-700",
|
||||
iconBg: "bg-emerald-500/20 text-emerald-300",
|
||||
light: "from-emerald-50 to-emerald-100/50",
|
||||
},
|
||||
purple: {
|
||||
bg: "bg-gradient-to-br from-purple-600 to-purple-700",
|
||||
iconBg: "bg-purple-500/20 text-purple-300",
|
||||
light: "from-purple-50 to-purple-100/50",
|
||||
},
|
||||
orange: {
|
||||
bg: "bg-gradient-to-br from-orange-600 to-orange-700",
|
||||
iconBg: "bg-orange-500/20 text-orange-300",
|
||||
light: "from-orange-50 to-orange-100/50",
|
||||
},
|
||||
};
|
||||
|
||||
const styles = colorStyles[color];
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-sm font-medium text-muted-foreground">
|
||||
<Card className={`bg-gradient-to-br ${styles.light} border-0 shadow-lg hover:shadow-xl transition-all duration-300 overflow-hidden group`}>
|
||||
<div className={`absolute inset-0 opacity-0 group-hover:opacity-5 ${styles.bg} transition-opacity`}></div>
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-3 relative z-10">
|
||||
<CardTitle className="text-sm font-semibold text-gray-700 uppercase tracking-wide">
|
||||
{title}
|
||||
</CardTitle>
|
||||
<div className={`p-2 rounded-lg ${colorStyles[color]}`}>
|
||||
<Icon size={16} />
|
||||
<div className={`p-3 rounded-2xl ${styles.iconBg} backdrop-blur-sm`}>
|
||||
<Icon size={20} />
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">{value}</div>
|
||||
<CardContent className="relative z-10">
|
||||
<div className={`text-4xl font-black bg-gradient-to-r ${styles.bg.replace('bg-gradient-to-br', '')} bg-clip-text text-transparent`}>
|
||||
{value}
|
||||
</div>
|
||||
{change && (
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
<span
|
||||
className={`font-medium ${trend === "up"
|
||||
? "text-green-600"
|
||||
: trend === "down"
|
||||
? "text-red-600"
|
||||
: "text-slate-600"
|
||||
<div className="flex items-center gap-2 mt-3">
|
||||
<p className="text-xs text-gray-600 font-medium">
|
||||
<span
|
||||
className={`font-bold px-2 py-1 rounded-full text-xs ${
|
||||
trend === "up"
|
||||
? "bg-emerald-100 text-emerald-700"
|
||||
: trend === "down"
|
||||
? "bg-red-100 text-red-700"
|
||||
: "bg-gray-100 text-gray-700"
|
||||
}`}
|
||||
>
|
||||
{change}
|
||||
</span>{" "}
|
||||
vs last month
|
||||
</p>
|
||||
>
|
||||
{trend === "up" ? "↑" : trend === "down" ? "↓" : "→"} {change}
|
||||
</span>
|
||||
</p>
|
||||
<span className="text-xs text-gray-500">vs last month</span>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@ -265,45 +265,72 @@ export function UserGrid({
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search users..."
|
||||
className="border border-gray-300 rounded px-4 py-2"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
/>
|
||||
<div className="flex gap-2">
|
||||
<div className="space-y-4">
|
||||
{/* Search and Actions Bar */}
|
||||
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 bg-gradient-to-r from-slate-50 to-blue-50 p-4 rounded-xl border border-slate-200">
|
||||
<div className="w-full sm:w-auto flex-1">
|
||||
<div className="relative">
|
||||
<svg className="absolute left-3 top-3 w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||
</svg>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search athletes..."
|
||||
className="w-full pl-10 pr-4 py-2.5 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white shadow-sm"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2 w-full sm:w-auto">
|
||||
<button
|
||||
className="bg-green-500 text-white px-4 py-2 rounded disabled:opacity-50"
|
||||
className="flex-1 sm:flex-none bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white px-4 py-2.5 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed font-semibold transition-all shadow-md hover:shadow-lg"
|
||||
onClick={handleEdit}
|
||||
disabled={selectedUsers.length !== 1}
|
||||
>
|
||||
Edit User
|
||||
✏️ Edit
|
||||
</button>
|
||||
<button
|
||||
className="bg-red-500 text-white px-4 py-2 rounded disabled:opacity-50"
|
||||
className="flex-1 sm:flex-none bg-gradient-to-r from-red-500 to-red-600 hover:from-red-600 hover:to-red-700 text-white px-4 py-2.5 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed font-semibold transition-all shadow-md hover:shadow-lg"
|
||||
onClick={handleDelete}
|
||||
disabled={selectedUsers.length !== 1}
|
||||
>
|
||||
Delete User
|
||||
🗑️ Delete
|
||||
</button>
|
||||
<button
|
||||
className="bg-yellow-500 text-white px-4 py-2 rounded disabled:opacity-50"
|
||||
className="flex-1 sm:flex-none bg-gradient-to-r from-orange-500 to-orange-600 hover:from-orange-600 hover:to-orange-700 text-white px-4 py-2.5 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed font-semibold transition-all shadow-md hover:shadow-lg"
|
||||
onClick={handleBulkDelete}
|
||||
disabled={selectedUsers.length === 0}
|
||||
>
|
||||
Bulk Delete
|
||||
⚡ Bulk
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ag-theme-alpine"
|
||||
style={{ height: "600px", width: "100%" }}
|
||||
>
|
||||
<AgGridReact<User> {...gridOptions} ref={gridRef} />
|
||||
|
||||
{/* Table */}
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-slate-200 overflow-hidden">
|
||||
<div
|
||||
className="ag-theme-alpine"
|
||||
style={{ height: "600px", width: "100%" }}
|
||||
>
|
||||
<AgGridReact<User> {...gridOptions} ref={gridRef} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Selected Count */}
|
||||
{selectedUsers.length > 0 && (
|
||||
<div className="flex items-center justify-between bg-gradient-to-r from-blue-500/10 to-cyan-500/10 border border-blue-200/50 px-4 py-3 rounded-lg">
|
||||
<p className="text-sm font-semibold text-blue-700">
|
||||
{selectedUsers.length} athlete{selectedUsers.length !== 1 ? 's' : ''} selected
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setSelectedUsers([])}
|
||||
className="text-sm text-blue-600 hover:text-blue-700 font-medium"
|
||||
>
|
||||
Clear selection
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user