UserForm: ajout retour de validation d'API
This commit is contained in:
parent
de6832e94f
commit
67732aa00a
15
frontend/src/actions/user.actions.js
Normal file
15
frontend/src/actions/user.actions.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
export const CREATE_USER_REQUEST = 'CREATE_USER_REQUEST';
|
||||||
|
export const CREATE_USER_SUCCESS = 'CREATE_USER_SUCCESS';
|
||||||
|
export const CREATE_USER_FAILURE = 'CREATE_USER_FAILURE';
|
||||||
|
|
||||||
|
export function createUser(username, password) {
|
||||||
|
return { type: CREATE_USER_REQUEST, username, password}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createUserSuccess(user) {
|
||||||
|
return { type: CREATE_USER_SUCCESS, user }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createUserFailure(error) {
|
||||||
|
return { type: CREATE_USER_FAILURE, error }
|
||||||
|
}
|
@ -4,7 +4,6 @@ import { WithForm } from './WithForm';
|
|||||||
export function UserForm({ form, onSubmit }) {
|
export function UserForm({ form, onSubmit }) {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log(form.valid, form.submitted);
|
|
||||||
if (form.valid && form.submitted) onSubmit(form.data);
|
if (form.valid && form.submitted) onSubmit(form.data);
|
||||||
}, [form.valid, form.submitted]);
|
}, [form.valid, form.submitted]);
|
||||||
|
|
||||||
|
@ -4,10 +4,10 @@ export function WithForm(validators) {
|
|||||||
|
|
||||||
return function(Component) {
|
return function(Component) {
|
||||||
|
|
||||||
return function WithFormWrapper(props) {
|
return function WithFormWrapper({ errors, ...props }) {
|
||||||
|
|
||||||
const [ formData, setFormData ] = useState({});
|
const [ formData, setFormData ] = useState({});
|
||||||
const [ formErrors, setFormErrors ] = useState({});
|
const [ formErrors, setFormErrors ] = useState(errors || {});
|
||||||
const [ submitted, setSubmitted ] = useState(false);
|
const [ submitted, setSubmitted ] = useState(false);
|
||||||
|
|
||||||
const validateField = (name) => {
|
const validateField = (name) => {
|
||||||
@ -27,6 +27,10 @@ export function WithForm(validators) {
|
|||||||
setSubmitted(false);
|
setSubmitted(false);
|
||||||
}, [submitted]);
|
}, [submitted]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setFormErrors(errors || {});
|
||||||
|
}, [errors]);
|
||||||
|
|
||||||
const form = {
|
const form = {
|
||||||
field: (name, defaultValue) => {
|
field: (name, defaultValue) => {
|
||||||
return name in formData ? formData[name] : defaultValue;
|
return name in formData ? formData[name] : defaultValue;
|
||||||
|
21
frontend/src/errors/api.error.js
Normal file
21
frontend/src/errors/api.error.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
export class APIError extends Error {
|
||||||
|
constructor(endpoint, code, message, data) {
|
||||||
|
super(`APIError: ${message}`);
|
||||||
|
this.endpoint = endpoint;
|
||||||
|
this.code = code;
|
||||||
|
this.data = data;
|
||||||
|
Error.captureStackTrace(this, APIError);
|
||||||
|
}
|
||||||
|
|
||||||
|
getEndpoint() {
|
||||||
|
return this.endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCode() {
|
||||||
|
return this.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
getData() {
|
||||||
|
return this.data;
|
||||||
|
}
|
||||||
|
}
|
@ -1,23 +1,50 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import Page from './page';
|
import Page from './page';
|
||||||
import { ExtendedUserForm as UserForm } from '../components/UserForm';
|
import { ExtendedUserForm as UserForm } from '../components/UserForm';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { createUser } from '../actions/user.actions';
|
||||||
|
|
||||||
export default class HomePage extends React.PureComponent {
|
export class HomePage extends React.PureComponent {
|
||||||
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const onUserFormSubmit = formData => {
|
|
||||||
console.log(formData);
|
const { createUserFormError } = this.props;
|
||||||
};
|
|
||||||
|
const onUserFormSubmit = ({ username, password }) => {
|
||||||
|
this.props.dispatch(createUser(username, password));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page title="home">
|
<Page title="home">
|
||||||
<div className="section">
|
<div className="section">
|
||||||
<h1 className="title">Bienvenue sur PleaseWait !</h1>
|
<h1 className="title">Bienvenue sur PleaseWait !</h1>
|
||||||
<h2 className="subtitle">Le gestionnaire de ticket simplifié.</h2>
|
<h2 className="subtitle">Le gestionnaire de ticket simplifié.</h2>
|
||||||
<a href="#/logout">Se déconnecter</a>
|
<a href="#/logout">Se déconnecter</a>
|
||||||
<UserForm onSubmit={onUserFormSubmit} />
|
<UserForm onSubmit={onUserFormSubmit} errors={createUserFormError} />
|
||||||
</div>
|
</div>
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function transformErrorCode(code) {
|
||||||
|
switch (code) {
|
||||||
|
case 0:
|
||||||
|
return {
|
||||||
|
username: {
|
||||||
|
hasError: true,
|
||||||
|
message: "Le nom d'utilisateur ne peut pas être vide. (API)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps({ users }) {
|
||||||
|
return {
|
||||||
|
createUserFormError: transformErrorCode(users.createUserForm.errorCode),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ConnectedHomePage = connect(mapStateToProps)(HomePage)
|
||||||
|
24
frontend/src/reducers/users.reducers.js
Normal file
24
frontend/src/reducers/users.reducers.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { CREATE_USER_FAILURE } from "../actions/user.actions";
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
byId: {},
|
||||||
|
createUserForm: { errorCode: null },
|
||||||
|
};
|
||||||
|
|
||||||
|
export function usersReducer(state = initialState, action) {
|
||||||
|
switch(action.type) {
|
||||||
|
case CREATE_USER_FAILURE:
|
||||||
|
return handleCreateUserFailure(state, action);
|
||||||
|
};
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCreateUserFailure(state, { error }) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
createUserForm: {
|
||||||
|
...state.createUserForm,
|
||||||
|
errorCode: error.code,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -2,11 +2,13 @@ import { all } from 'redux-saga/effects';
|
|||||||
import { rootSaga as authRootSaga } from './auth/root.saga';
|
import { rootSaga as authRootSaga } from './auth/root.saga';
|
||||||
import { rootSaga as failureRootSaga } from './failure/root.saga';
|
import { rootSaga as failureRootSaga } from './failure/root.saga';
|
||||||
import { rootSaga as initRootSaga } from './init/root.saga';
|
import { rootSaga as initRootSaga } from './init/root.saga';
|
||||||
|
import { rootSaga as userRootSaga } from './user/root.saga';
|
||||||
|
|
||||||
export default function* rootSaga() {
|
export default function* rootSaga() {
|
||||||
yield all([
|
yield all([
|
||||||
initRootSaga(),
|
initRootSaga(),
|
||||||
authRootSaga(),
|
authRootSaga(),
|
||||||
failureRootSaga(),
|
failureRootSaga(),
|
||||||
|
userRootSaga()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
9
frontend/src/sagas/user/root.saga.js
Normal file
9
frontend/src/sagas/user/root.saga.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { all, takeLatest } from 'redux-saga/effects';
|
||||||
|
import { CREATE_USER_REQUEST } from '../../actions/user.actions';
|
||||||
|
import { createUserSaga } from './user.saga';
|
||||||
|
|
||||||
|
export function* rootSaga() {
|
||||||
|
yield all([
|
||||||
|
takeLatest(CREATE_USER_REQUEST, createUserSaga),
|
||||||
|
]);
|
||||||
|
}
|
17
frontend/src/sagas/user/user.saga.js
Normal file
17
frontend/src/sagas/user/user.saga.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { call, put } from 'redux-saga/effects';
|
||||||
|
import { APIClient } from '../../services/api-client.service';
|
||||||
|
import { createUserFailure, createUserSuccess } from '../../actions/user.actions';
|
||||||
|
|
||||||
|
export function* createUserSaga({username, password}) {
|
||||||
|
const client = new APIClient();
|
||||||
|
let user;
|
||||||
|
try {
|
||||||
|
user = yield call(client.createUser, username, password);
|
||||||
|
} catch(err) {
|
||||||
|
console.error(err);
|
||||||
|
yield put(createUserFailure(err));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
yield put(createUserSuccess(user));
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
import { UnauthorizedError } from '../errors/unauthorized.error.js';
|
import { UnauthorizedError } from '../errors/unauthorized.error.js';
|
||||||
|
import { APIError } from '../errors/api.error.js';
|
||||||
|
|
||||||
export const UnauthorizedStatusCode = 401;
|
export const UnauthorizedStatusCode = 401;
|
||||||
|
|
||||||
@ -10,10 +11,12 @@ export class APIClient {
|
|||||||
this.retrieveSessionUser = this.retrieveSessionUser.bind(this);
|
this.retrieveSessionUser = this.retrieveSessionUser.bind(this);
|
||||||
this.listUsers = this.listUsers.bind(this);
|
this.listUsers = this.listUsers.bind(this);
|
||||||
this.listRequests = this.listRequests.bind(this);
|
this.listRequests = this.listRequests.bind(this);
|
||||||
|
this.createUser = this.createUser.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
login(username, password) {
|
login(username, password) {
|
||||||
return this._callAPI('/login', { username, password }, 'POST')
|
return this._callAPI('/login', { username, password }, 'POST')
|
||||||
|
.then(result => result.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
logout() {
|
logout() {
|
||||||
@ -21,7 +24,8 @@ export class APIClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
retrieveSessionUser() {
|
retrieveSessionUser() {
|
||||||
return this._callAPI('/me')
|
return this._callAPI('/me')
|
||||||
|
.then(result => result.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
listUsers() {
|
listUsers() {
|
||||||
@ -36,6 +40,12 @@ export class APIClient {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createUser(username, password) {
|
||||||
|
return this._callAPI('/users', { username, password }, 'POST')
|
||||||
|
.then(this._withAPIErrorMiddleware('create_user'))
|
||||||
|
.then(result => result.data);
|
||||||
|
}
|
||||||
|
|
||||||
updateRequestStatus(reqID, newStatus) {
|
updateRequestStatus(reqID, newStatus) {
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -50,13 +60,27 @@ export class APIClient {
|
|||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(this._withUnauthorizedErrorMiddleware())
|
||||||
if (res.status === UnauthorizedStatusCode) {
|
|
||||||
throw new UnauthorizedError();
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
})
|
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_withUnauthorizedErrorMiddleware() {
|
||||||
|
return res => {
|
||||||
|
if (res.status === UnauthorizedStatusCode) {
|
||||||
|
throw new UnauthorizedError();
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_withAPIErrorMiddleware(endpoint) {
|
||||||
|
return result => {
|
||||||
|
if (result.error) {
|
||||||
|
const { code, message, data } = result.error;
|
||||||
|
throw new APIError(endpoint, code, message, data);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,15 @@ import rootSaga from '../sagas/root'
|
|||||||
import { sessionReducer } from '../reducers/session.reducers';
|
import { sessionReducer } from '../reducers/session.reducers';
|
||||||
import { messagesReducer } from '../reducers/messages.reducers';
|
import { messagesReducer } from '../reducers/messages.reducers';
|
||||||
import project from '../reducers/project'
|
import project from '../reducers/project'
|
||||||
|
import { usersReducer } from '../reducers/users.reducers';
|
||||||
|
|
||||||
const sagaMiddleware = createSagaMiddleware()
|
const sagaMiddleware = createSagaMiddleware()
|
||||||
|
|
||||||
const rootReducer = combineReducers({
|
const rootReducer = combineReducers({
|
||||||
session: sessionReducer,
|
session: sessionReducer,
|
||||||
messages: messagesReducer,
|
messages: messagesReducer,
|
||||||
project
|
project,
|
||||||
|
users: usersReducer,
|
||||||
});
|
});
|
||||||
|
|
||||||
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
||||||
|
Loading…
Reference in New Issue
Block a user