@@ -8,68 +8,68 @@ import {
88 RssIcon ,
99 ShieldExclamationIcon ,
1010 NewspaperIcon ,
11- LinkIcon ,
1211} from "@heroicons/react/24/outline" ;
1312import { api } from "@/server/trpc/react" ;
1413
15- const AdminDashboard = ( ) => {
16- const { data : stats , isLoading } = api . admin . getStats . useQuery ( ) ;
17- const { data : reportCounts } = api . report . getCounts . useQuery ( ) ;
18-
19- const StatCard = ( {
20- title,
21- value,
22- icon : Icon ,
23- href,
24- color = "blue" ,
25- } : {
26- title : string ;
27- value : number | undefined ;
28- icon : React . ComponentType < { className ?: string } > ;
29- href ?: string ;
30- color ?: "blue" | "green" | "yellow" | "red" | "purple" | "orange" ;
31- } ) => {
32- const colorClasses = {
33- blue : "bg-blue-50 text-blue-600 dark:bg-blue-900/30 dark:text-blue-400" ,
34- green :
35- "bg-green-50 text-green-600 dark:bg-green-900/30 dark:text-green-400" ,
36- yellow :
37- "bg-yellow-50 text-yellow-600 dark:bg-yellow-900/30 dark:text-yellow-400" ,
38- red : "bg-red-50 text-red-600 dark:bg-red-900/30 dark:text-red-400" ,
39- purple :
40- "bg-purple-50 text-purple-600 dark:bg-purple-900/30 dark:text-purple-400" ,
41- orange :
42- "bg-orange-50 text-orange-600 dark:bg-orange-900/30 dark:text-orange-400" ,
43- } ;
14+ const colorClasses = {
15+ blue : "bg-blue-50 text-blue-600 dark:bg-blue-900/30 dark:text-blue-400" ,
16+ green : "bg-green-50 text-green-600 dark:bg-green-900/30 dark:text-green-400" ,
17+ yellow :
18+ "bg-yellow-50 text-yellow-600 dark:bg-yellow-900/30 dark:text-yellow-400" ,
19+ red : "bg-red-50 text-red-600 dark:bg-red-900/30 dark:text-red-400" ,
20+ purple :
21+ "bg-purple-50 text-purple-600 dark:bg-purple-900/30 dark:text-purple-400" ,
22+ orange :
23+ "bg-orange-50 text-orange-600 dark:bg-orange-900/30 dark:text-orange-400" ,
24+ } ;
4425
45- const content = (
46- < div className = "rounded-lg border border-neutral-200 bg-white p-4 transition-colors hover:border-neutral-300 dark:border-neutral-700 dark:bg-neutral-800 dark:hover:border-neutral-600" >
47- < div className = "flex items-center gap-3" >
48- < div className = { `rounded-lg p-2 ${ colorClasses [ color ] } ` } >
49- < Icon className = "h-5 w-5" />
50- </ div >
51- < div >
52- < p className = "text-sm text-neutral-500 dark:text-neutral-400" >
53- { title }
54- </ p >
55- < p className = "text-2xl font-bold text-neutral-900 dark:text-white" >
56- { isLoading ? (
57- < span className = "inline-block h-8 w-16 animate-pulse rounded bg-neutral-200 dark:bg-neutral-700" />
58- ) : (
59- ( value ?? 0 )
60- ) }
61- </ p >
62- </ div >
26+ const StatCard = ( {
27+ title,
28+ value,
29+ icon : Icon ,
30+ href,
31+ color = "blue" ,
32+ isLoading,
33+ } : {
34+ title : string ;
35+ value : number | undefined ;
36+ icon : React . ComponentType < { className ?: string } > ;
37+ href ?: string ;
38+ color ?: "blue" | "green" | "yellow" | "red" | "purple" | "orange" ;
39+ isLoading ?: boolean ;
40+ } ) => {
41+ const content = (
42+ < div className = "rounded-lg border border-neutral-200 bg-white p-4 transition-colors hover:border-neutral-300 dark:border-neutral-700 dark:bg-neutral-800 dark:hover:border-neutral-600" >
43+ < div className = "flex items-center gap-3" >
44+ < div className = { `rounded-lg p-2 ${ colorClasses [ color ] } ` } >
45+ < Icon className = "h-5 w-5" />
46+ </ div >
47+ < div >
48+ < p className = "text-sm text-neutral-500 dark:text-neutral-400" >
49+ { title }
50+ </ p >
51+ < p className = "text-2xl font-bold text-neutral-900 dark:text-white" >
52+ { isLoading ? (
53+ < span className = "inline-block h-8 w-16 animate-pulse rounded bg-neutral-200 dark:bg-neutral-700" />
54+ ) : (
55+ ( value ?? 0 )
56+ ) }
57+ </ p >
6358 </ div >
6459 </ div >
65- ) ;
60+ </ div >
61+ ) ;
62+
63+ if ( href ) {
64+ return < Link href = { href } > { content } </ Link > ;
65+ }
6666
67- if ( href ) {
68- return < Link href = { href } > { content } </ Link > ;
69- }
67+ return content ;
68+ } ;
7069
71- return content ;
72- } ;
70+ const AdminDashboard = ( ) => {
71+ const { data : stats , isLoading } = api . admin . getStats . useQuery ( ) ;
72+ const { data : reportCounts } = api . report . getCounts . useQuery ( ) ;
7373
7474 return (
7575 < div className = "mx-auto max-w-6xl px-4 py-8" >
@@ -90,25 +90,29 @@ const AdminDashboard = () => {
9090 icon = { UsersIcon }
9191 color = "blue"
9292 href = "/admin/users"
93+ isLoading = { isLoading }
9394 />
9495 < StatCard
9596 title = "Published Posts"
9697 value = { stats ?. publishedPosts }
9798 icon = { DocumentTextIcon }
9899 color = "green"
100+ isLoading = { isLoading }
99101 />
100102 < StatCard
101103 title = "Aggregated Articles"
102104 value = { stats ?. aggregatedArticles }
103105 icon = { NewspaperIcon }
104106 color = "purple"
107+ isLoading = { isLoading }
105108 />
106109 < StatCard
107110 title = "Active Feed Sources"
108111 value = { stats ?. activeFeedSources }
109112 icon = { RssIcon }
110113 color = "orange"
111114 href = "/admin/sources"
115+ isLoading = { isLoading }
112116 />
113117 </ div >
114118
@@ -124,25 +128,29 @@ const AdminDashboard = () => {
124128 icon = { FlagIcon }
125129 color = "yellow"
126130 href = "/admin/moderation"
131+ isLoading = { isLoading }
127132 />
128133 < StatCard
129134 title = "Actioned Reports"
130135 value = { reportCounts ?. actioned }
131136 icon = { ShieldExclamationIcon }
132137 color = "red"
138+ isLoading = { isLoading }
133139 />
134140 < StatCard
135141 title = "Banned Users"
136142 value = { stats ?. bannedUsers }
137143 icon = { ShieldExclamationIcon }
138144 color = "red"
139145 href = "/admin/users?filter=banned"
146+ isLoading = { isLoading }
140147 />
141148 < StatCard
142149 title = "Dismissed Reports"
143150 value = { reportCounts ?. dismissed }
144151 icon = { FlagIcon }
145152 color = "green"
153+ isLoading = { isLoading }
146154 />
147155 </ div >
148156 </ div >
0 commit comments