create and display users

This commit is contained in:
grimhilt 2023-08-08 14:17:37 +02:00
parent 86c423b07d
commit 7d84ed7f9e
7 changed files with 272 additions and 14 deletions

View File

@ -7,6 +7,7 @@ import Playlist from '../pages/playlist';
import Files from '../pages/files'; import Files from '../pages/files';
import Authentication from '../pages/auth'; import Authentication from '../pages/auth';
import { LoginRequired } from '../tools/grant-access'; import { LoginRequired } from '../tools/grant-access';
import Users from '../pages/users';
const AppRouter = () => { const AppRouter = () => {
return ( return (
@ -16,6 +17,7 @@ const AppRouter = () => {
<Route path="/playlists" element={<LoginRequired children={<Playlists />} />} /> <Route path="/playlists" element={<LoginRequired children={<Playlists />} />} />
<Route path="/files" element={<LoginRequired children={<Files />} />} /> <Route path="/files" element={<LoginRequired children={<Files />} />} />
<Route path="/playlist/:id" element={<LoginRequired children={<Playlist />} />} /> <Route path="/playlist/:id" element={<LoginRequired children={<Playlist />} />} />
<Route path="/users" element={<LoginRequired children={<Users />} />} />
<Route path="/login" element={<Authentication />} /> <Route path="/login" element={<Authentication />} />
<Route path="*" element={<NotFound />} /> <Route path="*" element={<NotFound />} />
</Routes> </Routes>

View File

@ -50,6 +50,7 @@ const links = [
{ label: 'Playlists', link: '/playlists' }, { label: 'Playlists', link: '/playlists' },
{ label: 'Files', link: '/files' }, { label: 'Files', link: '/files' },
{ label: 'Planning', link: '/planning' }, { label: 'Planning', link: '/planning' },
{ label: 'Users', link: '/users' },
]; ];
const HeaderSearch = () => { const HeaderSearch = () => {

125
src/pages/users/create.jsx Normal file
View File

@ -0,0 +1,125 @@
import { Button, Group, Modal, Stack, PasswordInput, Switch, Text, TextInput, Paper } from '@mantine/core';
import { isNotEmpty, useForm } from '@mantine/form';
import { useState } from 'react';
import { Perm, checkPerm } from '../../tools/grant-access';
import { useAuth } from '../../tools/auth-provider';
import setNotification from '../errors/error-notification';
const ModalCreateUser = ({ opened, handler, addUser, APICall, item }) => {
const [isLoading, setIsLoading] = useState(false);
const { user } = useAuth();
const handleClose = (item) => {
if (item) {
addUser(item);
}
handler();
};
const form = useForm({
initialValues: {
login: '',
password: '',
create_user: false,
create_role: false,
create_playlist: false,
},
validate: {
login: isNotEmpty('Login is required'),
password: isNotEmpty('Password is required'),
},
});
const handleSubmit = async (event) => {
event.preventDefault();
if (form.validate().hasErrors) return;
try {
setIsLoading(true);
if (item) {
// await APICall(item?.id, { name: form.values.name });
// item.name = form.values.name;
handleClose(item);
} else {
let data = { login: form.values.login, password: form.values.password };
data.permissions = 0;
if (form.values.create_user) data.permissions += 1;
if (form.values.create_role) data.permissions += 2;
if (form.values.create_playlist) data.permissions += 4;
const res = await APICall(data);
handleClose(res.data);
}
setIsLoading(false);
} catch (err) {
setIsLoading(false);
setNotification(true, err);
}
};
// todo parent role
return (
<Modal.Root opened={opened} onClose={handler}>
<Modal.Overlay />
<Modal.Content>
<Modal.Header>
<Modal.Title>
<Text fw={700} fz="lg">
Create User
</Text>
</Modal.Title>
<Modal.CloseButton />
</Modal.Header>
<Modal.Body>
<form onSubmit={handleSubmit}>
<Stack spacing="sm">
<Paper>
<TextInput
label="Login"
placeholder="User's login"
{...form.getInputProps('login')}
withAsterisk
/>
<PasswordInput
label="Password"
placeholder="User's password"
mt="md"
{...form.getInputProps('password')}
withAsterisk
/>
</Paper>
<Paper withBorder shadow="xs" p="md">
<Stack spacing="sm">
<Switch
label="Create user"
disabled={checkPerm(Perm.CREATE_USER, user)}
{...form.getInputProps('create_user')}
/>
<Switch
label="Create role"
disabled={checkPerm(Perm.CREATE_ROLE, user)}
{...form.getInputProps('create_role')}
/>
<Switch
label="Create playlist"
disabled={checkPerm(Perm.CREATE_PLAYLIST, user)}
{...form.getInputProps('create_playlist')}
/>
</Stack>
</Paper>
</Stack>
<Group position="right" mt="md">
<Button variant="light" color="red" onClick={handler}>
Cancel
</Button>
<Button type="submit" variant="light" color="green" loading={isLoading}>
Create
</Button>
</Group>
</form>
</Modal.Body>
</Modal.Content>
</Modal.Root>
);
};
export default ModalCreateUser;

75
src/pages/users/index.jsx Normal file
View File

@ -0,0 +1,75 @@
import { useEffect, useState } from 'react';
import NavbarSignage from '../../components/navbar';
import API from '../../services/api';
import setNotification from '../errors/error-notification';
import ModalCreateUser from './create';
import { Button } from '@mantine/core';
import GrantAccess, { Perm } from '../../tools/grant-access';
import UserTable from './users-table';
const Users = () => {
const [showCreate, setShowCreate] = useState(true);
const [showUpdate, setShowUpdate] = useState(false);
const toggleModalCreate = () => setShowCreate(!showCreate);
const toggleModalUpdate = () => setShowUpdate(!showUpdate);
const [users, setUsers] = useState([]);
const [roles, setRoles] = useState([]);
useEffect(() => {
API.listUsers()
.then((res) => {
if (res.status === 200) {
if (users.length === 0) setUsers(res.data);
else setUsers((prev) => [...prev, ...res.data]);
}
})
.catch((err) => {
setNotification(true, err);
});
API.listRoles()
.then((res) => {
if (res.status === 200) {
if (roles.length === 0) setRoles(res.data);
else setRoles((prev) => [...prev, ...res.data]);
}
})
.catch((err) => {
setNotification(true, err);
});
return () => {};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const [search, setSearch] = useState('');
const addUser = (user) => {
setUsers((prev) => [...prev, user]);
};
const navbar = {
title: 'Users',
search: search,
handlerChange: (e) => setSearch(e.target.value),
buttonCreate: (
<GrantAccess role={Perm.CREATE_ROLE} children={<Button onClick={toggleModalCreate}>New User</Button>} />
),
};
return (
<>
<NavbarSignage data={navbar} />
<UserTable
data={users}
// updateItem={setItem} // todo
onDelete={(id) => setUsers(users.filter((item) => item.id != id))}
updateHandler={toggleModalUpdate}
/>
<ModalCreateUser opened={showCreate} handler={toggleModalCreate} addUser={(user) => addUser(user)} APICall={API.createUser} />
</>
);
};
export default Users;

View File

@ -0,0 +1,40 @@
import { Button, Center, Table, Paper, ScrollArea, Group } from '@mantine/core';
import { useNavigate } from 'react-router-dom';
const UserTable = (props) => {
const rows = props.data.map((user) => (
<tr key={user.id}>
<td>{user.login}</td>
<td>
<Group>
<Button onClick={() => 1} color="green">View</Button>
<Button onClick={() => 1}>Update</Button>
<Button onClick={() => 1} color="red">Delete</Button>
</Group>
</td>
</tr>
));
return (
<Paper shadow="sm" p="md" withBorder mb="md">
<ScrollArea.Autosize mah={700} offsetScrollbars>
<Table>
<thead>
<tr>
<th>Name</th>
<th>Actions</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</Table>
</ScrollArea.Autosize>
<Center>
<Button onClick={props.loadMore} mt="md">
Load More
</Button>
</Center>
</Paper>
);
};
export default UserTable;

View File

@ -16,6 +16,12 @@ const API = {
login(data) { login(data) {
return caller().post('/auth/login', data); return caller().post('/auth/login', data);
}, },
listUsers(data) {
return caller().get('/users', data);
},
listRoles(data) {
return caller().get('/roles', data);
},
listPlaylists(data) { listPlaylists(data) {
return caller().get('/playlists', data); return caller().get('/playlists', data);
}, },
@ -23,7 +29,7 @@ const API = {
return caller().post('/playlists', data); return caller().post('/playlists', data);
}, },
updatePlaylist(playlistId, data) { updatePlaylist(playlistId, data) {
return caller().post(`/playlists/${playlistId}/update`, data); return caller().put(`/playlists/${playlistId}/update`, data);
}, },
activate(playlistId) { activate(playlistId) {
return caller().post(`/playlists/${playlistId}/activate`); return caller().post(`/playlists/${playlistId}/activate`);
@ -52,6 +58,9 @@ const API = {
playlistRemoveFile(playlistId, data) { playlistRemoveFile(playlistId, data) {
return caller().post(`/playlists/${playlistId}/remove_file`, data); return caller().post(`/playlists/${playlistId}/remove_file`, data);
}, },
createUser(data) {
return caller().post('/users', data);
},
}; };
export default API; export default API;

View File

@ -1,24 +1,30 @@
import { useAuth } from './auth-provider'; import { useAuth } from './auth-provider';
import Authentication from '../pages/auth'; import Authentication from '../pages/auth';
import { useEffect } from 'react';
export const Perm = { export const Perm = {
CREATE_ROLE: 0, CREATE_USER: 1,
CREATE_PLAYLIST: 1, CREATE_ROLE: 2,
VIEW_PLAYLIST: 2, CREATE_PLAYLIST: 3,
OWN_PLAYLIST: 3, VIEW_PLAYLIST: 4,
EDIT_PLAYLIST: 4, OWN_PLAYLIST: 5,
ACTIVATE_PLAYLIST: 5, EDIT_PLAYLIST: 6,
ACTIVATE_PLAYLIST: 7,
}; };
const checkPerm = (perm, user, item = {}) => { const checkBit = (dec, perm) => {
console.log(user); const binStr = (dec >>> 0).toString(2);
console.log(item); const len = binStr.length;
return binStr[len - perm - 1];
};
export const checkPerm = (perm, user, item = {}) => {
switch (perm) { switch (perm) {
case Perm.CREATE_ROLE: case Perm.CREATE_ROLE:
return false; return user.roles.length >= 1 && checkBit(user.roles[0].permissions, Perm.CREATE_ROLE);
case Perm.CREATE_PLAYLIST: case Perm.CREATE_PLAYLIST:
return user.roles.findIndex((role) => role.can_create_playlist) !== -1; return user.roles.length >= 1 && checkBit(user.roles[0].permissions, Perm.CREATE_PLAYLIST);
case Perm.CREATE_ROLE:
return user.roles.length >= 1 && checkBit(user.roles[0].permissions, Perm.CREATE_USER);
case Perm.OWN_PLAYLIST: case Perm.OWN_PLAYLIST:
return item?.owner_id === user.id; return item?.owner_id === user.id;
default: default:
@ -28,7 +34,7 @@ const checkPerm = (perm, user, item = {}) => {
const GrantAccess = ({ role, roles, children, item }) => { const GrantAccess = ({ role, roles, children, item }) => {
const { user } = useAuth(); const { user } = useAuth();
if (role && checkPerm(role, user, item)) { if (role && checkPerm(role, user)) {
return children; return children;
} else if (roles) { } else if (roles) {
let flag = false; let flag = false;