1+ import React , { useState , useEffect } from 'react' ;
2+ import { Card , CardHeader , CardTitle , CardContent } from './card' ;
3+ import { Badge } from './badge' ;
4+
5+ interface Lead {
6+ name : string ;
7+ email : string ;
8+ role : 'Creator' | 'Buyer' | 'Both' ;
9+ first_use : string ;
10+ ts : string ;
11+ }
12+
13+ export function AdminDashboard ( ) {
14+ const [ leads , setLeads ] = useState < Lead [ ] > ( [ ] ) ;
15+ const [ firebaseLeads , setFirebaseLeads ] = useState < Lead [ ] > ( [ ] ) ;
16+ const [ loading , setLoading ] = useState ( true ) ;
17+
18+ useEffect ( ( ) => {
19+ // Load localStorage leads
20+ try {
21+ const localLeads = JSON . parse ( localStorage . getItem ( 'anym8_waitlist' ) || '[]' ) ;
22+ setLeads ( localLeads ) ;
23+ } catch ( e ) {
24+ console . log ( 'No local leads found' ) ;
25+ }
26+
27+ // Try to fetch Firebase leads
28+ fetchFirebaseLeads ( ) ;
29+ } , [ ] ) ;
30+
31+ const fetchFirebaseLeads = async ( ) => {
32+ try {
33+ const response = await fetch ( '/api/admin-leads' ) ;
34+ if ( response . ok ) {
35+ const data = await response . json ( ) ;
36+ setFirebaseLeads ( data . leads || [ ] ) ;
37+ }
38+ } catch ( e ) {
39+ console . log ( 'Firebase leads not available' ) ;
40+ } finally {
41+ setLoading ( false ) ;
42+ }
43+ } ;
44+
45+ const downloadCSV = ( data : Lead [ ] , filename : string ) => {
46+ const csv = [ 'name,email,role,first_use,timestamp' ]
47+ . concat ( data . map ( lead =>
48+ [ lead . name , lead . email , lead . role , lead . first_use , lead . ts ]
49+ . map ( v => `"${ String ( v || '' ) . replace ( '"' , '""' ) } "` )
50+ . join ( ',' )
51+ ) )
52+ . join ( '\n' ) ;
53+
54+ const blob = new Blob ( [ csv ] , { type : 'text/csv' } ) ;
55+ const url = URL . createObjectURL ( blob ) ;
56+ const a = document . createElement ( 'a' ) ;
57+ a . href = url ;
58+ a . download = filename ;
59+ a . click ( ) ;
60+ URL . revokeObjectURL ( url ) ;
61+ } ;
62+
63+ if ( loading ) {
64+ return < div className = "p-8" > Loading admin dashboard...</ div > ;
65+ }
66+
67+ return (
68+ < div className = "min-h-screen bg-black text-white p-8" >
69+ < div className = "max-w-6xl mx-auto" >
70+ < h1 className = "text-3xl font-bold mb-8" > ANYM⁸ Admin Dashboard</ h1 >
71+
72+ < div className = "grid gap-6" >
73+ { /* Firebase Leads */ }
74+ < Card className = "bg-black/60 border-white/20" >
75+ < CardHeader >
76+ < CardTitle className = "flex justify-between items-center" >
77+ Firebase Leads ({ firebaseLeads . length } )
78+ < button
79+ onClick = { ( ) => downloadCSV ( firebaseLeads , 'firebase-leads.csv' ) }
80+ className = "px-4 py-2 bg-emerald-600 hover:bg-emerald-700 rounded-lg text-sm"
81+ >
82+ Download CSV
83+ </ button >
84+ </ CardTitle >
85+ </ CardHeader >
86+ < CardContent >
87+ { firebaseLeads . length > 0 ? (
88+ < div className = "overflow-x-auto" >
89+ < table className = "w-full text-sm" >
90+ < thead >
91+ < tr className = "border-b border-white/10" >
92+ < th className = "text-left p-2" > Name</ th >
93+ < th className = "text-left p-2" > Email</ th >
94+ < th className = "text-left p-2" > Role</ th >
95+ < th className = "text-left p-2" > First Use</ th >
96+ < th className = "text-left p-2" > Date</ th >
97+ </ tr >
98+ </ thead >
99+ < tbody >
100+ { firebaseLeads . map ( ( lead , i ) => (
101+ < tr key = { i } className = "border-b border-white/5" >
102+ < td className = "p-2" > { lead . name } </ td >
103+ < td className = "p-2" > { lead . email } </ td >
104+ < td className = "p-2" >
105+ < Badge variant = "secondary" > { lead . role } </ Badge >
106+ </ td >
107+ < td className = "p-2 text-xs text-white/70" > { lead . first_use } </ td >
108+ < td className = "p-2 text-xs text-white/70" >
109+ { new Date ( lead . ts ) . toLocaleDateString ( ) }
110+ </ td >
111+ </ tr >
112+ ) ) }
113+ </ tbody >
114+ </ table >
115+ </ div >
116+ ) : (
117+ < p className = "text-white/60" > No Firebase leads found. Check API connection.</ p >
118+ ) }
119+ </ CardContent >
120+ </ Card >
121+
122+ { /* Local Backup Leads */ }
123+ < Card className = "bg-black/60 border-white/20" >
124+ < CardHeader >
125+ < CardTitle className = "flex justify-between items-center" >
126+ Local Backup Leads ({ leads . length } )
127+ < button
128+ onClick = { ( ) => downloadCSV ( leads , 'local-backup-leads.csv' ) }
129+ className = "px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-lg text-sm"
130+ >
131+ Download CSV
132+ </ button >
133+ </ CardTitle >
134+ </ CardHeader >
135+ < CardContent >
136+ { leads . length > 0 ? (
137+ < div className = "overflow-x-auto" >
138+ < table className = "w-full text-sm" >
139+ < thead >
140+ < tr className = "border-b border-white/10" >
141+ < th className = "text-left p-2" > Name</ th >
142+ < th className = "text-left p-2" > Email</ th >
143+ < th className = "text-left p-2" > Role</ th >
144+ < th className = "text-left p-2" > First Use</ th >
145+ < th className = "text-left p-2" > Date</ th >
146+ </ tr >
147+ </ thead >
148+ < tbody >
149+ { leads . map ( ( lead , i ) => (
150+ < tr key = { i } className = "border-b border-white/5" >
151+ < td className = "p-2" > { lead . name } </ td >
152+ < td className = "p-2" > { lead . email } </ td >
153+ < td className = "p-2" >
154+ < Badge variant = "secondary" > { lead . role } </ Badge >
155+ </ td >
156+ < td className = "p-2 text-xs text-white/70" > { lead . first_use } </ td >
157+ < td className = "p-2 text-xs text-white/70" >
158+ { new Date ( lead . ts ) . toLocaleDateString ( ) }
159+ </ td >
160+ </ tr >
161+ ) ) }
162+ </ tbody >
163+ </ table >
164+ </ div >
165+ ) : (
166+ < p className = "text-white/60" > No local backup leads found.</ p >
167+ ) }
168+ </ CardContent >
169+ </ Card >
170+ </ div >
171+ </ div >
172+ </ div >
173+ ) ;
174+ }
0 commit comments