@@ -12,6 +12,10 @@ import { AuthProvider } from './context/AuthContext';
1212import PublicLayout from './layouts/PublicLayout' ;
1313import DashboardLayout from './layouts/DashboardLayout' ;
1414import ProtectedRoute from './components/common/ProtectedRoute' ;
15+ import { Toaster } from 'react-hot-toast' ;
16+ import Spinner from './components/common/Spinner' ;
17+ import api from './services/api' ;
18+ import { User , Layers , Calendar , CreditCard , PieChart } from 'lucide-react' ;
1519
1620// Pages to import - Actual page content components
1721import Home from './pages/public/Home' ;
@@ -46,6 +50,58 @@ const MockPage = ({ title }) => (
4650 </ div >
4751) ;
4852
53+ // Generic Data Manager for formerly MockPages
54+ const DataDashboard = ( { title, icon : Icon , endpoint, columns } ) => {
55+ const [ data , setData ] = React . useState ( [ ] ) ;
56+ const [ loading , setLoading ] = React . useState ( true ) ;
57+ const [ error , setError ] = React . useState ( null ) ;
58+
59+ React . useEffect ( ( ) => {
60+ const fetch = async ( ) => {
61+ try {
62+ const res = await api . get ( endpoint ) ;
63+ setData ( res . data . results || res . data ) ;
64+ } catch ( err ) {
65+ setError ( `Failed to load ${ title . toLowerCase ( ) } ` ) ;
66+ } finally {
67+ setLoading ( false ) ;
68+ }
69+ } ;
70+ fetch ( ) ;
71+ } , [ endpoint , title ] ) ;
72+
73+ if ( loading ) return < div className = "h-full flex items-center justify-center" > < Spinner size = "lg" /> </ div > ;
74+ if ( error ) return < div className = "card border-danger text-danger p-8 text-center" > < h2 > Error</ h2 > < p > { error } </ p > </ div > ;
75+
76+ return (
77+ < div className = "animate-fade-in" >
78+ < div className = "flex items-center gap-4 mb-8" >
79+ < div className = "stat-icon" > < Icon size = { 24 } /> </ div >
80+ < h1 className = "m-0" > { title } </ h1 >
81+ </ div >
82+
83+ < div className = "table-container" >
84+ < table >
85+ < thead >
86+ < tr > { columns . map ( c => < th key = { c . key } > { c . label } </ th > ) } </ tr >
87+ </ thead >
88+ < tbody >
89+ { data . length === 0 ? (
90+ < tr > < td colSpan = { columns . length } className = "text-center py-8" > No records found</ td > </ tr >
91+ ) : (
92+ data . map ( ( row , i ) => (
93+ < tr key = { row . id || i } >
94+ { columns . map ( c => < td key = { c . key } > { c . render ? c . render ( row ) : row [ c . key ] } </ td > ) }
95+ </ tr >
96+ ) )
97+ ) }
98+ </ tbody >
99+ </ table >
100+ </ div >
101+ </ div >
102+ ) ;
103+ } ;
104+
49105/**
50106 * App: The root React component.
51107 * It manages:
@@ -56,6 +112,7 @@ const MockPage = ({ title }) => (
56112export default function App ( ) {
57113 return (
58114 < AuthProvider >
115+ < Toaster position = "top-right" />
59116 < BrowserRouter >
60117 < Routes >
61118 { /*
@@ -111,10 +168,59 @@ export default function App() {
111168 < Route element = { < ProtectedRoute allowedRoles = { [ 'admin' ] } /> } >
112169 < Route element = { < DashboardLayout /> } >
113170 < Route path = "/admin/dashboard" element = { < AdminDashboard /> } />
114- < Route path = "/admin/users" element = { < MockPage title = "Manage Users" /> } />
115- < Route path = "/admin/adspaces" element = { < MockPage title = "Manage All Ad Spaces" /> } />
116- < Route path = "/admin/bookings" element = { < MockPage title = "All Bookings" /> } />
117- < Route path = "/admin/payments" element = { < MockPage title = "Payments Control" /> } />
171+ < Route path = "/admin/users" element = {
172+ < DataDashboard
173+ title = "Platform Users"
174+ icon = { User }
175+ endpoint = "/users/"
176+ columns = { [
177+ { key : 'id' , label : 'ID' } ,
178+ { key : 'name' , label : 'Name' } ,
179+ { key : 'email' , label : 'Email' } ,
180+ { key : 'role' , label : 'Role' , render : ( u ) => < span className = { `badge ${ u . role === 'admin' ? 'badge-primary' : u . role === 'owner' ? 'badge-warning' : 'badge-info' } ` } > { u . role } </ span > }
181+ ] }
182+ />
183+ } />
184+ < Route path = "/admin/adspaces" element = {
185+ < DataDashboard
186+ title = "Global Ad Spaces"
187+ icon = { Layers }
188+ endpoint = "/adspaces/"
189+ columns = { [
190+ { key : 'title' , label : 'Billboard' } ,
191+ { key : 'city' , label : 'City' } ,
192+ { key : 'basePricePerDay' , label : 'Price/Day' , render : ( a ) => `$${ a . basePricePerDay } ` } ,
193+ { key : 'availabilityStatus' , label : 'Status' , render : ( a ) => < span className = { `badge ${ a . availabilityStatus === 'available' ? 'badge-success' : 'badge-warning' } ` } > { a . availabilityStatus } </ span > }
194+ ] }
195+ />
196+ } />
197+ < Route path = "/admin/bookings" element = {
198+ < DataDashboard
199+ title = "System Bookings"
200+ icon = { Calendar }
201+ endpoint = "/bookings/"
202+ columns = { [
203+ { key : 'id' , label : 'Ref' } ,
204+ { key : 'startDate' , label : 'Starts' } ,
205+ { key : 'endDate' , label : 'Ends' } ,
206+ { key : 'totalPrice' , label : 'Total' , render : ( b ) => `$${ b . totalPrice } ` } ,
207+ { key : 'status' , label : 'Status' , render : ( b ) => < span className = { `badge ${ b . status === 'active' ? 'badge-success' : 'badge-warning' } ` } > { b . status } </ span > }
208+ ] }
209+ />
210+ } />
211+ < Route path = "/admin/payments" element = {
212+ < DataDashboard
213+ title = "Payment Transactions"
214+ icon = { CreditCard }
215+ endpoint = "/payments/"
216+ columns = { [
217+ { key : 'id' , label : 'ID' } ,
218+ { key : 'amount' , label : 'Amount' , render : ( p ) => `$${ p . amount } ` } ,
219+ { key : 'payment_method' , label : 'Method' } ,
220+ { key : 'status' , label : 'Status' , render : ( p ) => < span className = { `badge ${ p . status === 'successful' ? 'badge-success' : 'badge-warning' } ` } > { p . status } </ span > }
221+ ] }
222+ />
223+ } />
118224 < Route path = "/admin/reports" element = { < MockPage title = "System Reports" /> } />
119225 </ Route >
120226 </ Route >
0 commit comments