/* @flow */ import { compose, defaultTo, identity, isNil, lensPath, omit, over, set, uniq, without } from 'ramda' import reduceReducers from 'reduce-reducers' import { combineReducers } from 'redux' import i18n from '../i18n' import inFranceAppReducer from './inFranceAppReducer' import storageReducer from './storageReducer' import { findRuleByDottedName } from 'Engine/rules' import { targetNamesSelector } from 'Selectors/analyseSelectors' import type { Action } from 'Types/ActionsTypes' function explainedVariable(state = null, { type, variableName = null }) { switch (type) { case 'EXPLAIN_VARIABLE': return variableName case 'STEP_ACTION': return null default: return state } } function currentExample(state = null, { type, situation, name, dottedName }) { switch (type) { case 'SET_EXAMPLE': return name != null ? { name, situation, dottedName } : null default: return state } } function situationBranch(state = null, { type, id }) { switch (type) { case 'SET_SITUATION_BRANCH': return id default: return state } } function activeTargetInput(state = null, { type, name }) { switch (type) { case 'SET_ACTIVE_TARGET_INPUT': return name case 'RESET_SIMULATION': return null default: return state } } function lang(state = i18n.language, { type, lang }) { switch (type) { case 'SWITCH_LANG': return lang default: return state } } type ConversationSteps = {| +foldedSteps: Array, +unfoldedStep: ?string |} function conversationSteps( state: ConversationSteps = { foldedSteps: [], unfoldedStep: null }, action: Action ): ConversationSteps { if (action.type === 'RESET_SIMULATION') return { foldedSteps: [], unfoldedStep: null } if (action.type !== 'STEP_ACTION') return state const { name, step } = action if (name === 'fold') return { foldedSteps: [...state.foldedSteps, step], unfoldedStep: null } if (name === 'unfold') { return { foldedSteps: without([step], state.foldedSteps), unfoldedStep: step } } return state } function updateSituation(situation, { fieldName, value, config, rules }) { const goals = targetNamesSelector({ simulation: { config } }).filter( dottedName => { const target = rules.find(r => r.dottedName === dottedName) const isSmallTarget = !target.question || !target.formule return !isSmallTarget } ) const removePreviousTarget = goals.includes(fieldName) ? omit(goals) : identity return { ...removePreviousTarget(situation), [fieldName]: value } } function updatePeriod(situation, { toPeriod, rules }) { const currentPeriod = situation['période'] if (currentPeriod === toPeriod) { return situation } if (!['mois', 'année'].includes(toPeriod)) { throw new Error('Oups, changement de période invalide') } const needConversion = Object.keys(situation).filter(dottedName => { const rule = findRuleByDottedName(rules, dottedName) return rule?.période === 'flexible' }) const updatedSituation = Object.entries(situation) .filter(([fieldName]) => needConversion.includes(fieldName)) .map(([fieldName, value]) => [ fieldName, currentPeriod === 'mois' && toPeriod === 'année' ? value * 12 : value / 12 ]) return { ...situation, ...Object.fromEntries(updatedSituation), période: toPeriod } } function simulation(state = null, action, rules) { if (action.type === 'SET_SIMULATION') { const { config, url } = action return { config, url, hiddenControls: [], situation: {} } } if (state === null) { return state } switch (action.type) { case 'HIDE_CONTROL': return { ...state, hiddenControls: [...state.hiddenControls, action.id] } case 'RESET_SIMULATION': return { ...state, hiddenControls: [], situation: {} } case 'UPDATE_SITUATION': return { ...state, situation: updateSituation(state.situation, { fieldName: action.fieldName, value: action.value, config: state.config, rules }) } case 'UPDATE_PERIOD': return { ...state, situation: updatePeriod(state.situation, { toPeriod: action.toPeriod, rules }) } } return state } const addAnswerToSituation = (dottedName, value, state) => { const dottedPath = dottedName.split(' . ') return compose( set(lensPath(['form', 'conversation', 'values', ...dottedPath]), value), over(lensPath(['conversationSteps', 'foldedSteps']), (steps = []) => uniq([...steps, dottedName]) ) )(state) } const existingCompanyReducer = (state, action) => { if (action.type !== 'SAVE_EXISTING_COMPANY_DETAILS') { return state } const details = action.details let newState = state if (details.localisation) { newState = addAnswerToSituation( 'établissement . localisation', JSON.stringify(details.localisation), newState ) } if (!isNil(details.effectif)) { newState = addAnswerToSituation( 'entreprise . effectif', details.effectif, newState ) } return newState } export default reduceReducers( existingCompanyReducer, storageReducer, (state, action) => combineReducers({ sessionId: defaultTo(Math.floor(Math.random() * 1000000000000) + ''), conversationSteps, lang, rules: defaultTo(null), explainedVariable, // We need to access the `rules` in the simulation reducer simulation: (a, b) => simulation(a, b, state.rules), previousSimulation: defaultTo(null), currentExample, situationBranch, activeTargetInput, inFranceApp: inFranceAppReducer })(state, action) )