Skip to content
Snippets Groups Projects
Commit 94401a39 authored by Embruch, Gerd's avatar Embruch, Gerd
Browse files

added user management

parent b2a4678c
Branches
No related tags found
No related merge requests found
import { zodResolver } from '@hookform/resolvers/zod';
import React, { useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { RiDeleteBinLine } from 'react-icons/ri';
import { z } from 'zod';
import Input from '../../../components/form/Input';
import Select from '../../../components/form/Select';
import Submit from '../../../components/form/Submit';
import { useAuth } from '../../../contexts/Auth/AuthState';
import { ROLES } from '../UserTypes';
import { mergeBackendValidation, setFlashMsg } from '/src/utils/ErrorHandling';
function User({ user, idx, handleDelete }) {
// #################################
// VALIDATION SCHEMA
// #################################
const schema = z.object({
name: z.string().min(2),
username: z.string().min(2),
email: z.string().min(1).email(),
verified: z.string().transform((val) => val === 'true' ? true : false),
role: z.coerce.number()
});
// #################################
// HOOKS
// #################################
// ### CONNECT AUTH CONTEXT
const { currentUser, update } = useAuth();
const [updatedUser, setUpdatedUser] = useState(user);
// ### PREPARE FORM
const methods = useForm({
resolver: zodResolver(schema),
mode: 'onBlur',
defaultValues: user
});
// const id = useId();
// #################################
// FUNCTIONS
// #################################
// ### HANDLE SUBMITTING FORM
async function handleSendForm(inputs) {
// save to db
try {
// send data
const result = await update(user._id, inputs);
setUpdatedUser(result.data.document);
// set flash msg
setFlashMsg(result.data?.message);
} catch (error) {
// catch the error
mergeBackendValidation(error.response.status, error.response.data, methods.setError);
}
}
// #################################
// OUTPUT
// #################################
return (
<div className='flex items-center bg-white border border-UhhLightGrey rounded-lg shadow-lg p-3 m-3'>
<div>
{currentUser?.role >= 2 ?
<>
<h2 className='text-Uhh-Grey font-bold text-2xl flex justify-end'>
<RiDeleteBinLine className='cursor-pointer hover:text-UhhRed' title='delete user' onClick={() => handleDelete(user.id, idx)} />
</h2>
<FormProvider {...methods} >
<form onSubmit={methods.handleSubmit(handleSendForm)} noValidate>
<Input name='name' type='text' title='name' required={true} />
<Input name='username' type='text' title='username' required={true} />
<Input name='email' type='email' title='eMail' required={true} />
<Select title="verified" name="verified" options={[{ _id: true, title: 'verified' }, { _id: false, title: 'not verified' }]} required={true} />
<Select title="Role" name="role" options={ROLES} required={true} />
<Submit value='save' />
</form>
</FormProvider>
</>
:
<>
<h2 className='text-Uhh-Grey font-bold text-2xl'>{user.fullname}</h2>
<p className='text-sm'>{user.email}</p>
</>
}
</div>
</div>
);
}
export default React.memo(User);
\ No newline at end of file
import React, { useEffect, useState } from 'react';
import { Helmet } from 'react-helmet-async';
import Heading from '../../../components/font/Heading';
import api from '../../../utils/AxiosConfig';
import User from './User';
import { mergeBackendValidation, setFlashMsg } from '/src/utils/ErrorHandling';
function Users() {
// #################################
// HOOKS
// #################################
// ### USERS
const [users, setUsers] = useState();
// ### FETCH USERS
useEffect(() => {
// mount
const controller = new AbortController();
const getUsers = async () => {
try {
const result = await api.get('/users', {
signal: controller.signal
});
setUsers(result?.data);
} catch (error) {
mergeBackendValidation(error.response.status, error.response.data);
}
};
getUsers();
return () => {
// on unmount abort request
controller.abort();
};
}, []);
// #################################
// FUNCTIONS
// #################################
// ### DELETE USERS
const handleDelete = async (id, idx) => {
try {
// delete in backend
const result = await api.delete(`/users/${id}`);
// delete in frontend
const list = [...users];
list.splice(idx, 1);
setUsers(list);
setFlashMsg(result.data?.message);
} catch (error) {
mergeBackendValidation(error.response.status, error.response.data, methods.setError);
}
};
// #################################
// OUTPUT
// #################################
return (
<>
{/* render page title */}
<Helmet><title>[{import.meta.env.VITE_APP_NAME}] Users</title></Helmet>
<Heading level="1" className="col-span-2">Registered Users</Heading>
<div>
{users?.length
? (
<div className='flex flex-wrap justify-items-stretch'>
{users.map((user, idx) =>
<User key={user._id} idx={idx} user={user} handleDelete={handleDelete} />
)}
</div>
) : <p>No users to display</p>
}
</div>
</>
);
}
export default React.memo(Users);
// the _id represents the order ranking the role
// the lower the _id, the lower the permissions
export const ROLES = [
{ _id: 0, title: 'User' },
{ _id: 1, title: 'Editor' },
{ _id: 2, title: 'Poweruser' },
{ _id: 3, title: 'Lead' },
{ _id: 4, title: 'Admin' }
];
\ No newline at end of file
......@@ -38,6 +38,10 @@ export const sitemap = [{
]
},
// USER
{
title: 'Users', path: '/users', element: loadComponent('User/List/Users', true, true), handle: { crumb: () => <Link to="/users">Users</Link> }
},
// PROFILE
{
title: 'Profile', path: '/profile', handle: { crumb: () => <Link to="/profile">Profile</Link> },
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment