Skip to content

Commit

Permalink
belinda-nextjs-03-228-add-edit-user-role-page (#229)
Browse files Browse the repository at this point in the history
* add edit user role page

* update code formatter
  • Loading branch information
tinpham5614 authored Feb 23, 2024
1 parent fff28fc commit f3e075b
Show file tree
Hide file tree
Showing 3 changed files with 332 additions and 0 deletions.
83 changes: 83 additions & 0 deletions app/edit-user-role-page/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"use client";
import React, { useState, useEffect } from "react";
import UserCard from "../../components/UserCard";
import { Box, Container, Typography } from "@mui/material";

/**
* Represents a user.
*/
interface User {
id: string;
firstName: string;
lastName: string;
email: string;
role: string;
}
/**
* fetch user info from the server
* @param setUserInfo
*/
async function fetchUser(setUserInfo: (userInfo: User[]) => void) {
const apiUrl = "http://localhost:3000/api/user";
try {
const res = await fetch(apiUrl, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
if (!res.ok) {
throw new Error(res.statusText);
} else {
const data = await res.json();
setUserInfo(data);
console.log(data);
}
} catch (error) {
console.error("Error getting user info:", error);
}
}

/**
* Edit user role page
* @returns
*/
const EditUserRolePage = () => {
const [userInfo, setUserInfo] = useState<User[]>([]);

useEffect(() => {
fetchUser(setUserInfo);
}, []);

return (
<Container
fixed
maxWidth="lg"
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
bgcolor: "#12202d",
}}
>
<Box
width={800}
display="flex"
alignItems="center"
flexDirection="column"
gap={2}
bgcolor="#293745"
p={3}
>
<Typography component="h1" variant="h4">
User Management
</Typography>
{userInfo.map((user, index) => (
<UserCard user={user} key={index} />
))}
</Box>
</Container>
);
};

export default EditUserRolePage;
174 changes: 174 additions & 0 deletions components/EditUserRoleDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
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";
import Dialog from "@mui/material/Dialog";
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 { UserCardProps } from "./UserCard";

const options = ["admin", "creator", "user"];

/**
* Props for the ConfirmationDialogRaw component.
*/
interface ConfirmationDialogRawProps {
id: string;
keepMounted: boolean;
value: string;
open: boolean;
onClose: (value?: string) => void;
}
/**
* Renders a confirmation dialog for editing user roles.
*
* @param props - The component props.
* @returns The rendered ConfirmationDialogRaw component.
*/
export function ConfirmationDialogRaw(props: ConfirmationDialogRawProps) {
const { onClose, value: valueProp, open, ...other } = props;
const [value, setValue] = useState(valueProp);
const radioGroupRef = useRef<HTMLElement>(null);

useEffect(() => {
if (!open) {
setValue(valueProp);
}
}, [valueProp, open]);

/**
* Handles the entering event of the dialog.
*/
const handleEntering = () => {
if (radioGroupRef.current != null) {
radioGroupRef.current.focus();
}
};

/**
* Handles the cancel action.
*/
const handleCancel = () => {
onClose();
};

/**
* Handles the click event when the user confirms the role update.
* @returns {void}
*/
const handleOk = () => {
onClose(value);
// TODO: Update user role in the database
};

/**
* Handles the change event of the input element.
* @param event - The change event object.
*/
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue((event.target as HTMLInputElement).value);
};

return (
<Dialog
sx={{ "& .MuiDialog-paper": { width: "50%", maxHeight: 435 } }}
maxWidth="xs"
TransitionProps={{ onEntering: handleEntering }}
open={open}
{...other}
>
<DialogTitle>User Role</DialogTitle>
<DialogContent dividers>
<RadioGroup
ref={radioGroupRef}
aria-label="role"
name="role"
value={value}
onChange={handleChange}
>
{options.map((option) => (
<FormControlLabel
value={option}
key={option}
control={<Radio />}
label={option}
/>
))}
</RadioGroup>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleCancel}>
Cancel
</Button>
<Button onClick={handleOk}>Ok</Button>
</DialogActions>
</Dialog>
);
}

/**
* Renders a dialog for editing the user role.
*
* @component
* @param {Object} props - The component props.
* @param {UserCardProps} props.user - The user object containing user information.
* @param {Function} props.onClose - The function to be called when the dialog is closed.
* @returns {JSX.Element} The rendered EditUserRoleDialog component.
*/
export default function EditUserRoleDialog({
user,
}: {
user: UserCardProps;
onClose: () => void;
}) {
const [open, setOpen] = useState(false);
const [value, setValue] = useState(user.role);

const handleClickListItem = () => {
setOpen(true);
};

const handleClose = (newValue?: string) => {
setOpen(false);

if (newValue) {
setValue(newValue);
}
};

return (
<Box
sx={{
width: "100%",
maxWidth: 800,
bgcolor: "background.paper",
color: "#000",
}}
>
<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} />
</ListItemButton>
<ConfirmationDialogRaw
id="role-menu"
keepMounted
open={open}
onClose={handleClose}
value={value}
/>
</List>
</Box>
);
}
75 changes: 75 additions & 0 deletions components/UserCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React, { useState } from "react";
import { Box, Button, Container, Stack, Typography } from "@mui/material";
import EditIcon from "@mui/icons-material/Edit";
import EditUserRoleDialog from "./EditUserRoleDialog";

export interface UserCardProps {
id: string;
firstName: string;
lastName: string;
email: string;
role: string;
}

function UserCard({ user }: { user: UserCardProps }) {
const [openDialog, setOpenDialog] = useState(false);

const handleEditClick = () => {
setOpenDialog(true);
};

const handleCloseDialog = () => {
setOpenDialog(false);
};
return (
<Container
fixed
maxWidth="lg"
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
borderRadius: "10px",
bgcolor: "#12202d",
padding: "20px",
margin: "20px",
}}
>
<Stack
direction="column"
spacing={2}
alignItems="center"
justifyContent="center"
>
<Typography variant="body1" gutterBottom>
User ID: {user.id}
</Typography>
<Typography variant="body1" gutterBottom>
Full Name: {user.firstName} {user.lastName}
</Typography>
<Typography variant="body1" gutterBottom>
Email: {user.email}
</Typography>
<Typography variant="body1" gutterBottom>
Current Role: {user.role}
</Typography>
{openDialog && (
<Box display="flex" justifyContent="center">
<EditUserRoleDialog user={user} onClose={handleCloseDialog} />
</Box>
)}
<Box p={2} display="flex" justifyContent="center">
<Button
variant="contained"
color="primary"
startIcon={<EditIcon />}
onClick={handleEditClick}
>
Edit
</Button>
</Box>
</Stack>
</Container>
);
}
export default UserCard;

0 comments on commit f3e075b

Please sign in to comment.