Skip to content

Commit

Permalink
belindas-closet-nextjs_10_517_edit-user-role-cancel-button (#524)
Browse files Browse the repository at this point in the history
* belindas-closet-nextjs_10_517_edit-user-role-cancel-button

Resolves #517

This PR implements a Cancel button for when changes haven't been made to the user role. The Done button is available along with the Cancel button when changes are made, so the user can choose to go on and have the page reloaded with the new changes or not.

* Added styling and responsive UI
  • Loading branch information
intisarosman1 authored Jul 30, 2024
1 parent 344e3df commit 9590ac2
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 105 deletions.
81 changes: 10 additions & 71 deletions components/EditUserRoleDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import Radio from "@mui/material/Radio";
import FormControlLabel from "@mui/material/FormControlLabel";
import React, { useEffect, useRef, useState } from "react";
import { UserCardProps } from "./UserCard";
import Snackbar from "@mui/material/Snackbar";
import Alert, { AlertColor } from "@mui/material/Alert";
import { useMediaQuery, useTheme } from "@mui/material";
// WARNING: You won't be able to connect to local backend unless you remove the env variable below.
const URL =
Expand All @@ -29,12 +27,7 @@ interface ConfirmationDialogRawProps {
keepMounted: boolean;
value: string;
open: boolean;
onClose: (value?: string) => void;
setSnackbarOpen: (open: boolean) => void;
setSnackbarMessage: (message: string) => void;
setSnackbarSeverity: (
severity: "error" | "warning" | "info" | "success"
) => void;
onClose: (value?: string, success?: boolean) => void;
}

/**
Expand Down Expand Up @@ -74,39 +67,11 @@ export function ConfirmationDialogRaw(
onClose();
};

/**
* Handles the click event when the user confirms the role update.
* @returns {void}
*/
const handleOk = async () => {
const token = localStorage.getItem("token");
console.log("Token:", token);
// TODO: Update user role in the database
try {
const response = await fetch(`${URL}/user/update/${user.id}`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({ role: value }),
});
if (response.ok) {
onClose(value);
props.setSnackbarSeverity("success");
props.setSnackbarMessage("User role updated successfully!");
props.setSnackbarOpen(true);
} else {
console.error("Failed to update user role:", response.statusText);
props.setSnackbarSeverity("error");
props.setSnackbarMessage("Failed to update user role");
props.setSnackbarOpen(true);
}
} catch (error) {
console.error("Error updating user role:", error);
props.setSnackbarSeverity("error");
props.setSnackbarMessage("Error updating user role");
props.setSnackbarOpen(true);
const handleOk = () => {
if (value !== valueProp) {
onClose(value, true);
} else {
onClose();
}
};

Expand Down Expand Up @@ -175,30 +140,21 @@ export function ConfirmationDialogRaw(
*/
export default function EditUserRoleDialog({
user,
onRoleChange,
onClose,
}: {
user: UserCardProps;
onClose: () => void;
onRoleChange: (newRole: string) => void;
onClose: (newRole?: string, success?: boolean) => void;
}): JSX.Element {
const [open, setOpen] = useState(false);
const [value, setValue] = useState(user.role);
const [snackbarOpen, setSnackbarOpen] = useState(false);
const [snackbarMessage, setSnackbarMessage] = useState("");
const [snackbarSeverity, setSnackbarSeverity] =
useState<AlertColor>("success");

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

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

if (newValue) {
setValue(newValue);
onRoleChange(newValue);
}
onClose(newValue, success);
};

const theme = useTheme();
Expand Down Expand Up @@ -240,25 +196,8 @@ export default function EditUserRoleDialog({
onClose={handleClose}
value={value}
user={user}
setSnackbarOpen={setSnackbarOpen}
setSnackbarMessage={setSnackbarMessage}
setSnackbarSeverity={setSnackbarSeverity}
/>
</List>
<Snackbar
open={snackbarOpen}
autoHideDuration={6000}
onClose={() => setSnackbarOpen(false)}
anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
>
<Alert
onClose={() => setSnackbarOpen(false)}
severity={snackbarSeverity}
sx={{ width: "100%" }}
>
{snackbarMessage}
</Alert>
</Snackbar>
</Box>
);
}
142 changes: 108 additions & 34 deletions components/UserCard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React, { useState } from "react";
import { Box, Button, Container, Stack, Typography } from "@mui/material";
import { Box, Button, Container, Snackbar, Stack, Typography, useMediaQuery, useTheme } from "@mui/material";
import EditIcon from "@mui/icons-material/Edit";
import CheckIcon from '@mui/icons-material/Check';
import EditUserRoleDialog from "./EditUserRoleDialog";
import Alert, { AlertColor } from '@mui/material/Alert';

export interface UserCardProps {
id: string;
Expand All @@ -13,30 +15,72 @@ export interface UserCardProps {

function UserCard({ user }: { user: UserCardProps }) {
const [openDialog, setOpenDialog] = useState(false);
const [menuOpen, setMenuOpen] = useState(false);
const [currentRole, setCurrentRole] = useState(user.role);
const [editCompleted, setEditCompleted] = useState(false);
const [snackbarOpen, setSnackbarOpen] = useState(false);
const [snackbarMessage, setSnackbarMessage] = useState('');
const [snackbarSeverity, setSnackbarSeverity] = useState<AlertColor>('success');
const [newRole, setNewRole] = useState(user.role);
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));

const handleEditClick = () => {
if (menuOpen) {
setMenuOpen(false);
const handleEditClick = async () => {
if (editCompleted) {
window.location.reload();
} else if (openDialog) {
setOpenDialog(false);
if (currentRole !== user.role) {
window.location.reload();
}
} else {
setOpenDialog(true);
setMenuOpen(true);
}
};

const handleRoleChange = (newRole: string) => {
setCurrentRole(newRole);
if (editCompleted) {
const token = localStorage.getItem('token');
try {
const apiUrl = process.env.BELINDAS_CLOSET_PUBLIC_API_URL || `http://localhost:3000/api`;
const response = await fetch(`${apiUrl}/user/update/${user.id}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({ role: newRole })
});
if (response.ok) {
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');
setSnackbarOpen(true);
}
} catch (error) {
console.error('Error updating user role:', error);
setSnackbarSeverity('error');
setSnackbarMessage('Error updating user role');
setSnackbarOpen(true);
}
}
};

const handleCloseDialog = () => {
const handleCloseDialog = (newRole?: string, success?: boolean) => {
setOpenDialog(false);
setMenuOpen(false);
if (success && newRole) {
setNewRole(newRole);
setEditCompleted(true);
}
};

const handleCancel = () => {
if (editCompleted) {
setOpenDialog(false);
setEditCompleted(false);

} else {
setOpenDialog(false);
}
};

return (
<Container
fixed
Expand All @@ -55,38 +99,68 @@ function UserCard({ user }: { user: UserCardProps }) {
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>
<Box sx={{ textAlign: "center" }}>
<Typography variant="body1" gutterBottom sx={{ mt: 1, mb: isMobile ? 1 : 2 }}>
User ID: {user.id}
</Typography>
<Typography variant="body1" gutterBottom sx={{ mb: isMobile ? 1 : 2 }}>
Full Name: {user.firstName} {user.lastName}
</Typography>
<Typography variant="body1" gutterBottom sx={{ mb: isMobile ? 1 : 2 }}>
Email: {user.email}
</Typography>
<Typography variant="body1" gutterBottom sx={{ mb: isMobile ? 1 : 2 }}>
Current Role: {user.role}
</Typography>
{editCompleted && (
<Typography variant="body1" gutterBottom>
Selected Role: {newRole}
</Typography>
)}
</Box>
{openDialog && (
<Box display="flex" justifyContent="center">
<EditUserRoleDialog
user={user}
onClose={handleCloseDialog}
onRoleChange={handleRoleChange}
/>
<EditUserRoleDialog user={user} onClose={handleCloseDialog} />
</Box>
)}
<Box p={2} display="flex" justifyContent="center">
{!openDialog && (
<Button
variant="contained"
color="primary"
startIcon={<EditIcon />}
endIcon={editCompleted ? "" : <EditIcon />}
onClick={editCompleted ? handleCancel : handleEditClick}
>
{editCompleted ? "Cancel" : "Edit"}
</Button>
)}
{openDialog && (
<Button
variant="contained"
color="primary"
onClick={handleCancel}
>
Cancel
</Button>
)}
{editCompleted && (
<Button
variant="contained"
color="primary"
sx={{ ml: 2 }}
endIcon={<CheckIcon />}
onClick={handleEditClick}
>
{menuOpen ? "Done" : "Edit"}
Done
</Button>
)}
</Box>
</Stack>
<Snackbar open={snackbarOpen} autoHideDuration={6000} onClose={() => setSnackbarOpen(false)} anchorOrigin={{ vertical: "bottom", horizontal: "center" }}>
<Alert onClose={() => setSnackbarOpen(false)} severity={snackbarSeverity} sx={{ width: '100%' }}>
{snackbarMessage}
</Alert>
</Snackbar>
</Container>
);
}
Expand Down

0 comments on commit 9590ac2

Please sign in to comment.