create and display users
This commit is contained in:
parent
86c423b07d
commit
7d84ed7f9e
@ -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>
|
||||||
|
@ -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
125
src/pages/users/create.jsx
Normal 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
75
src/pages/users/index.jsx
Normal 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;
|
40
src/pages/users/users-table.jsx
Normal file
40
src/pages/users/users-table.jsx
Normal 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;
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user