source: frontend/src/pages/AdminPanelPage.jsx@ 09e02d7

Last change on this file since 09e02d7 was 09e02d7, checked in by Nikola Sarafimov <sarafimov.nikola12345@…>, 4 days ago

Final room reservation system implementation

  • Property mode set to 100644
File size: 5.1 KB
RevLine 
[09e02d7]1import { useEffect, useState } from "react";
2import toast from "react-hot-toast";
3import { Boxes, CalendarCheck, ShieldCheck, Users } from "lucide-react";
4import { getDashboardStats, getEquipment, getPendingReservations, getUsers } from "../api.js";
5
6export default function AdminPanelPage({ activeUser }) {
7 const [users, setUsers] = useState([]);
8 const [equipment, setEquipment] = useState([]);
9 const [pending, setPending] = useState([]);
10 const [stats, setStats] = useState(null);
11
12 useEffect(() => {
13 if (activeUser.role !== "admin") {
14 return;
15 }
16
17 Promise.all([
18 getUsers(),
19 getEquipment(),
20 getPendingReservations(),
21 getDashboardStats(),
22 ])
23 .then(([usersData, equipmentData, pendingData, statsData]) => {
24 setUsers(usersData);
25 setEquipment(equipmentData);
26 setPending(pendingData);
27 setStats(statsData);
28 })
29 .catch((err) => toast.error(err.message));
30 }, [activeUser.role]);
31
32 if (activeUser.role !== "admin") {
33 return (
34 <section>
35 <Hero
36 eyebrow="Access denied"
37 title="Admin Panel"
38 subtitle="This module is available only for administrator users."
39 />
40 </section>
41 );
42 }
43
44 return (
45 <section>
46 <Hero
47 eyebrow="Administration"
48 title="Admin Panel"
49 subtitle="Read-only operational overview of users, equipment and pending reservation activity."
50 />
51
52 {stats && (
53 <div className="stats-grid compact">
54 <SmallStat icon={Users} label="Users" value={stats.totalUsers} />
55 <SmallStat icon={Boxes} label="Equipment" value={stats.totalEquipmentTypes} />
56 <SmallStat icon={CalendarCheck} label="Pending" value={stats.pendingReservations} />
57 </div>
58 )}
59
60 <div className="admin-grid">
61 <div className="panel">
62 <SectionTitle icon={Users} title="System users" subtitle="Loaded from project.users." />
63 <MiniList
64 items={users.map((u) => ({
65 title: u.fullName,
66 subtitle: `${u.username} · ${u.email}`,
67 badge: u.role,
68 }))}
69 />
70 </div>
71
72 <div className="panel">
73 <SectionTitle icon={Boxes} title="Equipment inventory" subtitle="Registered equipment types." />
74 <MiniList
75 items={equipment.map((e) => ({
76 title: e.name,
77 subtitle: `Stock quantity: ${e.stockQuantity}`,
78 badge: "equipment",
79 }))}
80 />
81 </div>
82
83 <div className="panel">
84 <SectionTitle icon={CalendarCheck} title="Pending reservations" subtitle="Requests waiting for approval." />
85 <MiniList
86 items={pending.map((p) => ({
87 title: `#${p.reservationId} · ${p.requesterName}`,
88 subtitle: `${p.reservationDate} · ${p.startTime}-${p.endTime}`,
89 badge: p.status,
90 }))}
91 />
92 </div>
93 </div>
94 </section>
95 );
96}
97
98function Hero({ eyebrow, title, subtitle }) {
99 return (
100 <div className="hero">
101 <div>
102 <div className="eyebrow">
103 <ShieldCheck size={16} />
104 {eyebrow}
105 </div>
106 <h1>{title}</h1>
107 <p>{subtitle}</p>
108 </div>
109 </div>
110 );
111}
112
113function SmallStat({ icon: Icon, label, value }) {
114 return (
115 <div className="stat-card blue">
116 <div className="stat-icon">
117 <Icon size={24} />
118 </div>
119 <span>{label}</span>
120 <strong>{value}</strong>
121 </div>
122 );
123}
124
125function SectionTitle({ icon: Icon, title, subtitle }) {
126 return (
127 <div className="section-title">
128 <Icon size={21} />
129 <div>
130 <h3>{title}</h3>
131 <p>{subtitle}</p>
132 </div>
133 </div>
134 );
135}
136
137function MiniList({ items }) {
138 if (!items.length) {
139 return <div className="empty-state small">No records available.</div>;
140 }
141
142 return (
143 <div className="mini-list">
144 {items.map((item, index) => (
145 <div className="mini-list-item" key={index}>
146 <div>
147 <strong>{item.title}</strong>
148 <span>{item.subtitle}</span>
149 </div>
150 <Badge value={item.badge} />
151 </div>
152 ))}
153 </div>
154 );
155}
156
157function Badge({ value }) {
158 return <span className={`badge ${String(value || "").toLowerCase()}`}>{value}</span>;
159}
Note: See TracBrowser for help on using the repository browser.