Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • main
  • snuggle
  • userHandling
3 results

Target

Select target project
  • zbhai/ragchat-frontend
1 result
Select Git revision
  • main
  • snuggle
  • userHandling
3 results
Show changes
......@@ -34,7 +34,7 @@ function Login() {
// ### PREPARE FORM
const methods = useForm({
resolver: zodResolver(schema),
mode: 'onBlur',
mode: 'onSubmit',
defaultValues: {
email: '',
password: ''
......@@ -82,7 +82,6 @@ function Login() {
setFlashMsg(result.data?.message);
} catch (err) {
console.log(err);
// merge front & backend validation errors
mergeBackendValidation(err.response.status, err.response.data, methods.setError);
}
......@@ -105,8 +104,9 @@ function Login() {
</form>
</FormProvider>
<div className="mt-4">
<Link to="/reset_password">Forgot your Password?</Link>
<div className="mt-4 flex justify-between">
<Link to="/reset_password">Reset Password</Link>
<Link to="/signup">Create account</Link>
</div>
</>
);
......
......@@ -28,7 +28,6 @@ function Logout() {
// set flash mmessagesg
setFlashMsg(result.data?.message);
} catch (err) {
console.error(err);
mergeBackendValidation(500, err);
}
}
......
......@@ -38,7 +38,7 @@ function ResetPasswordForm() {
// ### PREPARE FORM
const methods = useForm({
resolver: zodResolver(schema),
mode: 'onBlur',
mode: 'onSubmit',
defaultValues: {
token: token,
}
......@@ -65,7 +65,6 @@ function ResetPasswordForm() {
setFlashMsg(result.data?.message);
} catch (error) {
// catch the error
console.error(error);
mergeBackendValidation(error.response.status, error.response.data, methods.setError);
}
}
......
import React, { useEffect } from 'react';
import { Link, useLocation, useNavigate } from 'react-router-dom';
import { useAuth } from '../../contexts/Auth/AuthState';
import { FormProvider, useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from "zod";
import { toast } from 'react-toastify';
import { mergeBackendValidation, setFlashMsg } from '../../utils/ErrorHandling';
import Input from '../../components/form/Input';
import Submit from '../../components/form/Submit';
import { Helmet } from 'react-helmet-async';
import Heading from '../../components/font/Heading';
import validator from 'validator';
import api from '../../utils/AxiosConfig';
function Signup() {
// #################################
// VALIDATION SCHEMA
// #################################
const schema = z.object({
name: z.string().min(1),
username: z.string().min(1),
email: z.string().email(),
password: z.string().refine((val) => val && validator.isStrongPassword(val), {
message: 'This value must be min 6 characters long and contain uppercase, lowercase, number, specialchar.',
}),
passwordConfirm: z.string(),
}).refine((data) => data.password === data.passwordConfirm, {
message: "Passwords don't match",
path: ["passwordConfirm"],
});
// #################################
// HOOKS
// #################################
// ### CONNECT AUTH CONTEXT
const { signup } = useAuth();
// ### MAKE USE OF NAVIGATION
const redirect = useNavigate();
// ### MAKE USE OF location state to fetch former requested page
const { state } = useLocation();
// ### PREPARE FORM
const methods = useForm({
resolver: zodResolver(schema),
mode: 'onSubmit',
defaultValues: {
name: '',
username: '',
email: '',
password: '',
passwordConfirm: ''
}
});
// #################################
// FUNCTIONS
// #################################
// ### HANDLE SUBMITTING LOGIN FORM
async function handleSendForm(record) {
try {
// send data to API
const result = await api.post(`/users/signup`, record);
// if successfull redirect to login page
redirect(`/login`);
// FIX: flash message not dislayed
setFlashMsg(result.data?.message);
} catch (err) {
// merge front & backend validation errors
mergeBackendValidation(err.response.status, err.response.data, methods.setError);
}
}
// #################################
// OUTPUT
// #################################
return (
<>
{/* render page title */}
<Helmet><title>[{import.meta.env.VITE_APP_NAME}]</title></Helmet>
<Heading level="1">ZBH-Portal LogIn</Heading>
<FormProvider {...methods} >
<form onSubmit={methods.handleSubmit(handleSendForm)}>
<Input name='name' type='text' title='Name' className='h-16' autoFocus={true} />
<Input name='username' type='text' title='username' className='h-16' />
<Input name='email' type='mail' title='E-Mail' className='h-16' />
<Input name='password' type='password' title='password' className='h-16' />
<Input name='passwordConfirm' type='password' title='confirm password' className='h-16' />
<Submit value='Signup' />
</form>
</FormProvider>
<div className="mt-4">
<Link to="/login">Back to Login</Link>
</div>
</>
);
}
export default Signup;
\ No newline at end of file
import { zodResolver } from '@hookform/resolvers/zod';
import React, { useRef, useState } from 'react';
import { Helmet } from 'react-helmet-async';
import { FormProvider, useForm, useFormContext } from 'react-hook-form';
import { Link, useNavigate, useParams } from 'react-router-dom';
import { z } from 'zod';
import Input from '../../components/form/Input';
import Submit from '../../components/form/Submit';
import { useAuth } from '../../contexts/Auth/AuthState';
import { mergeBackendValidation, setFlashMsg } from '../../utils/ErrorHandling';
import Heading from '../../components/font/Heading';
import api from '../../utils/AxiosConfig';
function Verify() {
// #################################
// VALIDATION SCHEMA
// #################################
// TODO limit file size via .env
// TODO check for file types
const schema = z.object({
token: z.string().min(1),
});
// #################################
// HOOKS
// #################################
// FETCH TOKEN FROM URL
const { token } = useParams();
// SET FORM
const methods = useForm({
resolver: zodResolver(schema),
mode: 'onSubmit',
defaultValues: {
token: token,
}
});
// ### ENABLE REDIRECTIONS
const redirect = useNavigate();
// #################################
// FUNCTIONS
// #################################
// ### HANDLE SUBMITTING FORM
async function handleSendForm(inputs) {
// TRY UPDATE
try {
// send data
const result = await api.post(`/users/confirmverification`, inputs);
redirect('/login');
// set flash message
setFlashMsg(result.data?.message);
} catch (error) {
// catch the error
mergeBackendValidation(error.response.status, error.response.data, methods.setError);
}
}
// #################################
// OUTPUT
// #################################
return (
<>
{/* render page title */}
<Helmet><title>[{import.meta.env.VITE_APP_NAME}] change E-Mail</title></Helmet>
<Heading level="1">Verify Account</Heading>
<FormProvider {...methods} >
<form onSubmit={methods.handleSubmit(handleSendForm)}>
<Input name='token' type='text' title='confirm token' required={true} />
<Submit value='Verify' />
</form>
</FormProvider>
<div className="my-4 flex justify-between">
<Link to="/login">Back to Login</Link>
</div>
</>
);
}
export default React.memo(Verify);
\ No newline at end of file
......@@ -20,8 +20,12 @@ export const sitemap = [{
{
title: 'Onboarding',
path: '/onboarding',
element: loadComponent('Onboarding/Onboarding', true, true),
handle: { crumb: () => <Link to="/onboarding">Onboarding</Link> }
handle: { crumb: () => <Link to="/onboarding">Onboarding</Link> },
children: [
{ index: true, element: loadComponent('Onboarding/Onboarding') },
{ title: 'Chat', path: ':id', element: loadComponent('Onboarding/Onboarding') }
]
},
// REQUEST CHANGE EMAIL ADDRESS
{
......@@ -50,6 +54,13 @@ export const sitemap = [{
}]
}, {
title: 'Others', element: <CleanLayout />, children: [
// SIGNUP
{
path: '/signup', children: [
{ index: true, element: loadComponent('User/Signup') },
{ path: ':token', element: loadComponent('User/Verify') }
]
},
// LOGIN
{ path: '/login', element: loadComponent('User/Login') },
// FORGOT PASSWORD
......
......@@ -29,40 +29,21 @@ api.interceptors.request.use(
// ### RESPONSE INTERCEPTOR
// refreshes accessToken if needed
api.interceptors.response.use(
(res) => {
async (res) => {
switch (res.config.url) {
case '/users/refreshjwt':
case '/users/logout':
case '/users/login':
return res;
},
async (err) => {
// console.log(err);
// save original request config
const originalConfig = err.config;
// if access denied and not a retry already
// BUG: Infinit loop because _retry isn't set at runtime
// console.log(originalConfig);
// console.log(JSON.stringify(originalConfig));
if (originalConfig && err?.response?.status === 403 && originalConfig._retry !== true) {
// patch config to remember it's a retry
originalConfig._retry = true;
console.log('trying to refresh the accessToken and rerun the request');
// console.log('retry', err.code, originalConfig._retry);
// refresh access token
try {
const result = await api.get(
default:
const refresh = await api.get(
'/users/refreshjwt',
{},
{ withCredentials: true }
);
// TODO: don't store accessToken in localStorage, keep in memory only
localStorage.setItem("accessToken", JSON.stringify(result.data.accessToken));
// run retry
return api(originalConfig);
} catch (error) {
return Promise.reject(error);
}
localStorage.setItem("accessToken", JSON.stringify(refresh.data.token));
return res;
}
return Promise.reject(err);
}
);
......