-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
belinda-nextjs-03-228-add-edit-user-role-page (#229)
* add edit user role page * update code formatter
- Loading branch information
1 parent
fff28fc
commit f3e075b
Showing
3 changed files
with
332 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |