From 2584984e081ef32aca8766e026e26c828a160275 Mon Sep 17 00:00:00 2001 From: Bennett Fife Date: Sun, 1 Dec 2024 19:46:07 -0800 Subject: [PATCH] initial changes to 'Edit Users' page --- app/edit-user-role-page/page.tsx | 152 +++++++++++++++++++++++++----- components/EditUserRoleDialog.tsx | 92 ++++++------------ components/UserCard.tsx | 146 ++++++++++++++-------------- 3 files changed, 229 insertions(+), 161 deletions(-) diff --git a/app/edit-user-role-page/page.tsx b/app/edit-user-role-page/page.tsx index 48fe50bd..3f11c52a 100644 --- a/app/edit-user-role-page/page.tsx +++ b/app/edit-user-role-page/page.tsx @@ -1,9 +1,24 @@ "use client"; import React, { useState, useEffect } from "react"; -import UserCard from "../../components/UserCard"; -import { Box, Stack, Typography, useMediaQuery, useTheme } from "@mui/material"; +import { + Box, + IconButton, + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Typography, + useMediaQuery, + useTheme, + Snackbar, +} from "@mui/material"; +import EditIcon from "@mui/icons-material/Edit"; import useAuth from "@/hooks/useAuth"; import UnauthorizedPageMessage from "@/components/UnauthorizedPageMessage"; +import EditUserRoleDialog from "@/components/EditUserRoleDialog"; /** * Represents a user. @@ -15,8 +30,9 @@ interface User { email: string; role: string; } + /** - * fetch user info from the server + * Fetch user info from the server * @param setUserInfo */ async function fetchUser(setUserInfo: (userInfo: User[]) => void) { @@ -35,7 +51,6 @@ async function fetchUser(setUserInfo: (userInfo: User[]) => void) { } else { const data = await res.json(); setUserInfo(data); - console.log(data); } } catch (error) { console.error("Error getting user info:", error); @@ -47,35 +62,122 @@ async function fetchUser(setUserInfo: (userInfo: User[]) => void) { * @returns */ const EditUserRolePage = () => { - const [userInfo, setUserInfo] = useState([]); + const [userInfo, setUserInfo] = useState([]); // All users + const [newRole, setNewRole] = useState(""); // User role we're editing + const [selectedUserId, setSelectedUserId] = useState(null); // Selected user ID + const [snackbarMessage, setSnackbarMessage] = useState(null); // Snackbar message for feedback + const [snackbarOpen, setSnackbarOpen] = useState(false); // Snackbar open state + + const { isAuth, user } = useAuth(); + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); useEffect(() => { fetchUser(setUserInfo); }, []); - const { isAuth, user } = useAuth(); - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down('sm')); + // Function to accept the new role and update via API + const acceptNewRole = async (userId: string, role: string) => { + const token = localStorage.getItem("token"); + try { + const apiUrl = process.env.NSC_EVENTS_PUBLIC_API_URL; + const response = await fetch(`${apiUrl}/users/update/${userId}`, { + method: "PATCH", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ role }), + }); + if (response.ok) { + setSnackbarMessage("User role updated successfully!"); + } else { + setSnackbarMessage("Error updating user role!"); + } + } catch (error) { + setSnackbarMessage("Error updating user role!"); + } + setSnackbarOpen(true); + }; + + // Close dialog and update role in parent component + const handleCloseDialog = (role?: string, success?: boolean, userId?: string) => { + if (success && role && userId) { + setNewRole(role); + acceptNewRole(userId, role); + setUserInfo((prevUserInfo) => + prevUserInfo.map( + (user) => (user.id === userId ? { ...user, role } : user) // Update the role of the modified user + ) + ); + } + }; - if (isAuth && (user?.role === 'admin')) { + if (isAuth && user?.role === "admin") { return ( - - - User Management - - {userInfo.map((user, index) => ( - - ))} - - +
+ + User Management + + + + + + + + Name + + E-Mail + + + Role + + Edit + + + + {userInfo.map((user) => ( + + + {user.firstName + " " + user.lastName} + + {user.email} + {user.role.toUpperCase()} + + + + + ))} + +
+
+
+ + {/* Snackbar for feedback */} + setSnackbarOpen(false)} + message={snackbarMessage} + /> +
); - } else { - return + } else { + return ; } }; -export default EditUserRolePage; \ No newline at end of file +export default EditUserRolePage; diff --git a/components/EditUserRoleDialog.tsx b/components/EditUserRoleDialog.tsx index a3d6330f..dac03a7d 100644 --- a/components/EditUserRoleDialog.tsx +++ b/components/EditUserRoleDialog.tsx @@ -1,8 +1,5 @@ import Box from "@mui/material/Box"; import Button from "@mui/material/Button"; -import List from "@mui/material/List"; -import ListItemButton from "@mui/material/ListItemButton"; -import ListItemText from "@mui/material/ListItemText"; import DialogTitle from "@mui/material/DialogTitle"; import DialogContent from "@mui/material/DialogContent"; import DialogActions from "@mui/material/DialogActions"; @@ -11,6 +8,8 @@ import RadioGroup from "@mui/material/RadioGroup"; import Radio from "@mui/material/Radio"; import FormControlLabel from "@mui/material/FormControlLabel"; import React, { useEffect, useRef, useState } from "react"; +import { IconButton } from "@mui/material"; +import EditIcon from "@mui/icons-material/Edit"; const options = ["admin", "creator", "user"]; @@ -19,7 +18,7 @@ interface ConfirmationDialogRawProps { keepMounted: boolean; value: string; open: boolean; - onClose: (value?: string, success?: boolean) => void; + onClose: (value?: string, success?: boolean, userId?: string) => void; } export function ConfirmationDialogRaw(props: ConfirmationDialogRawProps) { @@ -35,11 +34,15 @@ export function ConfirmationDialogRaw(props: ConfirmationDialogRawProps) { } }, [valueProp, open]); - const handleEntering = () => { - if (radioGroupRef.current != null) { - radioGroupRef.current.focus(); + const handleChange = (event: React.ChangeEvent) => { + const newValue = (event.target as HTMLInputElement).value; + setValue(newValue); + if (newValue !== valueProp) { + setRoleUpdated(true); + } else { + setRoleUpdated(false); } - }; + }; const handleCancel = () => { onClose(); @@ -53,25 +56,10 @@ export function ConfirmationDialogRaw(props: ConfirmationDialogRawProps) { } }; - // const handleChange = (event: React.ChangeEvent) => { - // setValue((event.target as HTMLInputElement).value); - // }; - - const handleChange = (event: React.ChangeEvent) => { - const newValue = (event.target as HTMLInputElement).value; - setValue(newValue); - if (newValue !== valueProp) { - setRoleUpdated(true); - } else { - setRoleUpdated(false); - } - }; - return ( @@ -85,12 +73,7 @@ export function ConfirmationDialogRaw(props: ConfirmationDialogRawProps) { onChange={handleChange} > {options.map((option) => ( - } - label={option} - /> + } label={option} /> ))} @@ -98,7 +81,9 @@ export function ConfirmationDialogRaw(props: ConfirmationDialogRawProps) { - + ); @@ -109,49 +94,32 @@ export default function EditUserRoleDialog({ onClose, }: { user: { id: string; role: string }; - onClose: (newRole?: string, success?: boolean) => void; + onClose: (newRole?: string, success?: boolean, userId?: string) => void; }): JSX.Element { const [open, setOpen] = useState(false); const [value, setValue] = useState(user.role); - const handleClickListItem = () => { + const handleClickIcon = () => { setOpen(true); }; const handleClose = (newValue?: string, success?: boolean) => { setOpen(false); - onClose(newValue, success); + onClose(newValue, success, user.id); }; return ( - - - - - - - - + <> + + + + + ); } diff --git a/components/UserCard.tsx b/components/UserCard.tsx index e4018e66..84c89e82 100644 --- a/components/UserCard.tsx +++ b/components/UserCard.tsx @@ -1,10 +1,10 @@ import React, { useState } from "react"; import { Box, Button, Container, Stack, Typography, useMediaQuery, useTheme } from "@mui/material"; import EditIcon from "@mui/icons-material/Edit"; -import CheckIcon from '@mui/icons-material/Check'; +import CheckIcon from "@mui/icons-material/Check"; import EditUserRoleDialog from "./EditUserRoleDialog"; -import Snackbar from '@mui/material/Snackbar'; -import Alert, { AlertColor } from '@mui/material/Alert'; +import Snackbar from "@mui/material/Snackbar"; +import Alert, { AlertColor } from "@mui/material/Alert"; export interface UserCardProps { id: string; @@ -18,11 +18,11 @@ function UserCard({ user }: { user: UserCardProps }) { const [openDialog, setOpenDialog] = useState(false); const [editCompleted, setEditCompleted] = useState(false); const [snackbarOpen, setSnackbarOpen] = useState(false); - const [snackbarMessage, setSnackbarMessage] = useState(''); - const [snackbarSeverity, setSnackbarSeverity] = useState('success'); + const [snackbarMessage, setSnackbarMessage] = useState(""); + const [snackbarSeverity, setSnackbarSeverity] = useState("success"); const [newRole, setNewRole] = useState(user.role); const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down('sm')); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); const handleEditClick = async () => { if (editCompleted) { @@ -34,31 +34,31 @@ function UserCard({ user }: { user: UserCardProps }) { } if (editCompleted) { - const token = localStorage.getItem('token'); + const token = localStorage.getItem("token"); try { const apiUrl = process.env.NSC_EVENTS_PUBLIC_API_URL; const response = await fetch(`${apiUrl}/users/update/${user.id}`, { - method: 'PATCH', + method: "PATCH", headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}` + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, }, - body: JSON.stringify({ role: newRole }) + body: JSON.stringify({ role: newRole }), }); if (response.ok) { - setSnackbarSeverity('success'); - setSnackbarMessage('User role updated successfully!'); + setSnackbarSeverity("success"); + setSnackbarMessage("User role updated successfully!"); setSnackbarOpen(true); } else { - console.error('Failed to update user role:', response.statusText); - setSnackbarSeverity('error'); - setSnackbarMessage('Failed to update user role'); + console.error("Failed to update user role:", response.statusText); + setSnackbarSeverity("error"); + setSnackbarMessage("Failed to update user role"); setSnackbarOpen(true); } } catch (error) { - console.error('Error updating user role:', error); - setSnackbarSeverity('error'); - setSnackbarMessage('Error updating user role'); + console.error("Error updating user role:", error); + setSnackbarSeverity("error"); + setSnackbarMessage("Error updating user role"); setSnackbarOpen(true); } } @@ -76,7 +76,6 @@ function UserCard({ user }: { user: UserCardProps }) { if (editCompleted) { setOpenDialog(false); setEditCompleted(false); - } else { setOpenDialog(false); } @@ -92,76 +91,75 @@ function UserCard({ user }: { user: UserCardProps }) { alignItems: "center", borderRadius: "10px", border: "1px solid #ccc", - padding: "20px", - margin: "20px", + padding: "7px", + margin: "3px", width: isMobile ? "75%" : "auto", }} > - - - - User ID: {user.id} + + + + {user.firstName} {user.lastName} - - Full Name: {user.firstName} {user.lastName} + + {user.email} - - Email: {user.email} - - - Current Role: {user.role} + + {user.role} {editCompleted && ( - - Selected Role: {newRole} - + + {newRole} + )} + {openDialog && ( )} - - {!openDialog && ( - - )} - {openDialog && ( - - )} - {editCompleted && ( - - )} + + + {!openDialog && ( + + )} + {openDialog && ( + + )} + {editCompleted && ( + + )} - setSnackbarOpen(false)} anchorOrigin={{ vertical: "bottom", horizontal: "center" }}> - setSnackbarOpen(false)} severity={snackbarSeverity} sx={{ width: '100%' }}> + setSnackbarOpen(false)} + anchorOrigin={{ vertical: "bottom", horizontal: "center" }} + > + setSnackbarOpen(false)} + severity={snackbarSeverity} + sx={{ width: "100%" }} + > {snackbarMessage}