From 159cac6460fb2a42456c6f9a44cbcdb03b938823 Mon Sep 17 00:00:00 2001 From: Pinapelz Date: Wed, 3 Sep 2025 21:45:38 -0700 Subject: implement admin dashboard frontend and handle game creation --- frontend/src/App.tsx | 2 + frontend/src/contexts/AuthContext.tsx | 6 +- frontend/src/pages/Admin.tsx | 205 ++++++++++++++++++++++++++++++++++ frontend/src/utils/authApi.ts | 1 + 4 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 frontend/src/pages/Admin.tsx (limited to 'frontend') diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 9c4e8bd..9b0e058 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -7,6 +7,7 @@ import Import from "./pages/Import"; import Home from "./pages/Home"; import Score from "./pages/Score"; import Chart from "./pages/Chart"; +import Admin from "./pages/Admin"; function App() { return ( @@ -19,6 +20,7 @@ function App() { } /> } /> } /> + } /> ); diff --git a/frontend/src/contexts/AuthContext.tsx b/frontend/src/contexts/AuthContext.tsx index 8abeeb4..09e4864 100644 --- a/frontend/src/contexts/AuthContext.tsx +++ b/frontend/src/contexts/AuthContext.tsx @@ -4,9 +4,10 @@ import { authApi } from '../utils/authApi'; import type { User as ApiUser, SessionResponse } from '../utils/authApi'; interface User { - id: string; + id: number; username: string; email: string; + isAdmin: boolean; } interface AuthContextType { @@ -40,9 +41,10 @@ export const AuthProvider: React.FC = ({ children }) => { const isAuthenticated = user !== null; const transformApiUser = (apiUser: ApiUser): User => ({ - id: apiUser.id.toString(), + id: apiUser.id, username: apiUser.username, email: apiUser.email, + isAdmin: apiUser.isAdmin, }); const checkAuth = async () => { diff --git a/frontend/src/pages/Admin.tsx b/frontend/src/pages/Admin.tsx new file mode 100644 index 0000000..f494fc2 --- /dev/null +++ b/frontend/src/pages/Admin.tsx @@ -0,0 +1,205 @@ +import { useNavigate } from "react-router"; +import { NavBar } from "../components/NavBar"; +import { useAuth } from "../contexts/AuthContext"; +import SessionExpiredPopup from "../components/SessionExpiredPopup"; +import { useState } from "react"; + + +const Admin = () => { + const { user, isLoading, logout } = useAuth(); + const [showAddGame, setShowAddGame] = useState(false); + const [formData, setFormData] = useState({ + gameInternalName: '', + gameFormattedName: '', + gameDescription: '' + }); + const [isSubmitting, setIsSubmitting] = useState(false); + const navigate = useNavigate(); + + const handleLogout = async () => { + try { + await logout(); + navigate("/"); + } catch (error) { + console.error("Logout failed:", error); + alert("Network error during logout. Please try again."); + } + }; + + const handleInputChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setFormData(prev => ({ + ...prev, + [name]: value + })); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!formData.gameInternalName || !formData.gameFormattedName || !formData.gameDescription) { + alert('Please fill in all fields'); + return; + } + + setIsSubmitting(true); + + try { + const response = await fetch(import.meta.env.VITE_API_URL + '/admin/createGame', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + credentials: 'include', + body: JSON.stringify(formData), + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.error || 'Failed to create game'); + } + + alert('Game created successfully!'); + setFormData({ + gameInternalName: '', + gameFormattedName: '', + gameDescription: '' + }); + setShowAddGame(false); + + } catch (error) { + console.error('Failed to create game:', error); + alert(error instanceof Error ? error.message : 'Failed to create game'); + } finally { + setIsSubmitting(false); + } + }; + + if (isLoading) { + return ( +
+
+
+

Loading Admin dashboard...

+
+
+ ); + } + + if (!user) { + return ; + } + if(!user.isAdmin && user.id != 1){ + console.log(user.id == 1) + return
+
+
+

You are not authorized to access this page.

+
+
; + } + + return ( +
+ + + {/* Main Content */} +
+ {/* Header */} +
+

Admin Page

+

+ Welcome Mirage Webmaster! Here are a variety of settings and tools you can use to customize the experience +

+
+ {/* Add New Game Section */} +
+
+ + {showAddGame && ( +
+

+ This form allows you to add a new game to Mirage. By default, Mirage will attempt to derive a method of showing the game's score on its own. + You may override this behavior by writing your own custom score display logic. +

+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+ )} +
+
+
+
+ ); +}; + +export default Admin; diff --git a/frontend/src/utils/authApi.ts b/frontend/src/utils/authApi.ts index 469553d..1d6cc70 100644 --- a/frontend/src/utils/authApi.ts +++ b/frontend/src/utils/authApi.ts @@ -155,6 +155,7 @@ export interface User { id: number; username: string; email: string; + isAdmin: boolean; } export interface SessionResponse { -- cgit v1.2.3