Files
upstream-traggo/ui/src/user/UsersPage.tsx
upstream-mirror cda7c1a474
Some checks failed
build / build (push) Failing after 3s
snapshot of https://github.com/traggo/server.git (master, 2026-09-16; blobs > 131072B omitted)
2026-09-16 05:24:45 +05:00

178 lines
7.3 KiB
TypeScript

import * as React from 'react';
import Paper from '@material-ui/core/Paper';
import {useMutation, useQuery} from '@apollo/react-hooks';
import * as gqlUser from '../gql/user';
import {CenteredSpinner} from '../common/CenteredSpinner';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import DeleteIcon from '@material-ui/icons/Delete';
import EditIcon from '@material-ui/icons/Edit';
import DoneIcon from '@material-ui/icons/Done';
import CloseIcon from '@material-ui/icons/Close';
import IconButton from '@material-ui/core/IconButton';
import {useSnackbar} from 'notistack';
import {TextField} from '@material-ui/core';
import {Users} from '../gql/__generated__/Users';
import {RemoveUser, RemoveUserVariables} from '../gql/__generated__/RemoveUser';
import {UpdateUser, UpdateUserVariables} from '../gql/__generated__/UpdateUser';
import Checkbox from '@material-ui/core/Checkbox';
import Button from '@material-ui/core/Button';
import {AddUserDialog} from './AddUserDialog';
import makeStyles from '@material-ui/core/styles/makeStyles';
import {ConfirmDialog} from '../common/ConfirmDialog';
const useStyles = makeStyles((theme) => ({
root: {
...theme.mixins.gutters(),
paddingTop: theme.spacing(3),
paddingBottom: theme.spacing(3),
textAlign: 'center',
maxWidth: 800,
margin: '0 auto',
},
}));
const NoEdit = [-1, '', '', false] as const;
export const UsersPage = () => {
const classes = useStyles();
const {data, loading} = useQuery<Users>(gqlUser.Users);
const [addUser, setAddUser] = React.useState(false);
const refetch = {refetchQueries: [{query: gqlUser.Users}, {query: gqlUser.CurrentUser}]};
const {enqueueSnackbar} = useSnackbar();
const [removeUserConfirmation, setRemoveUserConfirmation] = React.useState<false | [number, string]>(false);
const [removeUser] = useMutation<RemoveUser, RemoveUserVariables>(gqlUser.RemoveUser, refetch);
const [[editId, editName, editPass, editAdmin], setEditing] = React.useState<Readonly<[number, string, string, boolean]>>(
NoEdit
);
const [updateUser] = useMutation<UpdateUser, UpdateUserVariables>(gqlUser.UpdateUser, refetch);
if (loading || !data || !data.currentUser || !data.users) {
return <CenteredSpinner />;
}
const onClickDelete = () => {
if (removeUserConfirmation) {
removeUser({variables: {id: removeUserConfirmation[0]}}).then(() =>
enqueueSnackbar('user deleted', {variant: 'success'})
);
}
};
const users = data.users.map((user) => {
const onClickSubmit = () => {
setEditing(NoEdit);
updateUser({
variables: {
id: editId,
name: editName,
admin: editAdmin,
pass: editPass || undefined,
},
}).then(() => enqueueSnackbar('user edited', {variant: 'success'}));
};
const isCurrent = user.id === data.currentUser!.id;
const isEdited = editId === user.id;
return (
<TableRow selected={isCurrent} key={user.id}>
<TableCell>{user.id}</TableCell>
<TableCell>
{isEdited ? (
<TextField
value={editName}
onChange={(e) => setEditing([editId, e.target.value, '', editAdmin])}
onKeyDown={(e) => {
if (e.key === 'Enter') {
onClickSubmit();
}
}}
onSubmit={onClickSubmit}
/>
) : (
user.name + (isCurrent ? ' (current)' : '')
)}
</TableCell>
<TableCell>
{isEdited ? (
<TextField
value={editPass}
placeholder={'use old password'}
type="password"
onChange={(e) => setEditing([editId, editName, e.target.value, editAdmin])}
/>
) : (
'********'
)}
</TableCell>
<TableCell>
{isEdited ? (
<Checkbox checked={editAdmin} onChange={(e) => setEditing([editId, editName, '', e.target.checked])} />
) : user.admin ? (
'Yes'
) : (
'No'
)}
</TableCell>
<TableCell align="right">
{isEdited ? (
<>
<IconButton onClick={onClickSubmit} title="Save">
<DoneIcon />
</IconButton>
<IconButton onClick={() => setEditing(NoEdit)} title="Cancel">
<CloseIcon />
</IconButton>
</>
) : (
<>
<IconButton onClick={() => setEditing([user.id, user.name, '', user.admin])} title="Edit">
<EditIcon />
</IconButton>
<IconButton onClick={() => setRemoveUserConfirmation([user.id, user.name])} title="Delete">
<DeleteIcon />
</IconButton>
</>
)}
</TableCell>
</TableRow>
);
});
return (
<Paper elevation={1} square={true} className={classes.root}>
<Button
color={'primary'}
variant={'outlined'}
size="small"
onClick={() => setAddUser(true)}
fullWidth
style={{marginBottom: 10}}>
Create User
</Button>
{addUser && <AddUserDialog open={true} close={() => setAddUser(false)} />}
<Table>
<TableHead>
<TableRow>
<TableCell>ID</TableCell>
<TableCell>Name</TableCell>
<TableCell>Password</TableCell>
<TableCell>Admin</TableCell>
<TableCell style={{width: 150}} />
</TableRow>
</TableHead>
<TableBody>{users}</TableBody>
</Table>
{removeUserConfirmation ? (
<ConfirmDialog
title={`Are you sure you want to delete the user '${removeUserConfirmation[1]}'?`}
fClose={() => setRemoveUserConfirmation(false)}
fOnSubmit={onClickDelete}>
<b>This operation cannot be undone.</b> Deleting the user includes deleting dashboards, time spans, tags and
devices of that user.
</ConfirmDialog>
) : null}
</Paper>
);
};