|
1
|
import { useEffect } from "react"; |
|
2
|
import { useLocation } from "wouter"; |
|
3
|
import { useAuth } from "@/hooks/useAuth"; |
|
4
|
import { useAdminStats } from "@/hooks/useAdmin"; |
|
5
|
import { useToast } from "@/hooks/use-toast"; |
|
6
|
import Header from "@/components/layout/header"; |
|
7
|
import Footer from "@/components/layout/footer"; |
|
8
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; |
|
9
|
import { Badge } from "@/components/ui/badge"; |
|
10
|
|
|
11
|
export default function AdminDashboard() { |
|
12
|
const { user, isAuthenticated, isLoading: authLoading } = useAuth(); |
|
13
|
const { stats, isLoading: statsLoading } = useAdminStats(); |
|
14
|
const { toast } = useToast(); |
|
15
|
const [, setLocation] = useLocation(); |
|
16
|
|
|
17
|
// Redirect if not admin |
|
18
|
useEffect(() => { |
|
19
|
if (!authLoading && (!isAuthenticated || user?.role !== "admin")) { |
|
20
|
toast({ |
|
21
|
title: "Access Denied", |
|
22
|
description: "Administrator privileges required to access this page.", |
|
23
|
variant: "destructive", |
|
24
|
}); |
|
25
|
setLocation("/"); |
|
26
|
} |
|
27
|
}, [user, isAuthenticated, authLoading, toast, setLocation]); |
|
28
|
|
|
29
|
if (authLoading || statsLoading) { |
|
30
|
return ( |
|
31
|
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 amoled:bg-black"> |
|
32
|
<Header /> |
|
33
|
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6"> |
|
34
|
<div className="animate-pulse space-y-6"> |
|
35
|
<div className="h-8 bg-gray-200 dark:bg-gray-700 rounded w-1/3"></div> |
|
36
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6"> |
|
37
|
{[...Array(3)].map((_, i) => ( |
|
38
|
<div key={i} className="h-32 bg-gray-200 dark:bg-gray-700 rounded-xl"></div> |
|
39
|
))} |
|
40
|
</div> |
|
41
|
</div> |
|
42
|
</main> |
|
43
|
</div> |
|
44
|
); |
|
45
|
} |
|
46
|
|
|
47
|
if (!user || user.role !== "admin") { |
|
48
|
return null; // Will redirect via useEffect |
|
49
|
} |
|
50
|
|
|
51
|
return ( |
|
52
|
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 amoled:bg-black"> |
|
53
|
<Header /> |
|
54
|
|
|
55
|
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6"> |
|
56
|
{/* Header */} |
|
57
|
<div className="mb-8"> |
|
58
|
<div className="flex items-center justify-between"> |
|
59
|
<div> |
|
60
|
<h1 className="text-3xl font-bold text-gray-900 dark:text-white"> |
|
61
|
Admin Dashboard |
|
62
|
</h1> |
|
63
|
<p className="text-gray-600 dark:text-gray-400 mt-2"> |
|
64
|
Welcome back, {user.firstName || user.email} |
|
65
|
</p> |
|
66
|
</div> |
|
67
|
<Badge variant="default" className="bg-green-600 text-white"> |
|
68
|
<i className="material-icons text-sm mr-1">admin_panel_settings</i> |
|
69
|
Administrator |
|
70
|
</Badge> |
|
71
|
</div> |
|
72
|
</div> |
|
73
|
|
|
74
|
{/* Stats Cards */} |
|
75
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8"> |
|
76
|
<Card> |
|
77
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> |
|
78
|
<CardTitle className="text-sm font-medium">Total Guides</CardTitle> |
|
79
|
<i className="material-icons text-primary-600">library_books</i> |
|
80
|
</CardHeader> |
|
81
|
<CardContent> |
|
82
|
<div className="text-2xl font-bold">{stats?.totalGuides || 0}</div> |
|
83
|
<p className="text-xs text-gray-600 dark:text-gray-400"> |
|
84
|
Active repair guides |
|
85
|
</p> |
|
86
|
</CardContent> |
|
87
|
</Card> |
|
88
|
|
|
89
|
<Card> |
|
90
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> |
|
91
|
<CardTitle className="text-sm font-medium">Total Views</CardTitle> |
|
92
|
<i className="material-icons text-blue-600">visibility</i> |
|
93
|
</CardHeader> |
|
94
|
<CardContent> |
|
95
|
<div className="text-2xl font-bold">{stats?.totalViews || 0}</div> |
|
96
|
<p className="text-xs text-gray-600 dark:text-gray-400"> |
|
97
|
Across all guides |
|
98
|
</p> |
|
99
|
</CardContent> |
|
100
|
</Card> |
|
101
|
|
|
102
|
<Card> |
|
103
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> |
|
104
|
<CardTitle className="text-sm font-medium">Total Downloads</CardTitle> |
|
105
|
<i className="material-icons text-green-600">download</i> |
|
106
|
</CardHeader> |
|
107
|
<CardContent> |
|
108
|
<div className="text-2xl font-bold">{stats?.totalDownloads || 0}</div> |
|
109
|
<p className="text-xs text-gray-600 dark:text-gray-400"> |
|
110
|
Guide downloads |
|
111
|
</p> |
|
112
|
</CardContent> |
|
113
|
</Card> |
|
114
|
</div> |
|
115
|
|
|
116
|
{/* Admin Info */} |
|
117
|
<Card className="mb-8"> |
|
118
|
<CardHeader> |
|
119
|
<CardTitle className="flex items-center"> |
|
120
|
<i className="material-icons text-lg mr-2">account_circle</i> |
|
121
|
Administrator Information |
|
122
|
</CardTitle> |
|
123
|
</CardHeader> |
|
124
|
<CardContent> |
|
125
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm"> |
|
126
|
<div> |
|
127
|
<span className="font-medium text-gray-900 dark:text-white">Email:</span> |
|
128
|
<span className="ml-2 text-gray-600 dark:text-gray-400">{user.email}</span> |
|
129
|
</div> |
|
130
|
<div> |
|
131
|
<span className="font-medium text-gray-900 dark:text-white">Role:</span> |
|
132
|
<span className="ml-2 text-gray-600 dark:text-gray-400">Administrator</span> |
|
133
|
</div> |
|
134
|
<div> |
|
135
|
<span className="font-medium text-gray-900 dark:text-white">Name:</span> |
|
136
|
<span className="ml-2 text-gray-600 dark:text-gray-400"> |
|
137
|
{user.firstName} {user.lastName} |
|
138
|
</span> |
|
139
|
</div> |
|
140
|
<div> |
|
141
|
<span className="font-medium text-gray-900 dark:text-white">User ID:</span> |
|
142
|
<span className="ml-2 text-gray-600 dark:text-gray-400 font-mono text-xs"> |
|
143
|
{user.id} |
|
144
|
</span> |
|
145
|
</div> |
|
146
|
</div> |
|
147
|
</CardContent> |
|
148
|
</Card> |
|
149
|
|
|
150
|
{/* System Information */} |
|
151
|
<Card> |
|
152
|
<CardHeader> |
|
153
|
<CardTitle className="flex items-center"> |
|
154
|
<i className="material-icons text-lg mr-2">info</i> |
|
155
|
System Information |
|
156
|
</CardTitle> |
|
157
|
</CardHeader> |
|
158
|
<CardContent> |
|
159
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm"> |
|
160
|
<div> |
|
161
|
<span className="font-medium text-gray-900 dark:text-white">App Version:</span> |
|
162
|
<span className="ml-2 text-gray-600 dark:text-gray-400">1.0.0</span> |
|
163
|
</div> |
|
164
|
<div> |
|
165
|
<span className="font-medium text-gray-900 dark:text-white">Build Date:</span> |
|
166
|
<span className="ml-2 text-gray-600 dark:text-gray-400">2025.08.18</span> |
|
167
|
</div> |
|
168
|
<div> |
|
169
|
<span className="font-medium text-gray-900 dark:text-white">Database:</span> |
|
170
|
<span className="ml-2 text-gray-600 dark:text-gray-400">PostgreSQL (Connected)</span> |
|
171
|
</div> |
|
172
|
<div> |
|
173
|
<span className="font-medium text-gray-900 dark:text-white">Authentication:</span> |
|
174
|
<span className="ml-2 text-gray-600 dark:text-gray-400">Firebase Auth</span> |
|
175
|
</div> |
|
176
|
</div> |
|
177
|
</CardContent> |
|
178
|
</Card> |
|
179
|
</main> |
|
180
|
|
|
181
|
<Footer /> |
|
182
|
</div> |
|
183
|
); |
|
184
|
} |