feat(auth): add loading state for user and role management

This commit is contained in:
Rıdvan Akca
2023-07-31 17:56:31 +03:00
parent 9e64af4793
commit d3bc3a796b
7 changed files with 204 additions and 145 deletions

View File

@@ -9,7 +9,7 @@ import useFormatMessage from 'hooks/useFormatMessage';
type DeleteRoleButtonProps = {
roleId: string;
}
};
export default function DeleteRoleButton(props: DeleteRoleButtonProps) {
const { roleId } = props;

View File

@@ -9,7 +9,7 @@ import useFormatMessage from 'hooks/useFormatMessage';
type DeleteUserButtonProps = {
userId: string;
}
};
export default function DeleteUserButton(props: DeleteUserButtonProps) {
const { userId } = props;

View File

@@ -0,0 +1,47 @@
import {
IconButton,
Skeleton,
Stack,
TableCell,
TableRow,
} from '@mui/material';
import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/Delete';
type ListLoaderProps = {
rowsNumber: number;
cellNumber: number;
};
const ListLoader = ({ rowsNumber, cellNumber }: ListLoaderProps) => {
return (
<>
{[...Array(rowsNumber)].map((row, index) => (
<TableRow
key={index}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
>
{[...Array(cellNumber)].map((cell, index) => (
<TableCell key={index} scope="row">
<Skeleton />
</TableCell>
))}
<TableCell>
<Stack direction="row" gap={1} justifyContent="right">
<IconButton size="small">
<EditIcon />
</IconButton>
<IconButton size="small">
<DeleteIcon />
</IconButton>
</Stack>
</TableCell>
</TableRow>
))}
</>
);
};
export default ListLoader;

View File

@@ -13,15 +13,15 @@ import Typography from '@mui/material/Typography';
import EditIcon from '@mui/icons-material/Edit';
import DeleteRoleButton from 'components/DeleteRoleButton/index.ee';
import ListLoader from 'components/ListLoader';
import useFormatMessage from 'hooks/useFormatMessage';
import useRoles from 'hooks/useRoles.ee';
import * as URLS from 'config/urls';
// TODO: introduce interaction feedback upon deletion (successful + failure)
// TODO: introduce loading bar
export default function RoleList(): React.ReactElement {
const formatMessage = useFormatMessage();
const { roles } = useRoles();
const { roles, loading } = useRoles();
return (
<TableContainer component={Paper}>
@@ -50,23 +50,20 @@ export default function RoleList(): React.ReactElement {
</TableRow>
</TableHead>
<TableBody>
{roles.map((role) => (
{loading ? (
<ListLoader rowsNumber={3} cellNumber={2} />
) : (
roles.map((role) => (
<TableRow
key={role.id}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
>
<TableCell scope="row">
<Typography
variant="subtitle2"
>
{role.name}
</Typography>
<Typography variant="subtitle2">{role.name}</Typography>
</TableCell>
<TableCell scope="row">
<Typography
variant="subtitle2"
>
<Typography variant="subtitle2">
{role.description}
</Typography>
</TableCell>
@@ -85,7 +82,8 @@ export default function RoleList(): React.ReactElement {
</Stack>
</TableCell>
</TableRow>
))}
))
)}
</TableBody>
</Table>
</TableContainer>

View File

@@ -13,13 +13,13 @@ import Typography from '@mui/material/Typography';
import EditIcon from '@mui/icons-material/Edit';
import DeleteUserButton from 'components/DeleteUserButton/index.ee';
import ListLoader from 'components/ListLoader';
import useUsers from 'hooks/useUsers';
import useFormatMessage from 'hooks/useFormatMessage';
import * as URLS from 'config/urls';
// TODO: introduce translation entries
// TODO: introduce interaction feedback upon deletion (successful + failure)
// TODO: introduce loading bar
export default function UserList(): React.ReactElement {
const formatMessage = useFormatMessage();
const { users, loading } = useUsers();
@@ -51,25 +51,20 @@ export default function UserList(): React.ReactElement {
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
{loading ? (
<ListLoader rowsNumber={3} cellNumber={2} />
) : (
users.map((user) => (
<TableRow
key={user.id}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
>
<TableCell scope="row">
<Typography
variant="subtitle2"
>
{user.fullName}
</Typography>
<Typography variant="subtitle2">{user.fullName}</Typography>
</TableCell>
<TableCell>
<Typography
variant="subtitle2"
>
{user.email}
</Typography>
<Typography variant="subtitle2">{user.email}</Typography>
</TableCell>
<TableCell>
@@ -86,7 +81,8 @@ export default function UserList(): React.ReactElement {
</Stack>
</TableCell>
</TableRow>
))}
))
)}
</TableBody>
</Table>
</TableContainer>

View File

@@ -13,13 +13,13 @@ import PageTitle from 'components/PageTitle';
import Form from 'components/Form';
import TextField from 'components/TextField';
import useFormatMessage from 'hooks/useFormatMessage';
import { Skeleton } from '@mui/material';
type EditRoleParams = {
roleId: string;
}
};
// TODO: introduce interaction feedback upon deletion (successful + failure)
// TODO: introduce loading bar
export default function EditRole(): React.ReactElement {
const formatMessage = useFormatMessage();
const [updateRole, { loading }] = useMutation(UPDATE_ROLE);
@@ -33,13 +33,11 @@ export default function EditRole(): React.ReactElement {
id: roleId,
name: roleData.name,
description: roleData.description,
}
}
},
},
});
};
if (roleLoading) return <React.Fragment />;
return (
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
<Grid container item xs={12} sm={9} md={8} lg={6}>
@@ -48,6 +46,13 @@ export default function EditRole(): React.ReactElement {
</Grid>
<Grid item xs={12} justifyContent="flex-end" sx={{ pt: 5 }}>
{roleLoading ? (
<Stack direction="column" gap={2}>
<Skeleton variant="rounded" height={55} />
<Skeleton variant="rounded" height={55} />
<Skeleton variant="rounded" height={45} />
</Stack>
) : (
<Form defaultValues={role} onSubmit={handleRoleUpdate}>
<Stack direction="column" gap={2}>
<TextField
@@ -75,6 +80,7 @@ export default function EditRole(): React.ReactElement {
</LoadingButton>
</Stack>
</Form>
)}
</Grid>
</Grid>
</Container>

View File

@@ -16,17 +16,17 @@ import Form from 'components/Form';
import ControlledAutocomplete from 'components/ControlledAutocomplete';
import TextField from 'components/TextField';
import useFormatMessage from 'hooks/useFormatMessage';
import { Skeleton } from '@mui/material';
type EditUserParams = {
userId: string;
}
};
function generateRoleOptions(roles: IRole[]) {
return roles?.map(({ name: label, id: value }) => ({ label, value }));
}
// TODO: introduce interaction feedback upon deletion (successful + failure)
// TODO: introduce loading bar
export default function EditUser(): React.ReactElement {
const formatMessage = useFormatMessage();
const [updateUser, { loading }] = useMutation(UPDATE_USER);
@@ -42,15 +42,13 @@ export default function EditUser(): React.ReactElement {
fullName: userDataToUpdate.fullName,
email: userDataToUpdate.email,
role: {
id: userDataToUpdate.role?.id
}
}
}
id: userDataToUpdate.role?.id,
},
},
},
});
};
if (userLoading) return <React.Fragment />;
return (
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
<Grid container item xs={12} sm={9} md={8} lg={6}>
@@ -59,6 +57,14 @@ export default function EditUser(): React.ReactElement {
</Grid>
<Grid item xs={12} justifyContent="flex-end" sx={{ pt: 5 }}>
{userLoading ? (
<Stack direction="column" gap={2}>
<Skeleton variant="rounded" height={55} />
<Skeleton variant="rounded" height={55} />
<Skeleton variant="rounded" height={55} />
<Skeleton variant="rounded" height={45} />
</Stack>
) : (
<Form defaultValues={user} onSubmit={handleUserUpdate}>
<Stack direction="column" gap={2}>
<TextField
@@ -81,7 +87,12 @@ export default function EditUser(): React.ReactElement {
disablePortal
disableClearable={true}
options={generateRoleOptions(roles)}
renderInput={(params) => <MuiTextField {...params} label={formatMessage('userForm.role')} />}
renderInput={(params) => (
<MuiTextField
{...params}
label={formatMessage('userForm.role')}
/>
)}
loading={rolesLoading}
/>
@@ -96,6 +107,7 @@ export default function EditUser(): React.ReactElement {
</LoadingButton>
</Stack>
</Form>
)}
</Grid>
</Grid>
</Container>