From 4753e1f53594fcd2156bb4dccaf78b347abd0645 Mon Sep 17 00:00:00 2001
From: mama
Date: Wed, 17 Jan 2018 16:11:14 +0100
Subject: [PATCH 01/15] =?UTF-8?q?Les=20r=C3=A9sultats=20niveau=201=20sont?=
=?UTF-8?q?=20arrondis=20=C3=A0=20l'entier=20pr=C3=A8s?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
source/components/rule/RuleValueVignette.js | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/source/components/rule/RuleValueVignette.js b/source/components/rule/RuleValueVignette.js
index 945a4e70e..98fd90dc1 100644
--- a/source/components/rule/RuleValueVignette.js
+++ b/source/components/rule/RuleValueVignette.js
@@ -2,7 +2,6 @@ import React from 'react'
import { Link } from 'react-router-dom'
import { encodeRuleName } from 'Engine/rules'
import classNames from 'classnames'
-import { capitalise0 } from '../../utils'
let fmt = new Intl.NumberFormat('fr-FR').format
export let humanFigure = decimalDigits => 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 ?
*/
From cc1f25e4d71e1c14039dd45dfe9cab3aa9b29642 Mon Sep 17 00:00:00 2001
From: mama
Date: Wed, 17 Jan 2018 16:56:30 +0100
Subject: [PATCH 02/15] =?UTF-8?q?Recalcul=20des=20r=C3=A9sultats=20en=20te?=
=?UTF-8?q?mps=20r=C3=A9el=20pendant=20la=20saisie=20num=C3=A9rique?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../components/conversation/FormDecorator.js | 2 +-
source/reducers.js | 22 ++++++++++++++-----
2 files changed, 18 insertions(+), 6 deletions(-)
diff --git a/source/components/conversation/FormDecorator.js b/source/components/conversation/FormDecorator.js
index f477c6c2c..1db92d615 100644
--- a/source/components/conversation/FormDecorator.js
+++ b/source/components/conversation/FormDecorator.js
@@ -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.
*/
+ //TODO hack, enables redux-form/CHANGE to update the form state before the traverse functions are run
let submit = () => setTimeout(() => stepAction('fold', fieldName), 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)
}
diff --git a/source/reducers.js b/source/reducers.js
index 4286eaf52..319f34bd2 100644
--- a/source/reducers.js
+++ b/source/reducers.js
@@ -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, '@@redux-form/CHANGE'].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 === '@@redux-form/CHANGE') {
+ return { ...state, analysis, situationGate: situationWithDefaults(state) }
+ }
+
+ let nextWithDefaults = getNextSteps(situationWithDefaults(state), analysis),
assumptionsMade = !isEmpty(rulesDefaults),
done = nextWithDefaults.length == 0
From 110a9fd82c8b38035858dcf71a71380bb468dd05 Mon Sep 17 00:00:00 2001
From: mama
Date: Wed, 17 Jan 2018 17:39:26 +0100
Subject: [PATCH 03/15] Debounce de la saisie utilisateur
---
source/debounceFormChangeActions.js | 34 +++++++++++++++++++++++++++++
source/entry.js | 13 +++++++++--
source/reducers.js | 4 ++--
3 files changed, 47 insertions(+), 4 deletions(-)
create mode 100644 source/debounceFormChangeActions.js
diff --git a/source/debounceFormChangeActions.js b/source/debounceFormChangeActions.js
new file mode 100644
index 000000000..66d1dbfbe
--- /dev/null
+++ b/source/debounceFormChangeActions.js
@@ -0,0 +1,34 @@
+// Thank you, github.com/ryanseddon/redux-debounced
+
+export default () => {
+ let timers = {}
+
+ let time = 500
+
+ let middleware = () => dispatch => action => {
+ let { type } = action
+
+ let key = type
+
+ const shouldDebounce = key === '@@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 319f34bd2..c371c3648 100644
--- a/source/reducers.js
+++ b/source/reducers.js
@@ -51,7 +51,7 @@ export let reduceSteps = (tracker, flatRules, answerSource) => (
if (!state.parsedRules) state.parsedRules = parseAll(flatRules)
if (
- ![START_CONVERSATION, STEP_ACTION, '@@redux-form/CHANGE'].includes(
+ ![START_CONVERSATION, STEP_ACTION, 'USER_INPUT_UPDATE'].includes(
action.type
)
)
@@ -76,7 +76,7 @@ export let reduceSteps = (tracker, flatRules, answerSource) => (
situationWithDefaults(state)
)
- if (action.type === '@@redux-form/CHANGE') {
+ if (action.type === 'USER_INPUT_UPDATE') {
return { ...state, analysis, situationGate: situationWithDefaults(state) }
}
From dc4500d6ebdd1dd646c572e8fa8f4b5f4272a56c Mon Sep 17 00:00:00 2001
From: mama
Date: Wed, 17 Jan 2018 18:34:58 +0100
Subject: [PATCH 04/15] =?UTF-8?q?Suggestions=20vraiment=20ins=C3=A9r=C3=A9?=
=?UTF-8?q?es=20au=20clic=20seulement?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
source/components/conversation/Input.js | 25 +++++++++++--------
.../components/conversation/conversation.css | 7 +++---
2 files changed, 18 insertions(+), 14 deletions(-)
diff --git a/source/components/conversation/Input.js b/source/components/conversation/Input.js
index 50162c065..6e6a77b94 100644
--- a/source/components/conversation/Input.js
+++ b/source/components/conversation/Input.js
@@ -8,7 +8,7 @@ import SendButton from './SendButton'
@FormDecorator('input')
export default class Input extends Component {
state = {
- hoverSuggestion: null
+ lastValue: ''
}
render() {
let {
@@ -20,7 +20,6 @@ export default class Input extends Component {
answerSuffix = valueType.suffix,
suffixed = answerSuffix != null,
inputError = dirty && error,
- { hoverSuggestion } = this.state,
submitDisabled = !dirty || inputError
return (
@@ -33,7 +32,6 @@ export default class Input extends Component {
}}
type="text"
{...input}
- value={hoverSuggestion != null ? hoverSuggestion : input.value}
className={classnames({ suffixed })}
id={'step-' + dottedName}
{...attributes}
@@ -104,7 +102,7 @@ export default class Input extends Component {
)
}
renderSuggestions(themeColours) {
- let { setFormValue, submit, suggestions, inverted } = this.props.stepProps
+ let { setFormValue, suggestions, inverted } = this.props.stepProps
if (!suggestions || inverted) return null
return (
@@ -114,16 +112,21 @@ export default class Input extends Component {
{toPairs(suggestions).map(([text, value]) => (
- setFormValue('' + value) && submit() && e.preventDefault()
+ onClick={() => {
+ this.setState({ lastValue: null })
+ setFormValue('' + value)
+ }}
+ onMouseOver={() => {
+ this.setState({ lastValue: this.props.input.value })
+ setFormValue('' + value)
+ }}
+ onMouseOut={() =>
+ this.state.lastValue != null &&
+ setFormValue('' + this.state.lastValue)
}
- onMouseOver={() => this.setState({ hoverSuggestion: value })}
- onMouseOut={() => this.setState({ hoverSuggestion: null })}
style={{ color: themeColours.textColourOnWhite }}
>
-
- {text}
-
+ {text}
))}
diff --git a/source/components/conversation/conversation.css b/source/components/conversation/conversation.css
index 87623988d..f4a67d11c 100644
--- a/source/components/conversation/conversation.css
+++ b/source/components/conversation/conversation.css
@@ -315,7 +315,7 @@
font-style: italic;
float: right;
clear: right;
- font-size: 70%;
+ font-size: 75%;
color: #222;
}
.step .inputSuggestions ul {
@@ -326,11 +326,12 @@
}
.step .inputSuggestions li {
}
-.step .inputSuggestions a {
+.step .inputSuggestions span {
padding: 0.1em 0.9em;
font-weight: 600;
+ cursor: pointer;
}
-.step .inputSuggestions a:hover {
+.step .inputSuggestions span:hover {
text-decoration: none;
}
From 20d9c98c95d37d910c9c2a19b5ef18d871ce9038 Mon Sep 17 00:00:00 2001
From: mama
Date: Wed, 17 Jan 2018 19:12:54 +0100
Subject: [PATCH 05/15] =?UTF-8?q?Introduction=20du=20bouton=20valider=20da?=
=?UTF-8?q?ns=20les=20questions=20=C3=A0=20choix=20multiple?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Pour pouvoir comparer l'impact de deux réponses sans devoir revenir en
arrière
---
source/components/conversation/Question.js | 43 +++++++++++++---------
1 file changed, 26 insertions(+), 17 deletions(-)
diff --git a/source/components/conversation/Question.js b/source/components/conversation/Question.js
index b55fd4c52..207e9b0d4 100644
--- a/source/components/conversation/Question.js
+++ b/source/components/conversation/Question.js
@@ -4,7 +4,7 @@ import { answer, answered } from './userAnswerButtonStyle'
import HoverDecorator from '../HoverDecorator'
import Explicable from './Explicable'
import { pipe, split, reverse, reduce, is } from 'ramda'
-
+import SendButton from './SendButton'
/* Ceci est une saisie de type "radio" : l'utilisateur choisit une réponse dans une liste, ou une liste de listes.
Les données @choices sont un arbre de type:
- nom: motif CDD # La racine, unique, qui formera la Question. Ses enfants sont les choix possibles
@@ -21,26 +21,33 @@ import { pipe, split, reverse, reduce, is } from 'ramda'
*/
-let dottedNameToObject = pipe(
- split(' . '),
- reverse,
- reduce((memo, next) => ({ [next]: memo }), 'oui')
-)
-
// FormDecorator permet de factoriser du code partagé par les différents types de saisie,
// dont Question est un example
@FormDecorator('question')
export default class Question extends Component {
render() {
- let { stepProps: { choices } } = this.props
+ let {
+ stepProps: { choices },
+ themeColours,
+ stepProps: { submit }
+ } = this.props
+ let choiceElements = is(Array)(choices)
+ ? this.renderBinaryQuestion()
+ : this.renderChildren(choices)
- if (is(Array)(choices)) return this.renderBinaryQuestion()
- else return this.renderChildren(choices)
+ return (
+ <>
+ {choiceElements}
+
+ >
+ )
}
renderBinaryQuestion() {
let {
input, // vient de redux-form
- stepProps: { submit, choices },
+ stepProps: { submit, choices, setFormValue },
themeColours
} = this.props
@@ -49,7 +56,7 @@ export default class Question extends Component {
{choices.map(({ value, label }) => (
))}
@@ -62,7 +69,7 @@ export default class Question extends Component {
themeColours
} = this.props,
{ name } = input,
- { submit } = stepProps,
+ { submit, setFormValue } = stepProps,
// seront stockées ainsi dans le state :
// [parent object path]: dotted name relative to parent
relativeDottedName = radioDottedName =>
@@ -79,7 +86,8 @@ export default class Question extends Component {
input,
submit,
themeColours,
- dottedName: null
+ dottedName: null,
+ setFormValue
}}
/>
@@ -101,7 +109,8 @@ export default class Question extends Component {
dottedName,
input,
submit,
- themeColours
+ themeColours,
+ setFormValue
}}
/>
@@ -121,7 +130,7 @@ let RadioLabel = props => (
@HoverDecorator
class RadioLabelContent extends Component {
render() {
- let { value, label, input, submit, hover, themeColours } = this.props,
+ let { value, label, input, hover, themeColours, setFormValue } = this.props,
// value = when(is(Object), prop('value'))(choice),
labelStyle = Object.assign(
value === input.value || hover
@@ -136,7 +145,7 @@ class RadioLabelContent extends Component {
setFormValue(value)}
value={value}
checked={value === input.value ? 'checked' : ''}
/>
From 562e26639dfa89d6022955a923eeefabfe65fe3a Mon Sep 17 00:00:00 2001
From: mama
Date: Thu, 18 Jan 2018 18:58:15 +0100
Subject: [PATCH 06/15] =?UTF-8?q?:art:=20Am=C3=A9lioration=20du=20bouton?=
=?UTF-8?q?=20valider?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
source/components/conversation/SendButton.js | 2 +-
.../components/conversation/conversation.css | 23 +++++++++++++------
2 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/source/components/conversation/SendButton.js b/source/components/conversation/SendButton.js
index c995eb547..f198e074d 100644
--- a/source/components/conversation/SendButton.js
+++ b/source/components/conversation/SendButton.js
@@ -21,7 +21,7 @@ export default class SendButton extends Component {
onClick={this.getAction()}
>
valider
- ✓
+
Date: Thu, 18 Jan 2018 19:05:41 +0100
Subject: [PATCH 07/15] =?UTF-8?q?Le=20bouton=20valider=20dans=20les=20ques?=
=?UTF-8?q?tions=20=C3=A0=20choix=20peut=20=C3=AAtre=20d=C3=A9sactiv=C3=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
source/components/conversation/Question.js | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/source/components/conversation/Question.js b/source/components/conversation/Question.js
index 207e9b0d4..d3e18b021 100644
--- a/source/components/conversation/Question.js
+++ b/source/components/conversation/Question.js
@@ -27,19 +27,23 @@ import SendButton from './SendButton'
export default class Question extends Component {
render() {
let {
- stepProps: { choices },
+ stepProps: { choices, submit },
themeColours,
- stepProps: { submit }
+ meta: { pristine }
} = this.props
let choiceElements = is(Array)(choices)
? this.renderBinaryQuestion()
: this.renderChildren(choices)
-
return (
<>
{choiceElements}
>
)
From 64fd6b84bfc342840770c6864274560669b37470 Mon Sep 17 00:00:00 2001
From: mama
Date: Thu, 18 Jan 2018 19:15:29 +0100
Subject: [PATCH 08/15] =?UTF-8?q?Raccourci=20clavier=20'Entrer'=20pour=20v?=
=?UTF-8?q?alider=20les=20questions=20=C3=A0=20choix=20aussi?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
source/components/conversation/Input.js | 3 ---
source/components/conversation/SendButton.js | 13 +++++++++++++
2 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/source/components/conversation/Input.js b/source/components/conversation/Input.js
index 6e6a77b94..50821e296 100644
--- a/source/components/conversation/Input.js
+++ b/source/components/conversation/Input.js
@@ -40,9 +40,6 @@ export default class Input extends Component {
? { border: '2px dashed #ddd' }
: { border: `1px solid ${themeColours.textColourOnWhite}` }
}
- onKeyDown={({ key }) =>
- key == 'Enter' && (submitDisabled ? input.onBlur() : submit())
- }
/>
{suffixed && (
)}
@@ -46,13 +51,3 @@ export default class ProgressTip extends Component {
)
}
}
-
-let ProgressBar = ({ foldedSteps, nbQuestions, colour }) => (
-
-)
From ae610eb2b41c32834de06351e9c77c01d6c8a089 Mon Sep 17 00:00:00 2001
From: mama
Date: Tue, 30 Jan 2018 17:11:33 +0100
Subject: [PATCH 13/15] =?UTF-8?q?:bug:=20Si=20erreur=20de=20saisie,=20pas?=
=?UTF-8?q?=20de=20mise=20=C3=A0=20jour=20des=20r=C3=A9sultats?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Cela évite de montrer un NaN
---
source/debounceFormChangeActions.js | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/source/debounceFormChangeActions.js b/source/debounceFormChangeActions.js
index 66d1dbfbe..2074ba33f 100644
--- a/source/debounceFormChangeActions.js
+++ b/source/debounceFormChangeActions.js
@@ -10,12 +10,15 @@ export default () => {
let key = type
- const shouldDebounce = key === '@@redux-form/CHANGE'
+ let shouldDebounce = key === '@@redux-form/CHANGE'
- if (!shouldDebounce) {
- return dispatch(action)
+ 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])
}
From 3d206cb820663b48de2ba0723debefbea531be35 Mon Sep 17 00:00:00 2001
From: Laurent Bossavit
Date: Tue, 30 Jan 2018 14:50:47 +0100
Subject: [PATCH 14/15] =?UTF-8?q?:chart=5Fwith=5Fupwards=5Ftrend:=20Am?=
=?UTF-8?q?=C3=A9liorer=20le=20tracking?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
source/actions.js | 4 ++--
source/components/conversation/FormDecorator.js | 8 ++++----
source/components/conversation/Input.js | 2 +-
source/components/conversation/Question.js | 2 +-
source/components/conversation/SendButton.js | 6 +++---
source/reducers.js | 12 ++++++++++--
6 files changed, 21 insertions(+), 13 deletions(-)
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/conversation/FormDecorator.js b/source/components/conversation/FormDecorator.js
index 1db92d615..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))
})
@@ -76,7 +76,7 @@ export var FormDecorator = formType => RenderField =>
des balises html, dans notre cas.
*/
//TODO hack, enables redux-form/CHANGE to update the form state before the traverse functions are run
- let submit = () => setTimeout(() => stepAction('fold', fieldName), 1),
+ let submit = (cause) => setTimeout(() => stepAction('fold', fieldName, cause), 1),
stepProps = {
...this.props.step,
inverted,
@@ -121,7 +121,7 @@ export var FormDecorator = formType => RenderField =>
{
setFormValue(fieldName, '' + defaultValue)
- submit()
+ submit('ignore')
}}
/>
)}
@@ -159,7 +159,7 @@ export var FormDecorator = formType => RenderField =>