diff --git a/package.json b/package.json
index ce29967dd..6104ac605 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
"nearley": "^2.9.2",
"npm": "^5.3.0",
"ramda": "^0.25.0",
+ "rc-progress": "^2.2.5",
"react": "^16.2.0",
"react-addons-css-transition-group": "^15.6.2",
"react-color": "^2.13.8",
diff --git a/source/actions.js b/source/actions.js
index 6516f6bd2..9e187b9ed 100644
--- a/source/actions.js
+++ b/source/actions.js
@@ -2,8 +2,8 @@
// The state keeps track of which of them have been submitted
// The user can also come back to one of his answers and edit it
export const STEP_ACTION = 'STEP_ACTION'
-export function stepAction(name, step) {
- return { type: STEP_ACTION, name, step }
+export function stepAction(name, step, source) {
+ return { type: STEP_ACTION, name, step, source }
}
export const START_CONVERSATION = 'START_CONVERSATION'
diff --git a/source/components/ProgressTip.css b/source/components/ProgressTip.css
index 7b6244401..d3853d58b 100644
--- a/source/components/ProgressTip.css
+++ b/source/components/ProgressTip.css
@@ -1,6 +1,6 @@
#sim .tip {
font-style: italic;
- margin-bottom: 1em;
+ margin-bottom: -0.3em;
text-align: center;
}
#sim .tip p {
@@ -12,10 +12,3 @@
border-width: 1px;
border-style: solid;
}
-
-progress::-webkit-progress-bar {
- background: white;
-}
-
-progress::-webkit-progress-value {
-}
diff --git a/source/components/ProgressTip.js b/source/components/ProgressTip.js
index e64b8bf65..3e84ebbd4 100644
--- a/source/components/ProgressTip.js
+++ b/source/components/ProgressTip.js
@@ -2,13 +2,14 @@ import React, { Component } from 'react'
import { connect } from 'react-redux'
import { withRouter } from 'react-router'
import './ProgressTip.css'
+import { Line } from 'rc-progress'
@withRouter
@connect(state => ({
done: state.done,
foldedSteps: state.foldedSteps,
nextSteps: state.nextSteps,
- textColourOnWhite: state.themeColours.textColourOnWhite
+ colour: state.themeColours.colour
}))
export default class ProgressTip extends Component {
state = {
@@ -21,7 +22,7 @@ export default class ProgressTip extends Component {
})
}
render() {
- let { done, nextSteps, foldedSteps, textColourOnWhite } = this.props,
+ let { done, nextSteps, foldedSteps, colour } = this.props,
nbQuestions = nextSteps.length
if (!done) return null
return (
@@ -35,10 +36,14 @@ export default class ProgressTip extends Component {
{nbQuestions === 1
? 'Une dernière question !'
: `Il reste moins de ${nbQuestions} questions`}
-
)}
@@ -46,13 +51,3 @@ export default class ProgressTip extends Component {
)
}
}
-
-let ProgressBar = ({ foldedSteps, nbQuestions, colour }) => (
-
-)
diff --git a/source/components/conversation/FormDecorator.js b/source/components/conversation/FormDecorator.js
index f477c6c2c..69dac8ee2 100644
--- a/source/components/conversation/FormDecorator.js
+++ b/source/components/conversation/FormDecorator.js
@@ -25,7 +25,7 @@ export var FormDecorator = formType => RenderField =>
formValueSelector('conversation')(state, 'inversions.' + dottedName)
}),
dispatch => ({
- stepAction: (name, step) => dispatch(stepAction(name, step)),
+ stepAction: (name, step, source) => dispatch(stepAction(name, step, source)),
setFormValue: (field, value) =>
dispatch(change('conversation', field, value))
})
@@ -75,11 +75,11 @@ export var FormDecorator = formType => RenderField =>
props passées à ce dernier, car React 15.2 n'aime pas les attributes inconnus
des balises html, dans notre cas.
*/
- let submit = () => setTimeout(() => stepAction('fold', fieldName), 1),
+ //TODO hack, enables redux-form/CHANGE to update the form state before the traverse functions are run
+ let submit = (cause) => setTimeout(() => stepAction('fold', fieldName, cause), 1),
stepProps = {
...this.props.step,
inverted,
- //TODO hack, enables redux-form/CHANGE to update the form state before the traverse functions are run
submit,
setFormValue: (value, name = fieldName) => setFormValue(name, value)
}
@@ -121,7 +121,7 @@ export var FormDecorator = formType => RenderField =>
{
setFormValue(fieldName, '' + defaultValue)
- submit()
+ submit('ignore')
}}
/>
)}
@@ -159,7 +159,7 @@ export var FormDecorator = formType => RenderField =>
value =>
fmt(value.toFixed(decimalDigits))
@@ -45,7 +44,7 @@ let RuleValue = ({ unsatisfied, irrelevant, conversationStarted, ruleValue }) =>
? ['irrelevant', "Vous n'êtes pas concerné"]
: unsatisfied
? ['unsatisfied', 'En attente de vos réponses...']
- : ['figure', humanFigure(2)(ruleValue) + ' €']
+ : ['figure', humanFigure(0)(ruleValue) + ' €']
{
/*Pourquoi ?
*/
diff --git a/source/debounceFormChangeActions.js b/source/debounceFormChangeActions.js
new file mode 100644
index 000000000..2074ba33f
--- /dev/null
+++ b/source/debounceFormChangeActions.js
@@ -0,0 +1,37 @@
+// Thank you, github.com/ryanseddon/redux-debounced
+
+export default () => {
+ let timers = {}
+
+ let time = 500
+
+ let middleware = () => dispatch => action => {
+ let { type } = action
+
+ let key = type
+
+ let shouldDebounce = key === '@@redux-form/CHANGE'
+
+ if (key === '@@redux-form/UPDATE_SYNC_ERRORS') {
+ dispatch(action)
+ return clearTimeout(timers['@@redux-form/CHANGE'])
+ }
+
+ if (!shouldDebounce) return dispatch(action)
+
+ if (timers[key]) {
+ clearTimeout(timers[key])
+ }
+
+ dispatch(action)
+ return new Promise(resolve => {
+ timers[key] = setTimeout(() => {
+ resolve(dispatch({ type: 'USER_INPUT_UPDATE' }))
+ }, time)
+ })
+ }
+
+ middleware._timers = timers
+
+ return middleware
+}
diff --git a/source/entry.js b/source/entry.js
index 37c45582d..f425367a1 100644
--- a/source/entry.js
+++ b/source/entry.js
@@ -1,10 +1,11 @@
import React from 'react'
import { render } from 'react-dom'
-import { compose, createStore } from 'redux'
+import { compose, createStore, applyMiddleware } from 'redux'
import App from './containers/App'
import reducers from './reducers'
import DevTools from './DevTools'
import { AppContainer } from 'react-hot-loader'
+import debounceFormChangeActions from './debounceFormChangeActions'
import computeThemeColours from './components/themeColours'
import { getIframeOption, getUrl } from './utils'
@@ -13,7 +14,15 @@ let initialStore = {
themeColours: computeThemeColours(getIframeOption('couleur'))
}
-let store = createStore(reducers, initialStore, compose(DevTools.instrument()))
+let createStoreWithMiddleware = applyMiddleware(debounceFormChangeActions())(
+ createStore
+)
+
+let store = createStoreWithMiddleware(
+ reducers,
+ initialStore,
+ compose(DevTools.instrument())
+)
let anchor = document.querySelector('#js')
render(, anchor)
diff --git a/source/reducers.js b/source/reducers.js
index 4286eaf52..60488cf59 100644
--- a/source/reducers.js
+++ b/source/reducers.js
@@ -1,4 +1,4 @@
-import { head, isEmpty, pathOr, reject, contains, without, concat } from 'ramda'
+import { head, isEmpty, pathOr, reject, contains, without, concat, length } from 'ramda'
import { combineReducers } from 'redux'
import reduceReducers from 'reduce-reducers'
import { reducer as formReducer, formValueSelector } from 'redux-form'
@@ -50,10 +50,17 @@ export let reduceSteps = (tracker, flatRules, answerSource) => (
// Optimization - don't parse on each analysis
if (!state.parsedRules) state.parsedRules = parseAll(flatRules)
- if (![START_CONVERSATION, STEP_ACTION].includes(action.type)) return state
+ if (
+ ![START_CONVERSATION, STEP_ACTION, 'USER_INPUT_UPDATE'].includes(
+ action.type
+ )
+ )
+ return state
let targetNames =
- action.type == START_CONVERSATION ? action.targetNames : state.targetNames
+ action.type == START_CONVERSATION
+ ? action.targetNames
+ : state.targetNames || []
let sim =
targetNames.length === 1 ? findRuleByName(flatRules, targetNames[0]) : {},
@@ -66,9 +73,14 @@ export let reduceSteps = (tracker, flatRules, answerSource) => (
situationWithDefaults = assume(intermediateSituation, rulesDefaults)
let analysis = analyseMany(state.parsedRules, targetNames)(
- situationWithDefaults(state)
- ),
- nextWithDefaults = getNextSteps(situationWithDefaults(state), analysis),
+ situationWithDefaults(state)
+ )
+
+ if (action.type === 'USER_INPUT_UPDATE') {
+ return { ...state, analysis, situationGate: situationWithDefaults(state) }
+ }
+
+ let nextWithDefaults = getNextSteps(situationWithDefaults(state), analysis),
assumptionsMade = !isEmpty(rulesDefaults),
done = nextWithDefaults.length == 0
@@ -104,10 +116,18 @@ export let reduceSteps = (tracker, flatRules, answerSource) => (
if (action.type == STEP_ACTION && action.name == 'fold') {
tracker.push([
'trackEvent',
- 'answer',
+ 'answer:'+action.source,
action.step + ': ' + situationWithDefaults(state)(action.step)
])
+ if (!newState.currentQuestion) {
+ tracker.push([
+ 'trackEvent',
+ 'done',
+ 'after'+length(newState.foldedSteps)+'questions'
+ ])
+ }
+
return {
...newState,
foldedSteps: [...state.foldedSteps, state.currentQuestion]