React -> Upgrade forms & simple fixes

feature/2_forms
TBS093A 2021-02-17 23:13:39 +01:00
parent b2565641e5
commit b2ecdfb807
7 changed files with 279 additions and 68 deletions

View File

@ -13,15 +13,15 @@ export const FormGenerator = ({
const handler = async (event) => {
event.preventDefault()
for ( let i = 0; i < refList.length; i++ ) {
if (
refList[i].current.value === ''
for (let i = 0; i < refList.length; i++) {
if (refList[i].current.value === ''
&& inputList[0].action !== 'Update'
|| i === 0 && refList.length !== 1
|| i === 0
&& refList.length !== 1
) {
refList[i].current.focus()
} else if ( i === refList.length - 1 ) {
await action( refList )
} else if (i === refList.length - 1) {
await action(refList)
}
}
}
@ -29,32 +29,42 @@ export const FormGenerator = ({
let info
return (
<form onSubmit={ event => handler( event ) }>
<form onSubmit={event => handler(event)}>
{
inputList.map( (input, key) => {
inputList.map((input, key) => {
if ( input.type === 'info' ) {
if (input.type === 'info') {
info = input
} else if ( input.type === 'text' ) {
} else if (input.type === 'text') {
return (
<TextInputGenerator
input={ input }
info={ info }
key={ key }
input={input}
info={info}
key={key}
/>
)
} else if ( input.type === 'file' ) {
} else if (input.type === 'password') {
return (
<PasswordInputGenerator
input={input}
info={info}
key={key}
/>
)
} else if (input.type === 'file') {
return (
<UploadInputGenerator
input={ input }
info={ info }
key={ key }
input={input}
info={info}
key={key}
/>
)
}
})
}
<button type='submit' />
<button type='submit'>
{ info.button_value }
</button>
</form>
)
}
@ -79,11 +89,42 @@ const TextInputGenerator = ({
}) => {
return (
<div>
{ input.name + ':' }
{input.name + ':'}
<input
id={ input.name + info.action + info.endpoint + 'Input' }
id={input.name + info.action + info.endpoint + 'Input'}
autoComplete='off'
ref={ input.ref }
ref={input.ref}
/>
</div>
)
}
/**
* Text input generator, example:
* @param {
* {
* type: 'password',
* name: 'name',
* ref: React.createRef()
* } } input - basic text input
* @param {
* {
* type: 'info',
* action: 'Update'
* endpoint: 'Album'
* } } info - information about form
*/
const PasswordInputGenerator = ({
input, info
}) => {
return (
<div>
{input.name + ':'}
<input
id={input.name + info.action + info.endpoint + 'Input'}
autoComplete='off'
ref={input.ref}
type='password'
/>
</div>
)
@ -105,26 +146,26 @@ const UploadInputGenerator = ({
input, info
}) => {
const onLoadFile = async ( event ) => {
const onLoadFile = async (event) => {
event.preventDefault()
let data = event.target.files[0]
input.setFile( await toBase64( data ) )
input.setFile(await toBase64(data))
setDropInfos(data.name, data.size)
}
const onLoadFileDrop = async ( event ) => {
const onLoadFileDrop = async (event) => {
event.preventDefault()
event.persist()
let data = event.dataTransfer.files[0]
input.setFile( await toBase64( data ) )
input.setFile(await toBase64(data))
setDropInfos(data.name, data.size)
}
const toBase64 = ( file ) => new Promise( (resolve, reject) => {
const toBase64 = (file) => new Promise((resolve, reject) => {
let fileReader = new FileReader()
fileReader.readAsDataURL( file )
fileReader.onload = () => resolve( fileReader.result )
fileReader.onerror = error => reject( error )
fileReader.readAsDataURL(file)
fileReader.onload = () => resolve(fileReader.result)
fileReader.onerror = error => reject(error)
})
const setDropInfos = (name, size) => {
@ -132,24 +173,24 @@ const UploadInputGenerator = ({
'name: "'
+ name
+ '"\nsize: '
+ (Math.round(size / 100 + 'e-2') / 100 )
+ (Math.round(size / 100 + 'e-2') / 100)
+ ' MB'
)
}
return (
<div onDrop={ event => onLoadFileDrop( event ) } >
<pre style={ {marginTop: '25px', marginLeft: '40px'} }>
{ input.dropInfo }
<div onDrop={event => onLoadFileDrop(event)} >
<pre style={{ marginTop: '25px', marginLeft: '40px' }}>
{input.dropInfo}
</pre>
<input
style={ { marginTop: '-55px' } }
id={ input.name + info.action + info.endpoint + 'Input' }
style={{ marginTop: '-55px' }}
id={input.name + info.action + info.endpoint + 'Input'}
className='uploadInput'
type='file'
accept={ input.fileType + '/*' }
accept={input.fileType + '/*'}
autoComplete='off'
onChange={ event => onLoadFile( event ) }
onChange={event => onLoadFile(event)}
/>
</div>
)

View File

@ -0,0 +1,66 @@
import React, { useState, useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { userAuthSelector } from '../../../redux/slices/userAuthSlice'
import userAuthAsyncThunk from '../../../redux/asyncThunks/userAuthAsyncThunk'
import FormGenerator from '../formGenerator'
const UserLoginForm = () => {
const usernameInput = React.createRef()
const passwordInput = React.createRef()
const dispatch = useDispatch()
let refList = [
usernameInput,
passwordInput
]
let inputList = [
{
type: 'info',
action: 'Create',
endpint: 'user/auth/login',
button_value: 'Sign In'
},
{
type: 'text',
name: 'Username',
ref: usernameInput
},
{
type: 'password',
name: 'Password',
ref: passwordInput
}
]
const login = async ( refs ) => {
let pass = {
username: refs[0].current.value,
password: refs[1].current.value
}
dispatch(
userAuthAsyncThunk.fetchLogin(
pass
)
)
}
return (
<>
<FormGenerator
inputList={ inputList }
refList={ refList }
action={ login }
/>
</>
)
}
export default UserLoginForm

View File

@ -0,0 +1,74 @@
import React, { useState, useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { userCrudSelector } from '../../../redux/slices/userCrudSlice'
import userCrudAsyncThunk from '../../../redux/asyncThunks/userCrudAsyncThunk'
import FormGenerator from '../formGenerator'
const UserRegisterForm = () => {
const usernameInput = React.createRef()
const passwordInput = React.createRef()
const emailInput = React.createRef()
const dispatch = useDispatch()
let refList = [
usernameInput,
passwordInput,
emailInput
]
let inputList = [
{
type: 'info',
action: 'Create',
endpint: 'user/auth/login',
button_value: 'Sign In'
},
{
type: 'text',
name: 'Username',
ref: usernameInput
},
{
type: 'password',
name: 'Password',
ref: passwordInput
},
{
type: 'text',
name: 'Email',
ref: emailInput
}
]
const register = async ( refs ) => {
let pass = {
username: refs[0].current.value,
password: refs[1].current.value,
email: refs[2].current.value,
}
dispatch(
userCrudAsyncThunk.fetchRegister(
pass
)
)
}
return (
<>
<FormGenerator
inputList={ inputList }
refList={ refList }
action={ register }
/>
</>
)
}
export default UserRegisterForm

View File

@ -80,7 +80,7 @@ const __verifyUserSession = (token, user) => {
if ( user.id !== 0 && user.username !== '' && user.email !== '' && token !== '' )
return true
else
return true
return false
}

View File

@ -1,9 +1,13 @@
import React, { useState, useEffect } from 'react'
import UserLoginForm from '../../../components/forms/user_auth/userLogin'
const UserAuthIndex = () => {
return (
<div>
<UserLoginForm />
</div>
)
}

View File

@ -74,31 +74,53 @@ const responseAbstract = async (endpoint, method, token, body) => {
const headerBuilder = (url, method, token, body) => {
let headers_r = {
'Authorization': token,
'authorization': token,
'x-csrftoken': getCookie('csrftoken'),
'accept': 'application/json',
'Content-Type': 'application/json',
'content-type': 'application/json',
}
if ('file' in body) {
headers_r = {
'Authorization': token,
'authorization': token,
'x-csrftoken': getCookie('csrftoken'),
'accept': 'multipart/form-data',
'Content-Type': 'multipart/form-data',
'content-type': 'multipart/form-data',
}
}
let headers = {
url: url,
method: method,
headers: headers_r
headers: headers_r,
credentials: 'same-origin'
}
if (method === 'PUT' || method === 'POST' || method === 'PATCH') {
headers = Object.assign({}, headers, {
data: JSON.stringify(body),
withCredentials: true,
})
}
console.log(headers)
return headers
}
/**
* get cookie method for CSRF verification
* @param {string} name - name of handled cookie
*/
const getCookie = (name) => {
if (!document.cookie) {
return null;
}
const token = document.cookie.split(';')
.map(c => c.trim())
.filter(c => c.startsWith(name + '='));
if (token.length === 0) {
return null;
}
return decodeURIComponent(token[0].split('=')[1]);
}
export default {
APIAddress,

View File

@ -15,11 +15,15 @@ const userAuthSlice = createSlice(
reducers: {},
extraReducers: {
[userAuthAsyncThunk.fetchLogin.fulfilled.type]: (state, action) => {
try {
state.token = action.payload.data.Authorization
state.user.id = action.payload.data.user.id
state.user.username = action.payload.data.user.username
state.user.email = action.payload.data.user.email
state.info = 'login success'
} catch {
state.info = 'login failed'
}
},
[userAuthAsyncThunk.fetchLogout.fulfilled.type]: (state, action) => {
state.token = ''