Skip to content

Commit c2748d1

Browse files
authored
Merge pull request #42 from codeunia-dev/admin/usercount
Enhancement: Integrate total users count and trend analysis in Admindashboard
2 parents 2c5d8a4 + ba5439c commit c2748d1

2 files changed

Lines changed: 59 additions & 9 deletions

File tree

app/admin/page.tsx

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ import {
2020
ArrowDownRight,
2121
} from "lucide-react"
2222

23+
type SupabaseUser = {
24+
created_at: string;
25+
};
26+
2327
const dashboardStats = [
2428
{
2529
title: "Total Users",
@@ -151,16 +155,51 @@ const systemHealth = [
151155
export default function AdminDashboard() {
152156
const [currentTime, setCurrentTime] = useState(new Date())
153157
const [animatedStats, setAnimatedStats] = useState(dashboardStats.map(() => 0))
158+
const [totalUsers, setTotalUsers] = useState<string | null>(null)
159+
const [totalUsersChange, setTotalUsersChange] = useState<string>("")
160+
const [totalUsersTrend, setTotalUsersTrend] = useState<"up" | "down">("up")
154161

155162
useEffect(() => {
156-
157-
localStorage.setItem("isAdmin", "true")
163+
// Fetch real total users and previous month users
164+
fetch("/api/admin-users")
165+
.then(res => res.json())
166+
.then(data => {
167+
if (data.users) {
168+
const users: SupabaseUser[] = data.users;
169+
setTotalUsers(users.length.toLocaleString());
170+
171+
// Calculate previous month user count
172+
const now = new Date();
173+
const prevMonth = now.getMonth() === 0 ? 11 : now.getMonth() - 1;
174+
const prevYear = now.getMonth() === 0 ? now.getFullYear() - 1 : now.getFullYear();
175+
const prevMonthUsers = users.filter((u) => {
176+
const d = new Date(u.created_at);
177+
return d.getMonth() === prevMonth && d.getFullYear() === prevYear;
178+
}).length;
179+
180+
// Calculate change percentage
181+
const thisMonthUsers = users.filter((u) => {
182+
const d = new Date(u.created_at);
183+
return d.getMonth() === now.getMonth() && d.getFullYear() === now.getFullYear();
184+
}).length;
185+
186+
if (prevMonthUsers > 0) {
187+
const change = ((thisMonthUsers - prevMonthUsers) / prevMonthUsers) * 100;
188+
setTotalUsersChange(`${change > 0 ? "+" : ""}${change.toFixed(1)}%`);
189+
setTotalUsersTrend(change >= 0 ? "up" : "down");
190+
} else {
191+
setTotalUsersChange("N/A");
192+
setTotalUsersTrend("up");
193+
}
194+
}
195+
})
196+
}, [])
158197

198+
useEffect(() => {
199+
localStorage.setItem("isAdmin", "true")
159200
const timer = setInterval(() => {
160201
setCurrentTime(new Date())
161202
}, 1000)
162-
163-
164203
dashboardStats.forEach((stat, i) => {
165204
const value = parseInt(stat.value.replace(/[^\d]/g, ""))
166205
const step = Math.ceil(value / 30)
@@ -173,7 +212,6 @@ export default function AdminDashboard() {
173212
}, 20)
174213
setTimeout(() => clearInterval(interval), 600)
175214
})
176-
177215
return () => clearInterval(timer)
178216
}, [])
179217

@@ -203,6 +241,13 @@ export default function AdminDashboard() {
203241
}
204242
}
205243

244+
// When rendering dashboardStats, override Total Users value if totalUsers is available
245+
const statsToShow = dashboardStats.map(stat =>
246+
stat.title === "Total Users"
247+
? { ...stat, value: totalUsers ?? stat.value, change: totalUsersChange, trend: totalUsersTrend }
248+
: stat
249+
)
250+
206251
return (
207252
<div className="bg-black space-y-8 md:space-y-14 min-h-screen px-4 py-8 md:px-8 lg:px-16 relative overflow-x-hidden">
208253

@@ -237,7 +282,7 @@ export default function AdminDashboard() {
237282

238283

239284
<div className="grid gap-4 sm:gap-6 md:gap-8 grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 relative z-10">
240-
{dashboardStats.map((stat, i) => (
285+
{statsToShow.map((stat, i) => (
241286
<div className="group" key={stat.title}>
242287
<Card
243288
className={
@@ -265,7 +310,9 @@ export default function AdminDashboard() {
265310
</CardHeader>
266311
<CardContent className="relative z-10">
267312
<div className="text-2xl sm:text-3xl font-extrabold text-zinc-900 dark:text-white flex items-end gap-2 tracking-tight">
268-
{stat.value.match(/\d/) ? (
313+
{stat.title === "Total Users" && totalUsers ? (
314+
<span>{totalUsers}</span>
315+
) : stat.value.match(/\d/) ? (
269316
<span>{animatedStats[i].toLocaleString()}</span>
270317
) : (
271318
<span>{stat.value}</span>

app/admin/users/page.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export default function UsersPage() {
7070
status: user.banned ? "suspended" : "active",
7171
joinDate: user.created_at,
7272
lastActive: user.last_sign_in_at,
73-
avatar: meta.avatar_url ? null : user.email[0]?.toUpperCase() || null,
73+
avatar: user.email[0]?.toUpperCase() || "",
7474
avatarUrl: meta.avatar_url || null,
7575
};
7676
}));
@@ -311,8 +311,11 @@ export default function UsersPage() {
311311
<TableRow key={user.id} className="hover:bg-purple-700/10 transition-colors">
312312
<TableCell>
313313
<div className="flex items-center space-x-3">
314-
<div className="w-8 h-8 bg-gradient-to-br from-primary to-purple-600 rounded-full flex items-center justify-center text-white text-sm font-semibold">
314+
{/* <div className="w-8 h-8 bg-gradient-to-br from-primary to-purple-600 rounded-full flex items-center justify-center text-white text-sm font-semibold">
315315
{user.avatar}
316+
</div> */}
317+
<div className="w-8 h-8 bg-gradient-to-br from-primary to-purple-600 rounded-full flex items-center justify-center text-white text-sm font-semibold">
318+
{user.avatar}
316319
</div>
317320
<div className="min-w-0 flex-1">
318321
<p className="font-medium text-sm truncate text-zinc-900 dark:text-zinc-100">{user.name}</p>

0 commit comments

Comments
 (0)