mirror of
https://github.com/pawelmalak/snippet-box.git
synced 2025-12-21 13:23:05 +01:00
Auth route and login form. Client error handler
This commit is contained in:
88
client/src/components/Auth/AuthForm.tsx
Normal file
88
client/src/components/Auth/AuthForm.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import { ChangeEvent, FormEvent, useState, useContext, Fragment } from 'react';
|
||||
import { AuthContext } from '../../store';
|
||||
import { Button } from '../UI';
|
||||
|
||||
export const AuthForm = (): JSX.Element => {
|
||||
const [isInLogin, setIsInLogin] = useState(true);
|
||||
const [formData, setFormData] = useState({ email: '', password: '' });
|
||||
const { login } = useContext(AuthContext);
|
||||
|
||||
const inputHandler = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData({
|
||||
...formData,
|
||||
[name]: value
|
||||
});
|
||||
};
|
||||
|
||||
const formHandler = (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (isInLogin) {
|
||||
login(formData);
|
||||
} else {
|
||||
// register
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<h5 className='card-title'>{isInLogin ? 'Login' : 'Register'}</h5>
|
||||
<p className=''>
|
||||
{isInLogin ? "Don't have an account yet?" : 'Already a user?'}
|
||||
<span
|
||||
onClick={() => setIsInLogin(!isInLogin)}
|
||||
className='text-success cursor-pointer'
|
||||
>
|
||||
{isInLogin ? ' Sign Up' : ' Login'}
|
||||
</span>
|
||||
</p>
|
||||
<hr />
|
||||
|
||||
<form onSubmit={e => formHandler(e)}>
|
||||
<div className='mb-3'>
|
||||
<label htmlFor='email' className='form-label'>
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
type='email'
|
||||
className='form-control'
|
||||
id='email'
|
||||
name='email'
|
||||
placeholder='john@doe.com'
|
||||
required
|
||||
autoComplete='email'
|
||||
value={formData.email}
|
||||
onChange={e => inputHandler(e)}
|
||||
/>
|
||||
</div>
|
||||
<div className='mb-3'>
|
||||
<label htmlFor='password' className='form-label'>
|
||||
Password
|
||||
</label>
|
||||
<input
|
||||
type='password'
|
||||
className='form-control'
|
||||
id='password'
|
||||
name='password'
|
||||
placeholder='••••••'
|
||||
required
|
||||
autoComplete='current-password'
|
||||
value={formData.password}
|
||||
onChange={e => inputHandler(e)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<div className='d-grid gap-2'>
|
||||
<Button
|
||||
text={isInLogin ? 'Login' : 'Register'}
|
||||
color='secondary'
|
||||
type='submit'
|
||||
outline
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
1
client/src/components/Auth/index.ts
Normal file
1
client/src/components/Auth/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './AuthForm';
|
||||
@@ -14,6 +14,11 @@
|
||||
"name": "Editor",
|
||||
"dest": "/editor",
|
||||
"isPublic": false
|
||||
},
|
||||
{
|
||||
"name": "Auth",
|
||||
"dest": "/auth",
|
||||
"isPublic": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
14
client/src/containers/Auth.tsx
Normal file
14
client/src/containers/Auth.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { AuthForm } from '../components/Auth';
|
||||
import { Card, Layout } from '../components/UI';
|
||||
|
||||
export const Auth = (): JSX.Element => {
|
||||
return (
|
||||
<Layout>
|
||||
<div className='col-12 col-md-6 mx-auto'>
|
||||
<Card>
|
||||
<AuthForm />
|
||||
</Card>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
@@ -2,3 +2,4 @@ export * from './Home';
|
||||
export * from './Snippet';
|
||||
export * from './Snippets';
|
||||
export * from './Editor';
|
||||
export * from './Auth';
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { useState, createContext, ReactNode } from 'react';
|
||||
import axios from 'axios';
|
||||
import { createContext, ReactNode } from 'react';
|
||||
|
||||
import { AuthContext as Context } from '../typescript/interfaces';
|
||||
import { AuthContext as Context, Response } from '../typescript/interfaces';
|
||||
import { errorHandler } from '../utils';
|
||||
|
||||
export const AuthContext = createContext<Context>({
|
||||
isAuthenticated: false,
|
||||
@@ -13,7 +15,19 @@ interface Props {
|
||||
|
||||
export const AuthContextProvider = (props: Props): JSX.Element => {
|
||||
const login = async (formData: { email: string; password: string }) => {
|
||||
console.table(formData);
|
||||
try {
|
||||
const res = await axios.post<Response<{ token: string }>>(
|
||||
'/api/auth/login',
|
||||
formData
|
||||
);
|
||||
|
||||
localStorage.setItem('token', res.data.data.token);
|
||||
|
||||
// get profile
|
||||
// redirect to snippets? / home?
|
||||
} catch (err) {
|
||||
errorHandler(err);
|
||||
}
|
||||
};
|
||||
|
||||
const context: Context = {
|
||||
|
||||
5
client/src/typescript/interfaces/User.ts
Normal file
5
client/src/typescript/interfaces/User.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Model } from '.';
|
||||
|
||||
export interface User extends Model {
|
||||
email: string;
|
||||
}
|
||||
@@ -5,3 +5,4 @@ export * from './Response';
|
||||
export * from './Context';
|
||||
export * from './Statistics';
|
||||
export * from './SearchQuery';
|
||||
export * from './User';
|
||||
|
||||
15
client/src/utils/errorHandler.ts
Normal file
15
client/src/utils/errorHandler.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { AxiosError } from 'axios';
|
||||
|
||||
export const errorHandler = (err: any) => {
|
||||
const error = err as AxiosError<{ error: string }>;
|
||||
|
||||
let msg: string;
|
||||
|
||||
if (error.response) {
|
||||
msg = error.response.data.error;
|
||||
} else {
|
||||
msg = 'Something went wrong';
|
||||
}
|
||||
|
||||
console.log(msg);
|
||||
};
|
||||
@@ -3,3 +3,4 @@ export * from './badgeColor';
|
||||
export * from './findLanguage';
|
||||
export * from './searchParser';
|
||||
export * from './ProtectedRoute';
|
||||
export * from './errorHandler';
|
||||
|
||||
Reference in New Issue
Block a user