Admin Dashboard Components

Analytics charts, user management tables, and business intelligence.

Analytics

  • • Revenue tracking (MRR/ARR)
  • • User growth metrics
  • • Churn analysis
  • • Subscription analytics

Management

  • • User search & filtering
  • • Subscription management
  • • Account status controls
  • • Bulk operations

Revenue Metrics

src/components/admin/RevenueChart.tsx
1"use client";
2
3import { useMRRAnalytics } from "~/hooks/useAnalytics";
4
5export function RevenueChart() {
6 const { data, isLoading } = useMRRAnalytics();
7 if (isLoading || !data) return <div>Loading...</div>;
8
9 const current = data[data.length - 1];
10 const previous = data[data.length - 2];
11 const growth = previous
12 ? ((current.totalMRR - previous.totalMRR) / previous.totalMRR) * 100
13 : 0;
14
15 return (
16 <div className="grid gap-4 md:grid-cols-3">
17 <div className="rounded-xl border bg-card p-4">
18 <p className="text-sm text-muted-foreground">Monthly Revenue</p>
19 <p className="text-2xl font-bold">${current.totalMRR.toLocaleString()}</p>
20 <p className="text-xs text-muted-foreground">
21 {growth > 0 ? "+" : ""}{growth.toFixed(1)}% from last month
22 </p>
23 </div>
24 <div className="rounded-xl border bg-card p-4">
25 <p className="text-sm text-muted-foreground">New MRR</p>
26 <p className="text-2xl font-bold text-green-600">
27 +${current.newMRR.toLocaleString()}
28 </p>
29 </div>
30 <div className="rounded-xl border bg-card p-4">
31 <p className="text-sm text-muted-foreground">Churned MRR</p>
32 <p className="text-2xl font-bold text-red-600">
33 -${current.churnMRR.toLocaleString()}
34 </p>
35 </div>
36 </div>
37 );
38}

User Management Table

src/components/admin/UserManagementTable.tsx
1"use client";
2
3import { useState, useEffect } from "react";
4
5export function UserManagementTable({ users, onUserAction }) {
6 const [searchTerm, setSearchTerm] = useState("");
7 const [filteredUsers, setFilteredUsers] = useState(users);
8
9 useEffect(() => {
10 const filtered = users.filter(user =>
11 user.email.toLowerCase().includes(searchTerm.toLowerCase())
12 );
13 setFilteredUsers(filtered);
14 }, [users, searchTerm]);
15
16 return (
17 <div className="space-y-4">
18 <input
19 placeholder="Search users..."
20 value={searchTerm}
21 onChange={(e) => setSearchTerm(e.target.value)}
22 className="rounded-lg border px-3 py-2"
23 />
24
25 <table className="w-full rounded-lg border">
26 <thead>
27 <tr className="border-b bg-muted/50">
28 <th className="p-3 text-left">User</th>
29 <th className="p-3 text-left">Status</th>
30 <th className="p-3 text-left">Joined</th>
31 <th className="p-3 text-left">Actions</th>
32 </tr>
33 </thead>
34 <tbody>
35 {filteredUsers.map((user) => (
36 <tr key={user.id} className="border-b">
37 <td className="p-3">
38 <div className="font-medium">{user.name || "Anonymous"}</div>
39 <div className="text-sm text-muted-foreground">{user.email}</div>
40 </td>
41 <td className="p-3">
42 <span className="rounded-full bg-muted px-2 py-1 text-xs">
43 {user.subscription?.status || "Free"}
44 </span>
45 </td>
46 <td className="p-3 text-sm text-muted-foreground">
47 {new Date(user.createdAt).toLocaleDateString()}
48 </td>
49 <td className="p-3">
50 <button onClick={() => onUserAction(user.id, "view")}>
51 View
52 </button>
53 </td>
54 </tr>
55 ))}
56 </tbody>
57 </table>
58 </div>
59 );
60}

Best Practices

Security

  • • Role-based access control
  • • Log all admin actions
  • • Secure session management
  • • Server-side validation

Performance

  • • Use React Query for caching
  • • Paginate large datasets
  • • Debounce filter inputs
  • • Add database indexes

Next: Pricing Section

Component documentation

Continue →