mon-entreprise/source/reducers/rootReducer.ts

227 lines
5.4 KiB
TypeScript

import { Action } from 'Actions/actions'
import { Unit } from 'Engine/units'
import { defaultTo, omit, without } from 'ramda'
import reduceReducers from 'reduce-reducers'
import { combineReducers, Reducer } from 'redux'
import originRules, { Rules } from 'Rules'
import { SavedSimulation } from 'Selectors/storageSelectors'
import i18n, { AvailableLangs } from '../i18n'
import { DottedName } from './../rules/index'
import { objectifsSelector } from '../selectors/simulationSelectors'
import inFranceAppReducer, { Company } from './inFranceAppReducer'
import storageRootReducer from './storageReducer'
function rules(state: Rules = originRules) {
return state
}
function explainedVariable(
state: DottedName | null = null,
action: Action
): DottedName | null {
switch (action.type) {
case 'EXPLAIN_VARIABLE':
return action.variableName
case 'STEP_ACTION':
return null
default:
return state
}
}
type Example = null | {
name: string
situation: object
dottedName: DottedName
defaultUnit?: Unit
}
function situationBranch(state: number | null = null, action: Action) {
switch (action.type) {
case 'SET_SITUATION_BRANCH':
return action.id
default:
return state
}
}
function activeTargetInput(state: DottedName | null = null, action: Action) {
switch (action.type) {
case 'SET_ACTIVE_TARGET_INPUT':
return action.name
case 'RESET_SIMULATION':
return null
default:
return state
}
}
function lang(
state = i18n.language as AvailableLangs,
{ type, lang }
): AvailableLangs {
switch (type) {
case 'SWITCH_LANG':
return lang
default:
return state
}
}
type QuestionsKind =
| "à l'affiche"
| 'non prioritaires'
| 'uniquement'
| 'liste noire'
export type SimulationConfig = {
objectifs:
| Array<DottedName>
| Array<{ icône: string; nom: string; objectifs: Array<DottedName> }>
situation: Simulation['situation']
bloquant?: Array<DottedName>
questions?: Partial<Record<QuestionsKind, Array<DottedName>>>
branches?: Array<{ nom: string; situation: SimulationConfig['situation'] }>
'unité par défaut': string
}
type Situation = Partial<Record<DottedName, any>>
export type Simulation = {
config: SimulationConfig
url: string
hiddenControls: Array<string>
situation: Situation
initialSituation: Situation
targetUnit: string
foldedSteps: Array<DottedName>
unfoldedStep?: DottedName | null
}
function getCompanySituation(company: Company): Situation {
return {
...(company?.localisation && {
'établissement . localisation': company.localisation
}),
...(company?.dateDeCréation && {
'entreprise . date de création': company.dateDeCréation.replace(
/(.*)-(.*)-(.*)/,
'$3/$2/$1'
)
})
}
}
function simulation(
state: Simulation | null = null,
action: Action,
existingCompany: Company
): Simulation | null {
if (action.type === 'SET_SIMULATION') {
const companySituation = action.useCompanyDetails
? getCompanySituation(existingCompany)
: {}
const { config, url } = action
if (state && state.config === config) {
return state
}
return {
config,
url,
hiddenControls: [],
situation: companySituation,
initialSituation: companySituation,
targetUnit: config['unité par défaut'] || '€/mois',
foldedSteps: Object.keys(companySituation) as Array<DottedName>,
unfoldedStep: null
}
}
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: state.initialSituation,
foldedSteps: [],
unfoldedStep: null
}
case 'UPDATE_SITUATION':
const targets = without(
['entreprise . charges'],
objectifsSelector({ simulation: state })
)
const situation = state.situation
const { fieldName: dottedName, value } = action
return {
...state,
situation: {
...(targets.includes(dottedName)
? omit(targets, situation)
: situation),
[dottedName]: value
}
}
case 'STEP_ACTION':
const { name, step } = action
if (name === 'fold')
return {
...state,
foldedSteps: [...state.foldedSteps, step],
unfoldedStep: null
}
if (name === 'unfold') {
return {
...state,
foldedSteps: without([step], state.foldedSteps),
unfoldedStep: step
}
}
return state
case 'UPDATE_TARGET_UNIT':
return {
...state,
targetUnit: action.targetUnit
}
}
return state
}
const existingCompanyReducer = (state, action: Action) => {
if (action.type.startsWith('EXISTING_COMPANY::') && state.simulation) {
return {
...state,
simulation: {
...state.simulation,
situation: {
...state.simulation.situation,
...getCompanySituation(state.inFranceApp.existingCompany)
}
}
}
}
return state
}
const mainReducer = (state, action: Action) =>
combineReducers({
lang,
explainedVariable,
// We need to access the `rules` in the simulation reducer
simulation: (a: Simulation | null = null, b: Action): Simulation | null =>
simulation(a, b, state?.inFranceApp?.existingCompany),
previousSimulation: defaultTo(null) as Reducer<SavedSimulation | null>,
situationBranch,
activeTargetInput,
inFranceApp: inFranceAppReducer
})(state, action)
export default reduceReducers<RootState>(
mainReducer as any,
existingCompanyReducer as any,
storageRootReducer as any
) as Reducer<RootState>
export type RootState = ReturnType<typeof mainReducer>