Modification des reducers pour bien prendre en compte les défauts
✨ Déplacement de 'fromConversation' vers rules.js
pull/138/head
parent
aba5248e58
commit
9b545ee583
|
@ -13,7 +13,6 @@
|
|||
- motif . classique . saisonnier
|
||||
- motif . contrat aidé
|
||||
|
||||
|
||||
formule:
|
||||
multiplication:
|
||||
assiette: assiette cotisations sociales
|
||||
|
@ -36,7 +35,7 @@
|
|||
événement: aucun
|
||||
motif: accroissement activité
|
||||
contrat jeune vacances: non
|
||||
|
||||
|
||||
assiette cotisations sociales: 1480
|
||||
valeur attendue: 14.8
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ import Results from 'Components/Results'
|
|||
state => ({
|
||||
currentQuestion: state.currentQuestion,
|
||||
foldedSteps: state.foldedSteps,
|
||||
extraSteps: state.extraSteps,
|
||||
themeColours: state.themeColours,
|
||||
situationGate: state.situationGate,
|
||||
targetNames: state.targetNames,
|
||||
|
@ -65,7 +64,6 @@ export default class extends Component {
|
|||
|
||||
let {
|
||||
foldedSteps,
|
||||
extraSteps,
|
||||
currentQuestion,
|
||||
situationGate,
|
||||
themeColours,
|
||||
|
@ -109,14 +107,6 @@ export default class extends Component {
|
|||
),
|
||||
foldedSteps
|
||||
),
|
||||
extraSteps: R.map(
|
||||
this.buildStep({ unfolded: true })(
|
||||
situationGate,
|
||||
targetNames,
|
||||
inputInversions
|
||||
),
|
||||
extraSteps
|
||||
),
|
||||
textColourOnWhite: themeColours.textColourOnWhite
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -11,7 +11,7 @@ import Scroll from 'react-scroll'
|
|||
})
|
||||
export default class Conversation extends Component {
|
||||
render() {
|
||||
let {foldedSteps, currentQuestion, extraSteps, reinitalise, textColourOnWhite} = this.props
|
||||
let {foldedSteps, currentQuestion, reinitalise, textColourOnWhite} = this.props
|
||||
|
||||
Scroll.animateScroll.scrollToBottom()
|
||||
return (
|
||||
|
@ -30,13 +30,13 @@ export default class Conversation extends Component {
|
|||
</div>
|
||||
}
|
||||
{!currentQuestion &&
|
||||
<Conclusion affiner={!R.isEmpty(extraSteps)}/>}
|
||||
{ !R.isEmpty(extraSteps) &&
|
||||
<Conclusion affiner={!R.isEmpty({})}/>}
|
||||
{ !R.isEmpty({}) &&
|
||||
<div id="foldedSteps">
|
||||
<div className="header" >
|
||||
<h3>Affiner votre situation</h3>
|
||||
</div>
|
||||
{extraSteps}
|
||||
{}
|
||||
</div>
|
||||
}
|
||||
<div id="currentQuestion">
|
||||
|
|
|
@ -5,6 +5,7 @@ import R from 'ramda'
|
|||
import possibleVariableTypes from './possibleVariableTypes.yaml'
|
||||
import marked from './marked'
|
||||
import {capitalise0} from '../utils'
|
||||
import formValueTypes from 'Components/conversation/formValueTypes'
|
||||
|
||||
// TODO - should be in UI, not engine
|
||||
import taux_versement_transport from '../../règles/rémunération-travail/cotisations/ok/liste-taux.json'
|
||||
|
@ -108,3 +109,16 @@ export let findRuleByDottedName = (allRules, dottedName) => {
|
|||
Autres */
|
||||
|
||||
let isVariant = R.path(['formule', 'une possibilité'])
|
||||
|
||||
export let formatInputs = (flatRules, formValueSelector) => state => name => {
|
||||
// Our situationGate retrieves data from the "conversation" form
|
||||
// The search below is to apply input conversions such as replacing "," with "."
|
||||
if (name.startsWith('sys.')) return null
|
||||
|
||||
let rule = findRuleByDottedName(flatRules, name),
|
||||
format = rule ? formValueTypes[rule.format] : null,
|
||||
pre = format && format.validator.pre ? format.validator.pre : R.identity,
|
||||
value = formValueSelector('conversation')(state, name)
|
||||
|
||||
return value && pre(value)
|
||||
}
|
||||
|
|
|
@ -1,32 +1,28 @@
|
|||
import R from 'ramda'
|
||||
import { combineReducers } from 'redux'
|
||||
import reduceReducers from 'reduce-reducers'
|
||||
import {reducer as formReducer, formValueSelector} from 'redux-form'
|
||||
import { reducer as formReducer, formValueSelector } from 'redux-form'
|
||||
|
||||
import {rules, findRuleByName, findRuleByDottedName, collectDefaults} from 'Engine/rules'
|
||||
import {nextSteps} from 'Engine/generateQuestions'
|
||||
import {
|
||||
rules,
|
||||
findRuleByName,
|
||||
collectDefaults,
|
||||
nameLeaf,
|
||||
formatInputs
|
||||
} from 'Engine/rules'
|
||||
import { nextSteps } from 'Engine/generateQuestions'
|
||||
import computeThemeColours from 'Components/themeColours'
|
||||
import { STEP_ACTION, START_CONVERSATION, EXPLAIN_VARIABLE, CHANGE_THEME_COLOUR} from './actions'
|
||||
import {
|
||||
STEP_ACTION,
|
||||
START_CONVERSATION,
|
||||
EXPLAIN_VARIABLE,
|
||||
CHANGE_THEME_COLOUR
|
||||
} from './actions'
|
||||
|
||||
import {analyse} from 'Engine/traverse'
|
||||
import { analyse } from 'Engine/traverse'
|
||||
|
||||
import ReactPiwik from 'Components/Tracker'
|
||||
|
||||
import formValueTypes from 'Components/conversation/formValueTypes'
|
||||
|
||||
let fromConversation = flatRules => state => name => {
|
||||
// Our situationGate retrieves data from the "conversation" form
|
||||
// The search below is to apply input conversions such as replacing "," with "."
|
||||
if (name.startsWith("sys.")) return null
|
||||
|
||||
let rule = findRuleByDottedName(flatRules, name),
|
||||
format = rule ? formValueTypes[rule.format] : null,
|
||||
pre = format && format.validator.pre ? format.validator.pre : R.identity,
|
||||
value = formValueSelector('conversation')(state, name)
|
||||
|
||||
return value && pre(value)
|
||||
}
|
||||
|
||||
// assume "wraps" a given situation function with one that overrides its values with
|
||||
// the given assumptions
|
||||
let assume = (evaluator, assumptions) => state => name => {
|
||||
|
@ -34,73 +30,73 @@ let assume = (evaluator, assumptions) => state => name => {
|
|||
return userInput != null ? userInput : assumptions[name]
|
||||
}
|
||||
|
||||
export let reduceSteps = (tracker, flatRules, answerSource) => (state, action) => {
|
||||
if (![START_CONVERSATION, STEP_ACTION].includes(action.type))
|
||||
return state
|
||||
export let reduceSteps = (tracker, flatRules, answerSource) => (
|
||||
state,
|
||||
action
|
||||
) => {
|
||||
if (![START_CONVERSATION, STEP_ACTION].includes(action.type)) return state
|
||||
|
||||
let targetNames = action.type == START_CONVERSATION ? action.targetNames : state.targetNames
|
||||
let targetNames =
|
||||
action.type == START_CONVERSATION ? action.targetNames : state.targetNames
|
||||
|
||||
let sim = targetNames.length === 1 ? findRuleByName(flatRules, targetNames[0]) : {},
|
||||
let sim =
|
||||
targetNames.length === 1 ? findRuleByName(flatRules, targetNames[0]) : {},
|
||||
// Hard assumptions cannot be changed, they are used to specialise a simulator
|
||||
// before the user sees the first question
|
||||
hardAssumptions = R.pathOr({},['simulateur','hypothèses'],sim),
|
||||
hardAssumptions = R.pathOr({}, ['simulateur', 'hypothèses'], sim),
|
||||
intermediateSituation = assume(answerSource, hardAssumptions),
|
||||
// Most rules have default values
|
||||
rulesDefaults = collectDefaults(flatRules),
|
||||
situationWithDefaults = assume(intermediateSituation, rulesDefaults)
|
||||
|
||||
let situationGate = situationWithDefaults(state),
|
||||
let
|
||||
parsedRules = R.path(['analysis', 'parsedRules'], state),
|
||||
analysis = analyse(parsedRules || flatRules, targetNames)(situationGate)
|
||||
analysis = analyse(parsedRules || flatRules, targetNames)(situationWithDefaults(state)),
|
||||
next = nextSteps(situationWithDefaults(state), flatRules, analysis),
|
||||
assumptionsMade = !R.isEmpty(rulesDefaults),
|
||||
done = next.length == 0,
|
||||
currentQuestion =
|
||||
done && assumptionsMade
|
||||
? // The simulation is "over" - except we can now fill in extra questions
|
||||
// where the answers were previously given default reasonable assumptions
|
||||
do {
|
||||
let
|
||||
reanalysis = analyse(analysis.parsedRules, targetNames)(
|
||||
intermediateSituation(state)
|
||||
),
|
||||
next = nextSteps(intermediateSituation(state), flatRules, reanalysis)
|
||||
R.head(next)
|
||||
}
|
||||
: R.head(next)
|
||||
|
||||
let newState = {
|
||||
...state,
|
||||
targetNames,
|
||||
analysis,
|
||||
situationGate: situationGate,
|
||||
extraSteps: [],
|
||||
explainedVariable: null
|
||||
situationGate: situationWithDefaults(state),
|
||||
explainedVariable: null,
|
||||
currentQuestion
|
||||
}
|
||||
|
||||
if (action.type == START_CONVERSATION) {
|
||||
let next = nextSteps(situationGate, flatRules, newState.analysis)
|
||||
return {
|
||||
...newState,
|
||||
foldedSteps: [],
|
||||
currentQuestion: R.head(next)
|
||||
// when objectives change, reject theme from answered questions
|
||||
foldedSteps: R.reject(name => targetNames.includes(nameLeaf(name)))(
|
||||
state.foldedSteps
|
||||
)
|
||||
}
|
||||
}
|
||||
if (action.type == STEP_ACTION && action.name == 'fold') {
|
||||
tracker.push(['trackEvent', 'answer', action.step+': '+situationGate(action.step)])
|
||||
|
||||
let foldedSteps = [...state.foldedSteps, state.currentQuestion],
|
||||
next = nextSteps(situationGate, flatRules, newState.analysis),
|
||||
assumptionsMade = !R.isEmpty(rulesDefaults),
|
||||
done = next.length == 0
|
||||
|
||||
// The simulation is "over" - except we can now fill in extra questions
|
||||
// where the answers were previously given default reasonable assumptions
|
||||
if (done && assumptionsMade) {
|
||||
let newSituation = intermediateSituation(state),
|
||||
reanalysis = analyse(analysis.parsedRules, targetNames)(newSituation),
|
||||
extraSteps = nextSteps(newSituation, flatRules, reanalysis)
|
||||
|
||||
tracker.push(['trackEvent', 'done', 'extra questions: '+extraSteps.length])
|
||||
|
||||
return {
|
||||
...newState,
|
||||
foldedSteps,
|
||||
currentQuestion: R.head(extraSteps)
|
||||
}
|
||||
}
|
||||
|
||||
if (done) {
|
||||
tracker.push(['trackEvent', 'done', 'no more questions'])
|
||||
}
|
||||
tracker.push([
|
||||
'trackEvent',
|
||||
'answer',
|
||||
action.step + ': ' + situationWithDefaults(state)(action.step)
|
||||
])
|
||||
|
||||
return {
|
||||
...newState,
|
||||
foldedSteps,
|
||||
currentQuestion: R.head(next)
|
||||
foldedSteps: [...state.foldedSteps, state.currentQuestion]
|
||||
}
|
||||
}
|
||||
if (action.type == STEP_ACTION && action.name == 'unfold') {
|
||||
|
@ -110,7 +106,9 @@ export let reduceSteps = (tracker, flatRules, answerSource) => (state, action) =
|
|||
let previous = state.currentQuestion,
|
||||
// we fold it back into foldedSteps if it had been answered
|
||||
answered = previous && answerSource(state)(previous) != undefined,
|
||||
foldedSteps = answered ? R.concat(state.foldedSteps, [previous]) : state.foldedSteps
|
||||
foldedSteps = answered
|
||||
? R.concat(state.foldedSteps, [previous])
|
||||
: state.foldedSteps
|
||||
|
||||
return {
|
||||
...newState,
|
||||
|
@ -120,13 +118,12 @@ export let reduceSteps = (tracker, flatRules, answerSource) => (state, action) =
|
|||
}
|
||||
}
|
||||
|
||||
function themeColours(state = computeThemeColours(), {type, colour}) {
|
||||
if (type == CHANGE_THEME_COLOUR)
|
||||
return computeThemeColours(colour)
|
||||
function themeColours(state = computeThemeColours(), { type, colour }) {
|
||||
if (type == CHANGE_THEME_COLOUR) return computeThemeColours(colour)
|
||||
else return state
|
||||
}
|
||||
|
||||
function explainedVariable(state = null, {type, variableName=null}) {
|
||||
function explainedVariable(state = null, { type, variableName = null }) {
|
||||
switch (type) {
|
||||
case EXPLAIN_VARIABLE:
|
||||
return variableName
|
||||
|
@ -135,17 +132,15 @@ function explainedVariable(state = null, {type, variableName=null}) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
export default reduceReducers(
|
||||
combineReducers({
|
||||
sessionId: (id = Math.floor(Math.random() * 1000000000000) + '') => id,
|
||||
sessionId: (id = Math.floor(Math.random() * 1000000000000) + '') => id,
|
||||
// this is handled by redux-form, pas touche !
|
||||
form: formReducer,
|
||||
|
||||
/* Have forms been filled or ignored ?
|
||||
false means the user is reconsidering its previous input */
|
||||
foldedSteps: (steps = []) => steps,
|
||||
extraSteps: (steps = []) => steps,
|
||||
currentQuestion: (state = null) => state,
|
||||
|
||||
analysis: (state = null) => state,
|
||||
|
@ -158,8 +153,7 @@ export default reduceReducers(
|
|||
themeColours,
|
||||
|
||||
explainedVariable
|
||||
|
||||
}),
|
||||
// cross-cutting concerns because here `state` is the whole state tree
|
||||
reduceSteps(ReactPiwik, rules, fromConversation(rules))
|
||||
reduceSteps(ReactPiwik, rules, formatInputs(rules, formValueSelector))
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue