feat(auth): add loading state for user and role management (#1188)
This commit is contained in:
47
packages/web/src/components/ListLoader/index.tsx
Normal file
47
packages/web/src/components/ListLoader/index.tsx
Normal 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;
|
||||
columnsNumber: number;
|
||||
};
|
||||
|
||||
const ListLoader = ({ rowsNumber, columnsNumber }: ListLoaderProps) => {
|
||||
return (
|
||||
<>
|
||||
{[...Array(rowsNumber)].map((row, index) => (
|
||||
<TableRow
|
||||
key={index}
|
||||
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
|
||||
>
|
||||
{[...Array(columnsNumber)].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;
|
@@ -0,0 +1,61 @@
|
||||
import {
|
||||
IconButton,
|
||||
Skeleton,
|
||||
Stack,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import SettingsIcon from '@mui/icons-material/Settings';
|
||||
import ControlledCheckbox from 'components/ControlledCheckbox';
|
||||
|
||||
const PermissionCatalogFieldLoader = () => {
|
||||
return (
|
||||
<TableContainer>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell component="th" />
|
||||
{[...Array(5)].map((row, index) => (
|
||||
<TableCell key={index} component="th">
|
||||
<Skeleton />
|
||||
</TableCell>
|
||||
))}
|
||||
<TableCell component="th" />
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{[...Array(3)].map((row, index) => (
|
||||
<TableRow key={index} sx={{ '&:last-child td': { border: 0 } }}>
|
||||
<TableCell scope="row">
|
||||
<Skeleton width={40} />
|
||||
</TableCell>
|
||||
|
||||
{[...Array(5)].map((action, index) => (
|
||||
<TableCell key={index} align="center">
|
||||
<Typography variant="subtitle2">
|
||||
<ControlledCheckbox name="value" />
|
||||
</Typography>
|
||||
</TableCell>
|
||||
))}
|
||||
|
||||
<TableCell>
|
||||
<Stack direction="row" gap={1} justifyContent="right">
|
||||
<IconButton color="info" size="small">
|
||||
<SettingsIcon />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default PermissionCatalogFieldLoader;
|
@@ -14,17 +14,21 @@ import * as React from 'react';
|
||||
import ControlledCheckbox from 'components/ControlledCheckbox';
|
||||
import usePermissionCatalog from 'hooks/usePermissionCatalog.ee';
|
||||
import PermissionSettings from './PermissionSettings.ee';
|
||||
import PermissionCatalogFieldLoader from './PermissionCatalogFieldLoader';
|
||||
|
||||
type PermissionCatalogFieldProps = {
|
||||
name?: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
const PermissionCatalogField = ({ name = 'permissions', disabled = false }: PermissionCatalogFieldProps) => {
|
||||
const permissionCatalog = usePermissionCatalog();
|
||||
const PermissionCatalogField = ({
|
||||
name = 'permissions',
|
||||
disabled = false,
|
||||
}: PermissionCatalogFieldProps) => {
|
||||
const { permissionCatalog, loading } = usePermissionCatalog();
|
||||
const [dialogName, setDialogName] = React.useState<string>();
|
||||
|
||||
if (!permissionCatalog) return (<React.Fragment />);
|
||||
if (loading) return <PermissionCatalogFieldLoader />;
|
||||
|
||||
return (
|
||||
<TableContainer component={Paper}>
|
||||
@@ -33,14 +37,14 @@ const PermissionCatalogField = ({ name = 'permissions', disabled = false }: Perm
|
||||
<TableRow>
|
||||
<TableCell component="th" />
|
||||
|
||||
{permissionCatalog.actions.map(action => (
|
||||
{permissionCatalog.actions.map((action) => (
|
||||
<TableCell component="th" key={action.key}>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
align="center"
|
||||
sx={{
|
||||
color: 'text.secondary',
|
||||
fontWeight: 700
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
{action.label}
|
||||
@@ -58,21 +62,12 @@ const PermissionCatalogField = ({ name = 'permissions', disabled = false }: Perm
|
||||
sx={{ '&:last-child td': { border: 0 } }}
|
||||
>
|
||||
<TableCell scope="row">
|
||||
<Typography
|
||||
variant="subtitle2"
|
||||
>
|
||||
{subject.label}
|
||||
</Typography>
|
||||
<Typography variant="subtitle2">{subject.label}</Typography>
|
||||
</TableCell>
|
||||
|
||||
{permissionCatalog.actions.map((action) => (
|
||||
<TableCell
|
||||
key={`${subject.key}.${action.key}`}
|
||||
align="center"
|
||||
>
|
||||
<Typography
|
||||
variant="subtitle2"
|
||||
>
|
||||
<TableCell key={`${subject.key}.${action.key}`} align="center">
|
||||
<Typography variant="subtitle2">
|
||||
{action.subjects.includes(subject.key) && (
|
||||
<ControlledCheckbox
|
||||
disabled={disabled}
|
||||
@@ -86,11 +81,7 @@ const PermissionCatalogField = ({ name = 'permissions', disabled = false }: Perm
|
||||
))}
|
||||
|
||||
<TableCell>
|
||||
<Stack
|
||||
direction="row"
|
||||
gap={1}
|
||||
justifyContent="right"
|
||||
>
|
||||
<Stack direction="row" gap={1} justifyContent="right">
|
||||
<IconButton
|
||||
color="info"
|
||||
size="small"
|
||||
@@ -116,7 +107,7 @@ const PermissionCatalogField = ({ name = 'permissions', disabled = false }: Perm
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export default PermissionCatalogField;
|
||||
|
@@ -13,14 +13,14 @@ 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 loading bar
|
||||
export default function RoleList(): React.ReactElement {
|
||||
const formatMessage = useFormatMessage();
|
||||
const { roles } = useRoles();
|
||||
const { roles, loading } = useRoles();
|
||||
|
||||
return (
|
||||
<TableContainer component={Paper}>
|
||||
@@ -49,34 +49,41 @@ export default function RoleList(): React.ReactElement {
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{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>
|
||||
</TableCell>
|
||||
{loading && <ListLoader rowsNumber={3} columnsNumber={2} />}
|
||||
{!loading &&
|
||||
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>
|
||||
</TableCell>
|
||||
|
||||
<TableCell scope="row">
|
||||
<Typography variant="subtitle2">{role.description}</Typography>
|
||||
</TableCell>
|
||||
<TableCell scope="row">
|
||||
<Typography variant="subtitle2">
|
||||
{role.description}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<Stack direction="row" gap={1} justifyContent="right">
|
||||
<IconButton
|
||||
size="small"
|
||||
component={Link}
|
||||
to={URLS.ROLE(role.id)}
|
||||
>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
<TableCell>
|
||||
<Stack direction="row" gap={1} justifyContent="right">
|
||||
<IconButton
|
||||
size="small"
|
||||
component={Link}
|
||||
to={URLS.ROLE(role.id)}
|
||||
>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
|
||||
<DeleteRoleButton disabled={role.isAdmin} roleId={role.id} />
|
||||
</Stack>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
<DeleteRoleButton
|
||||
disabled={role.isAdmin}
|
||||
roleId={role.id}
|
||||
/>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
|
@@ -13,11 +13,11 @@ 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 loading bar
|
||||
export default function UserList(): React.ReactElement {
|
||||
const formatMessage = useFormatMessage();
|
||||
const { users, loading } = useUsers();
|
||||
@@ -49,34 +49,36 @@ export default function UserList(): React.ReactElement {
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{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>
|
||||
</TableCell>
|
||||
{loading && <ListLoader rowsNumber={3} columnsNumber={2} />}
|
||||
{!loading &&
|
||||
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>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<Typography variant="subtitle2">{user.email}</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Typography variant="subtitle2">{user.email}</Typography>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<Stack direction="row" gap={1} justifyContent="right">
|
||||
<IconButton
|
||||
size="small"
|
||||
component={Link}
|
||||
to={URLS.USER(user.id)}
|
||||
>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
<TableCell>
|
||||
<Stack direction="row" gap={1} justifyContent="right">
|
||||
<IconButton
|
||||
size="small"
|
||||
component={Link}
|
||||
to={URLS.USER(user.id)}
|
||||
>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
|
||||
<DeleteUserButton userId={user.id} />
|
||||
</Stack>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
<DeleteUserButton userId={user.id} />
|
||||
</Stack>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
|
Reference in New Issue
Block a user