Seth0330 commited on
Commit
0ec6899
·
verified ·
1 Parent(s): 0db8b08

Update frontend/src/pages/AdminDashboard.jsx

Browse files
Files changed (1) hide show
  1. frontend/src/pages/AdminDashboard.jsx +166 -91
frontend/src/pages/AdminDashboard.jsx CHANGED
@@ -3,8 +3,11 @@ import React, { useState } from "react";
3
  import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
4
  import { Users, Calendar, AlertCircle, Plus, Sparkles } from "lucide-react";
5
  import { differenceInDays } from "date-fns";
 
6
 
7
  import api from "../api/client";
 
 
8
  import OverviewCard from "../components/admin/OverviewCard";
9
  import AIInsightBox from "../components/admin/AIInsightBox";
10
  import PlanForm from "../components/admin/PlanForm";
@@ -13,12 +16,22 @@ import AdminAIAssistant from "../components/admin/AdminAIAssistant";
13
 
14
  export default function AdminDashboard() {
15
  const queryClient = useQueryClient();
 
16
 
17
  const [showPlanForm, setShowPlanForm] = useState(false);
18
  const [editingPlan, setEditingPlan] = useState(null);
19
  const [showAIChat, setShowAIChat] = useState(false);
20
  const [planError, setPlanError] = useState("");
21
 
 
 
 
 
 
 
 
 
 
22
  // MEMBERSHIPS
23
  const membershipsQuery = useQuery({
24
  queryKey: ["all-memberships"],
@@ -122,122 +135,184 @@ export default function AdminDashboard() {
122
  }
123
  };
124
 
 
 
 
 
 
 
 
 
 
125
  return (
126
- <div className="min-h-screen bg-stone-50">
127
- <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6 sm:py-8">
128
- {/* Header matches Base44 layout */}
129
- <div className="mb-8">
130
- <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
131
- <div>
132
- <h1 className="text-2xl sm:text-3xl font-bold text-stone-900">
133
- Admin Dashboard
134
- </h1>
135
- <p className="text-stone-600 mt-1">
136
- Manage memberships and monitor your dojo
137
- </p>
138
- </div>
139
- <button
140
- type="button"
141
- onClick={() => setShowAIChat(true)}
142
- className="inline-flex items-center gap-2 rounded-lg bg-purple-600 hover:bg-purple-700 text-white text-sm font-medium px-4 py-2 shadow-sm"
143
- >
144
- <Sparkles className="w-4 h-4" />
145
- AI Assistant
146
- </button>
147
  </div>
 
148
  </div>
149
-
150
- {/* Overview Cards */}
151
- <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6 mb-8">
152
- <OverviewCard
153
- title="Active Members"
154
- value={membershipsLoading ? "…" : activeMembers}
155
- icon={Users}
156
- color="blue"
157
- trend={`${memberships.length} total`}
158
- />
159
- <OverviewCard
160
- title="Renewing Soon"
161
- value={membershipsLoading ? "…" : renewingSoon}
162
- icon={Calendar}
163
- color="amber"
164
- trend="Next 14 days"
165
- />
166
- <OverviewCard
167
- title="Expired"
168
- value={membershipsLoading ? "…" : expired}
169
- icon={AlertCircle}
170
- color="red"
171
- trend="Needs attention"
172
- />
 
173
  </div>
 
174
 
175
- {/* AI Insight Box */}
176
- <AIInsightBox memberships={memberships} />
 
 
 
 
 
 
 
 
 
 
 
 
177
 
178
- {/* Plan Management – card layout similar to Base44 */}
179
- <div className="mb-8 bg-white border border-stone-200 rounded-2xl shadow-sm">
180
- <div className="px-4 sm:px-6 py-4 border-b border-stone-100">
 
181
  <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
182
  <div>
183
- <h2 className="text-base font-semibold text-stone-900">
184
- Membership Plans
185
- </h2>
186
- <p className="text-sm text-stone-500">
187
- Create and manage membership offerings
188
  </p>
189
  </div>
190
  <button
191
  type="button"
192
- onClick={() => {
193
- setEditingPlan(null);
194
- setShowPlanForm((v) => !v);
195
- setPlanError("");
196
- }}
197
- className="inline-flex items-center gap-2 rounded-lg bg-red-600 hover:bg-red-700 text-white text-sm font-medium px-4 py-2 shadow-sm"
198
  >
199
- <Plus className="w-4 h-4" />
200
- New Plan
201
  </button>
202
  </div>
203
  </div>
204
 
205
- <div className="px-4 sm:px-6 py-4">
206
- {planError && (
207
- <p className="mb-4 text-sm text-red-600 bg-red-50 border border-red-100 rounded-md px-3 py-2">
208
- {planError}
209
- </p>
210
- )}
211
-
212
- {showPlanForm && (
213
- <div className="mb-6">
214
- <PlanForm
215
- plan={editingPlan}
216
- onSave={handleSavePlan}
217
- onCancel={() => {
218
- setShowPlanForm(false);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  setEditingPlan(null);
 
220
  setPlanError("");
221
  }}
222
- isLoading={
223
- createPlanMutation.isPending ||
224
- updatePlanMutation.isPending
225
- }
226
- />
227
  </div>
228
- )}
229
 
230
- <PlansTable
231
- plans={plans}
232
- isLoading={plansLoading}
233
- onEdit={handleEditPlan}
234
- onDelete={handleDeletePlan}
235
- />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  </div>
237
- </div>
238
  </div>
239
 
240
- {/* AI Assistant */}
241
  <AdminAIAssistant
242
  open={showAIChat}
243
  onClose={() => setShowAIChat(false)}
 
3
  import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
4
  import { Users, Calendar, AlertCircle, Plus, Sparkles } from "lucide-react";
5
  import { differenceInDays } from "date-fns";
6
+ import { Link, useNavigate } from "react-router-dom";
7
 
8
  import api from "../api/client";
9
+ import AppHeader from "../components/AppHeader";
10
+ import UserMenu from "../components/UserMenu";
11
  import OverviewCard from "../components/admin/OverviewCard";
12
  import AIInsightBox from "../components/admin/AIInsightBox";
13
  import PlanForm from "../components/admin/PlanForm";
 
16
 
17
  export default function AdminDashboard() {
18
  const queryClient = useQueryClient();
19
+ const navigate = useNavigate();
20
 
21
  const [showPlanForm, setShowPlanForm] = useState(false);
22
  const [editingPlan, setEditingPlan] = useState(null);
23
  const [showAIChat, setShowAIChat] = useState(false);
24
  const [planError, setPlanError] = useState("");
25
 
26
+ const storedAdmin = JSON.parse(sessionStorage.getItem("admin") || "{}");
27
+ const adminName = storedAdmin.name || "Admin";
28
+ const adminEmail = storedAdmin.email || "admin@dojo.com";
29
+
30
+ function handleAdminLogout() {
31
+ sessionStorage.removeItem("admin");
32
+ navigate("/login");
33
+ }
34
+
35
  // MEMBERSHIPS
36
  const membershipsQuery = useQuery({
37
  queryKey: ["all-memberships"],
 
135
  }
136
  };
137
 
138
+ // simple nav config for left sidebar
139
+ const navItems = [
140
+ { label: "Dashboard", to: "/admin" },
141
+ { label: "Classes", to: "/admin/classes" },
142
+ { label: "Exams", to: "/admin/exams" },
143
+ { label: "Competitions", to: "/admin/competitions" },
144
+ { label: "Shop", to: "/admin/products" },
145
+ ];
146
+
147
  return (
148
+ <div className="min-h-screen bg-stone-50 flex">
149
+ {/* LEFT SIDEBAR */}
150
+ <aside className="hidden md:flex w-60 flex-col border-r border-stone-200 bg-white">
151
+ <div className="px-5 pt-6 pb-4 border-b border-stone-100">
152
+ <div className="text-sm font-semibold text-stone-900">
153
+ Karate Dojo
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  </div>
155
+ <div className="text-xs text-stone-500 mt-0.5">Admin Portal</div>
156
  </div>
157
+ <nav className="flex-1 px-3 py-4 space-y-1">
158
+ {navItems.map((item) => {
159
+ const isActive = item.to === "/admin"; // only dashboard active for now
160
+ return (
161
+ <Link
162
+ key={item.to}
163
+ to={item.to}
164
+ className={`flex items-center gap-2 rounded-xl px-3 py-2 text-sm ${
165
+ isActive
166
+ ? "bg-rose-50 text-rose-700 font-semibold"
167
+ : "text-stone-600 hover:bg-stone-50"
168
+ }`}
169
+ >
170
+ {item.label}
171
+ </Link>
172
+ );
173
+ })}
174
+ </nav>
175
+ <div className="px-4 pb-5">
176
+ <div className="rounded-2xl border border-rose-100 bg-rose-50 px-3 py-2">
177
+ <div className="text-[11px] font-semibold text-rose-700">
178
+ Admin Mode
179
+ </div>
180
+ <div className="text-[11px] text-rose-500">Full Access</div>
181
+ </div>
182
  </div>
183
+ </aside>
184
 
185
+ {/* MAIN AREA */}
186
+ <div className="flex-1 flex flex-col">
187
+ {/* TOP HEADER: brand + user menu */}
188
+ <AppHeader
189
+ title="Karate Dojo"
190
+ subtitle="Admin Portal"
191
+ right={
192
+ <UserMenu
193
+ name={adminName}
194
+ email={adminEmail}
195
+ onLogout={handleAdminLogout}
196
+ />
197
+ }
198
+ />
199
 
200
+ {/* PAGE CONTENT */}
201
+ <main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6 sm:py-8">
202
+ {/* Page heading + AI button (Base44 style) */}
203
+ <div className="mb-8">
204
  <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
205
  <div>
206
+ <h1 className="text-2xl sm:text-3xl font-bold text-stone-900">
207
+ Admin Dashboard
208
+ </h1>
209
+ <p className="text-stone-600 mt-1">
210
+ Manage memberships and monitor your dojo
211
  </p>
212
  </div>
213
  <button
214
  type="button"
215
+ onClick={() => setShowAIChat(true)}
216
+ className="inline-flex items-center gap-2 rounded-lg bg-purple-600 hover:bg-purple-700 text-white text-sm font-medium px-4 py-2 shadow-sm"
 
 
 
 
217
  >
218
+ <Sparkles className="w-4 h-4" />
219
+ AI Assistant
220
  </button>
221
  </div>
222
  </div>
223
 
224
+ {/* Overview cards */}
225
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6 mb-8">
226
+ <OverviewCard
227
+ title="Active Members"
228
+ value={membershipsLoading ? "…" : activeMembers}
229
+ icon={Users}
230
+ color="blue"
231
+ trend={`${memberships.length} total`}
232
+ />
233
+ <OverviewCard
234
+ title="Renewing Soon"
235
+ value={membershipsLoading ? "…" : renewingSoon}
236
+ icon={Calendar}
237
+ color="amber"
238
+ trend="Next 14 days"
239
+ />
240
+ <OverviewCard
241
+ title="Expired"
242
+ value={membershipsLoading ? "…" : expired}
243
+ icon={AlertCircle}
244
+ color="red"
245
+ trend="Needs attention"
246
+ />
247
+ </div>
248
+
249
+ {/* AI Assistant Insights */}
250
+ <AIInsightBox memberships={memberships} />
251
+
252
+ {/* Membership Plans */}
253
+ <div className="mb-8 bg-white border border-stone-200 rounded-2xl shadow-sm">
254
+ <div className="px-4 sm:px-6 py-4 border-b border-stone-100">
255
+ <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
256
+ <div>
257
+ <h2 className="text-base font-semibold text-stone-900">
258
+ Membership Plans
259
+ </h2>
260
+ <p className="text-sm text-stone-500">
261
+ Create and manage membership offerings
262
+ </p>
263
+ </div>
264
+ <button
265
+ type="button"
266
+ onClick={() => {
267
  setEditingPlan(null);
268
+ setShowPlanForm((v) => !v);
269
  setPlanError("");
270
  }}
271
+ className="inline-flex items-center gap-2 rounded-lg bg-red-600 hover:bg-red-700 text-white text-sm font-medium px-4 py-2 shadow-sm"
272
+ >
273
+ <Plus className="w-4 h-4" />
274
+ New Plan
275
+ </button>
276
  </div>
277
+ </div>
278
 
279
+ <div className="px-4 sm:px-6 py-4">
280
+ {planError && (
281
+ <p className="mb-4 text-sm text-red-600 bg-red-50 border border-red-100 rounded-md px-3 py-2">
282
+ {planError}
283
+ </p>
284
+ )}
285
+
286
+ {showPlanForm && (
287
+ <div className="mb-6">
288
+ <PlanForm
289
+ plan={editingPlan}
290
+ onSave={handleSavePlan}
291
+ onCancel={() => {
292
+ setShowPlanForm(false);
293
+ setEditingPlan(null);
294
+ setPlanError("");
295
+ }}
296
+ isLoading={
297
+ createPlanMutation.isPending ||
298
+ updatePlanMutation.isPending
299
+ }
300
+ />
301
+ </div>
302
+ )}
303
+
304
+ <PlansTable
305
+ plans={plans}
306
+ isLoading={plansLoading}
307
+ onEdit={handleEditPlan}
308
+ onDelete={handleDeletePlan}
309
+ />
310
+ </div>
311
  </div>
312
+ </main>
313
  </div>
314
 
315
+ {/* AI chat modal */}
316
  <AdminAIAssistant
317
  open={showAIChat}
318
  onClose={() => setShowAIChat(false)}