Merge pull request #29 from Tiqa/feat/api/add_statement

Feat/api/add statement
This commit is contained in:
Jalil Arfaoui 2016-12-20 17:03:29 +01:00 committed by GitHub
commit 73599eb131
16 changed files with 61 additions and 107 deletions

View file

@ -1,4 +1,4 @@
{ {
"presets": ["es2015", "react", "stage-0"], "presets": ["es2015", "react", "stage-0"],
"plugins": ["transform-decorators-legacy", "react-hot-loader/babel"] "plugins": ["transform-decorators-legacy", "react-hot-loader/babel", "ramda"]
} }

View file

@ -79,6 +79,7 @@
"babel-core": "<6.3.0", "babel-core": "<6.3.0",
"babel-eslint": "^7.1.0", "babel-eslint": "^7.1.0",
"babel-loader": "~6.2.1", "babel-loader": "~6.2.1",
"babel-plugin-ramda": "^1.1.6",
"babel-plugin-transform-decorators-legacy": "^1.3.4", "babel-plugin-transform-decorators-legacy": "^1.3.4",
"babel-plugin-transform-es2015-modules-umd": "^6.8.0", "babel-plugin-transform-es2015-modules-umd": "^6.8.0",
"babel-preset-es2015": "<6.3.0", "babel-preset-es2015": "<6.3.0",

View file

@ -15,3 +15,4 @@ export const getStatements = () => get('statements');
export const getPublicFiguresAutocomplete = typed => get(`autocomplete/public_figure/${typed}`); export const getPublicFiguresAutocomplete = typed => get(`autocomplete/public_figure/${typed}`);
export const getSubjectsAutocomplete = typed => get(`autocomplete/subject/${typed}`); export const getSubjectsAutocomplete = typed => get(`autocomplete/subject/${typed}`);
export const getPositions = subjectId => get(`subjects/${subjectId}/positions`); export const getPositions = subjectId => get(`subjects/${subjectId}/positions`);
export const postStatement = statement => post('statements', statement);

View file

@ -2,7 +2,7 @@ import moment from 'moment';
import 'bootstrap-loader'; import 'bootstrap-loader';
const locale = 'fr'; const locale = 'fr';
moment.locale(locale);
moment.locale(locale, { moment.locale(locale, {
calendar : { calendar : {
lastDay : '[Hier]', lastDay : '[Hier]',

View file

@ -0,0 +1,10 @@
import { connect } from 'react-redux';
import { onAddStatementValidate } from 'store/actions';
const mapStateToProps = () => ({});
const mapDispatchToProps = dispatch => ({
onValidate: statement => dispatch(onAddStatementValidate(statement)),
});
export default connect(mapStateToProps, mapDispatchToProps);

View file

@ -1,11 +1,16 @@
import React, { Component } from 'react'; import React, { Component, PropTypes } from 'react';
import { Button } from 'react-bootstrap'; import { Button } from 'react-bootstrap';
import AddStatementModal from 'components/AddStatementModal'; import AddStatementModal from 'components/AddStatementModal';
import connect from './connector';
class AddStatementButton extends Component { class AddStatementButton extends Component {
static propTypes = {
onValidate: PropTypes.func.isRequired,
};
state = { state = {
showModal: false, showModal: false,
} };
close = () => this.setState({ showModal: false }); close = () => this.setState({ showModal: false });
open = () => this.setState({ showModal: true }); open = () => this.setState({ showModal: true });
@ -20,10 +25,10 @@ class AddStatementButton extends Component {
> >
Nouvelle prise de position Nouvelle prise de position
</Button> </Button>
<AddStatementModal show={this.state.showModal} onHide={this.close} /> <AddStatementModal show={this.state.showModal} onHide={this.close} onValidate={this.props.onValidate} />
</div> </div>
); );
} }
} }
export default AddStatementButton; export default connect(AddStatementButton);

View file

@ -285,7 +285,7 @@ class AddStatementModal extends Component {
} }
{step === steps.SUMMARY && {step === steps.SUMMARY &&
<Button <Button
onClick={onValidate} onClick={() => onValidate(this.state)}
disabled={!this.isComplete()} disabled={!this.isComplete()}
bsStyle="success" bsStyle="success"
> >

View file

@ -4,9 +4,11 @@ import LastStatements from 'components/LastStatements';
import HomeSubjects from './HomeSubjects'; import HomeSubjects from './HomeSubjects';
import bgSrc from './images/intro-bg.jpg'; import bgSrc from './images/intro-bg.jpg';
import styles from './Home.css'; import styles from './Home.css';
import AddStatementButton from 'components/AddStatementButton';
const Home = () => ( const Home = () => (
<div className="container-fluid" styleName="container"> <div className="container-fluid" styleName="container">
<AddStatementButton />
<div className="row" styleName="background-title" style={{ backgroundImage: `url(${bgSrc})` }}> <div className="row" styleName="background-title" style={{ backgroundImage: `url(${bgSrc})` }}>
<div styleName="title-mask"> <div styleName="title-mask">
<h5 >Bienvenue sur Débats.co</h5> <h5 >Bienvenue sur Débats.co</h5>
@ -16,7 +18,7 @@ const Home = () => (
</div> </div>
</div> </div>
</div> </div>
<div className="col-md-1" /> <div className="col-md-1"></div>
<div className="subjects-index col-md-7 subjects-home"> <div className="subjects-index col-md-7 subjects-home">
<h1>Sujets d'actualité</h1> <h1>Sujets d'actualité</h1>
<table className="table"> <table className="table">
@ -27,6 +29,7 @@ const Home = () => (
</tbody> </tbody>
</table> </table>
</div> </div>
<div className="col-md-1" />
<div className="col-md-3 col-centered" style={{ textAlign: 'right' }}> <div className="col-md-3 col-centered" style={{ textAlign: 'right' }}>
<LastStatements /> <LastStatements />
</div> </div>

View file

@ -1,27 +1,6 @@
import types from '../actions_types'; import types from '../actions_types';
export const onAddStatementPublicFigureSelection = id => ({ export const onAddStatementValidate = newStatement => ({
type: types.ADD_STATEMENT_PUBLIC_FIGURE_SELECTION,
id,
});
export const onAddStatementSubjectSelection = id => ({
type: types.ADD_STATEMENT_SUBJECT_SELECTION,
id,
});
export const onAddStatementPositionSelection = id => ({
type: types.ADD_STATEMENT_POSITION_SELECTION,
id,
});
export const onAddStatementUpdateEvidenceUrl = url => ({
type: types.ADD_STATEMENT_UPDATE_EVIDENCE_URL,
url,
});
export const onAddStatementUpdateEvidenceFile = file => ({
type: types.ADD_STATEMENT_UPDATE_EVIDENCE_FILE,
file,
});
export const onAddStatementValidate = () => ({
type: types.ADD_STATEMENT_VALIDATE, type: types.ADD_STATEMENT_VALIDATE,
payload: { ...newStatement },
}); });

View file

@ -1,11 +1,6 @@
export default { export default {
ENTITY_ACCESS: 'ENTITY_ACCESS', ENTITY_ACCESS: 'ENTITY_ACCESS',
ENTITY_READ: 'ENTITY_READ', ENTITY_READ: 'ENTITY_READ',
ADD_STATEMENT_NEXT_STEP: 'ADD_STATEMENT_NEXT_STEP',
ADD_STATEMENT_PUBLIC_FIGURE_SELECTION: 'ADD_STATEMENT_PUBLIC_FIGURE_SELECTION',
ADD_STATEMENT_SUBJECT_SELECTION: 'ADD_STATEMENT_SUBJECT_SELECTION',
ADD_STATEMENT_POSITION_SELECTION: 'ADD_STATEMENT_POSITION_SELECTION',
ADD_STATEMENT_UPDATE_EVIDENCE_URL: 'ADD_STATEMENT_UPDATE_EVIDENCE_URL',
ADD_STATEMENT_UPDATE_EVIDENCE_FILE: 'ADD_STATEMENT_UPDATE_EVIDENCE_FILE',
ADD_STATEMENT_VALIDATE: 'ADD_STATEMENT_VALIDATE', ADD_STATEMENT_VALIDATE: 'ADD_STATEMENT_VALIDATE',
STATEMENT_POST_BEGIN: 'STATEMENT_POST_BEGIN',
}; };

View file

@ -8,7 +8,7 @@ import createHashHistory from 'history/lib/createHashHistory';
import createMemoryHistory from 'history/lib/createMemoryHistory'; import createMemoryHistory from 'history/lib/createMemoryHistory';
import rootSaga from './sagas'; import rootSaga from './sagas';
import { entitiesReducer, addStatementReducer } from './reducers'; import { entitiesReducer } from './reducers';
// Build history // Build history
const createHistory = isClientSide() ? createHashHistory : createMemoryHistory; const createHistory = isClientSide() ? createHashHistory : createMemoryHistory;
@ -22,7 +22,6 @@ const initialState = isClientSide() ? window.__INITIAL_STATE__ : {};
const reducer = combineReducers({ const reducer = combineReducers({
routing: routerReducer, routing: routerReducer,
entities: entitiesReducer, entities: entitiesReducer,
addStatement: addStatementReducer,
}); });
// MIDDLEWARE // MIDDLEWARE

View file

@ -1,46 +0,0 @@
import {
assoc, compose, not, allPass, prop, isNil, either, complement,
} from 'ramda';
import actionsTypes from '../actions_types';
const initialState = {
publicFigureId: null,
subjectId: null,
positionId: null,
statementDate: null,
evidenceUrl: null,
evidenceFile: null,
quote: null,
note: null,
tags: [],
};
const isPublicFigureChosen = compose(not, isNil, prop('publicFigureId'));
const isSubjectChosen = compose(not, isNil, prop('publicFigureId'));
const isPositionChosen = compose(not, isNil, prop('publicFigureId'));
const isStatementComplete = allPass([
compose(not, isNil, prop('statementDate')),
compose(not, isNil, prop('quote')),
compose(not, isNil, prop('statementDate')),
either(
compose(not, isNil, prop('evidenceUrl')),
compose(not, isNil, prop('evidenceFile')),
),
]);
/* eslint-disable no-unused-vars */
const isPublicFigureMissing = complement(isPublicFigureChosen);
const isSubjectMissing = complement(isSubjectChosen);
const isPositionMissing = complement(isPositionChosen);
const isStatementIncomplete = complement(isStatementComplete);
export const addStatementReducer = (state = initialState, action) => {
switch (action.type) {
case actionsTypes.ADD_STATEMENT_PUBLIC_FIGURE_SELECTION: return assoc('publicFigureId', action.id, state);
case actionsTypes.ADD_STATEMENT_SUBJECT_SELECTION: return assoc('subjectId', action.id, state);
case actionsTypes.ADD_STATEMENT_POSITION_SELECTION: return assoc('positionId', action.id, state);
case actionsTypes.ADD_STATEMENT_UPDATE_EVIDENCE_URL: return assoc('evidenceUrl', action.url, state);
case actionsTypes.ADD_STATEMENT_UPDATE_EVIDENCE_FILE: return assoc('evidenceFile', action.file, state);
default: return state;
}
};

View file

@ -1,2 +1,2 @@
export * from './entities'; export * from './entities';
export * from './addStatement';

View file

@ -42,18 +42,4 @@ function* fetchEntityIfNeeded(action) {
export function* watchEntityAccess() { export function* watchEntityAccess() {
yield* takeEvery(actionsTypes.ENTITY_ACCESS, fetchEntityIfNeeded); yield* takeEvery(actionsTypes.ENTITY_ACCESS, fetchEntityIfNeeded);
} }
function* fetchPositionsOfSubject(action) {
if (action.id) {
// Call API
const response = yield call(getPositions, action.id);
// Error actions
// Success actions
yield put({ type: actionsTypes.ENTITY_READ, data: response.data });
}
}
export function* watchSubjectSelection() {
yield* takeEvery(actionsTypes.ADD_STATEMENT_SUBJECT_SELECTION, fetchPositionsOfSubject);
}

View file

@ -1,9 +1,9 @@
import { watchEntityAccess, watchSubjectSelection } from './apiSaga'; import { watchEntityAccess } from './apiSaga';
import { watchStatementValidation } from './postStatement';
export default function* rootSaga() { export default function* rootSaga() {
yield [ yield [
watchEntityAccess(), watchEntityAccess(),
watchSubjectSelection(), watchStatementValidation(),
]; ];
} }

View file

@ -0,0 +1,21 @@
import { takeEvery } from 'redux-saga';
import { call, put } from 'redux-saga/effects';
import actionsTypes from '../actions_types';
import { postStatement } from 'api/debats';
function* postNewStatement(action) {
yield put({ type: actionsTypes.STATEMENT_POST_BEGIN, payload: action.payload });
// Gérer d'abord les transferts de fichier sur un ID de transation
const response = yield call(postStatement);
// Error actions
// Success actions
yield put({ type: actionsTypes.STATEMENT_POST_SUCCESS, payload: action.payload });
}
export function* watchStatementValidation() {
yield* takeEvery(actionsTypes.ADD_STATEMENT_VALIDATE, postNewStatement);
}