Skip to content

Commit

Permalink
initial changes to 'Edit Users' page
Browse files Browse the repository at this point in the history
  • Loading branch information
bennettsf committed Dec 2, 2024
1 parent 0e14bea commit 2584984
Show file tree
Hide file tree
Showing 3 changed files with 229 additions and 161 deletions.
152 changes: 127 additions & 25 deletions app/edit-user-role-page/page.tsx
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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) {
Expand All @@ -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);
Expand All @@ -47,35 +62,122 @@ async function fetchUser(setUserInfo: (userInfo: User[]) => void) {
* @returns
*/
const EditUserRolePage = () => {
const [userInfo, setUserInfo] = useState<User[]>([]);
const [userInfo, setUserInfo] = useState<User[]>([]); // All users
const [newRole, setNewRole] = useState<string>(""); // User role we're editing
const [selectedUserId, setSelectedUserId] = useState<string | null>(null); // Selected user ID
const [snackbarMessage, setSnackbarMessage] = useState<string | null>(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 (
<Box sx={{ display: "flex", justifyContent: "center" }}>
<Stack alignItems="center">
<Typography
fontSize={isMobile ? "1.75rem" : "2.25rem"}
textAlign={"center"}
marginTop={"2rem"}
>User Management
</Typography>
{userInfo.map((user, index) => (
<UserCard user={user} key={index} />
))}
</Stack>
</Box>
<div>
<Typography
fontSize={isMobile ? "1.75rem" : "2.25rem"}
textAlign={"center"}
margin={"1.5rem"}
>
User Management
</Typography>

<Box
sx={{
display: "flex",
justifyContent: "center", // Centers horizontally
alignItems: "center", // Centers vertically if needed
width: "100%", // Ensures the Box takes up the full width
maxWidth: "90%", // Ensures the table doesn't stretch beyond 90% of the container
margin: "0 auto", // Centers the Box within the parent container
}}
>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell sx={{ fontWeight: 700 }}>Name</TableCell>
<TableCell sx={{ fontWeight: 700 }} align="left">
E-Mail
</TableCell>
<TableCell sx={{ fontWeight: 700 }} align="left">
Role
</TableCell>
<TableCell sx={{ fontWeight: 700 }}>Edit</TableCell>
</TableRow>
</TableHead>
<TableBody>
{userInfo.map((user) => (
<TableRow key={user.id}>
<TableCell component="th" scope="row">
{user.firstName + " " + user.lastName}
</TableCell>
<TableCell align="left">{user.email}</TableCell>
<TableCell align="left">{user.role.toUpperCase()}</TableCell>
<TableCell>
<EditUserRoleDialog user={user} onClose={handleCloseDialog} />
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Box>

{/* Snackbar for feedback */}
<Snackbar
open={snackbarOpen}
autoHideDuration={6000}
onClose={() => setSnackbarOpen(false)}
message={snackbarMessage}
/>
</div>
);
} else {
return <UnauthorizedPageMessage/>
} else {
return <UnauthorizedPageMessage />;
}
};

export default EditUserRolePage;
export default EditUserRolePage;
92 changes: 30 additions & 62 deletions components/EditUserRoleDialog.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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"];

Expand All @@ -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) {
Expand All @@ -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<HTMLInputElement>) => {
const newValue = (event.target as HTMLInputElement).value;
setValue(newValue);
if (newValue !== valueProp) {
setRoleUpdated(true);
} else {
setRoleUpdated(false);
}
};
};

const handleCancel = () => {
onClose();
Expand All @@ -53,25 +56,10 @@ export function ConfirmationDialogRaw(props: ConfirmationDialogRawProps) {
}
};

// const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
// setValue((event.target as HTMLInputElement).value);
// };

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const newValue = (event.target as HTMLInputElement).value;
setValue(newValue);
if (newValue !== valueProp) {
setRoleUpdated(true);
} else {
setRoleUpdated(false);
}
};

return (
<Dialog
sx={{ "& .MuiDialog-paper": { width: "50%", maxHeight: 435 } }}
maxWidth="xs"
TransitionProps={{ onEntering: handleEntering }}
open={open}
{...other}
>
Expand All @@ -85,20 +73,17 @@ export function ConfirmationDialogRaw(props: ConfirmationDialogRawProps) {
onChange={handleChange}
>
{options.map((option) => (
<FormControlLabel
value={option}
key={option}
control={<Radio />}
label={option}
/>
<FormControlLabel value={option} key={option} control={<Radio />} label={option} />
))}
</RadioGroup>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleCancel}>
Cancel
</Button>
<Button onClick={handleOk} disabled={!roleUpdated}>Ok</Button>
<Button onClick={handleOk} disabled={!roleUpdated}>
Ok
</Button>
</DialogActions>
</Dialog>
);
Expand All @@ -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 (
<Box
sx={{
width: "100%",
maxWidth: 800,
bgcolor: "#fff",
color: "#000",
boxShadow: 1,
borderRadius: "8px"
}}
>
<List component="div" role="group">
<ListItemButton
divider
aria-haspopup="true"
aria-controls="role-menu"
aria-label="User role"
onClick={handleClickListItem}
>
<ListItemText primary="Select User Role" secondary={value} secondaryTypographyProps={{ sx: { color: "#333" } }}/>
</ListItemButton>
<ConfirmationDialogRaw
id="role-menu"
keepMounted
open={open}
onClose={handleClose}
value={value}
/>
</List>
</Box>
<>
<IconButton onClick={handleClickIcon}>
<EditIcon />
</IconButton>
<ConfirmationDialogRaw
id="role-menu"
keepMounted
open={open}
onClose={handleClose}
value={value}
/>
</>
);
}
Loading

0 comments on commit 2584984

Please sign in to comment.