import { setActiveTarget, updateSituation } from 'Actions/actions'
import InputSuggestions from 'Components/conversation/InputSuggestions'
import PeriodSwitch from 'Components/PeriodSwitch'
import RuleLink from 'Components/RuleLink'
import { ThemeColorsContext } from 'Components/utils/colors'
import { SitePathsContext } from 'Components/utils/withSitePaths'
import { formatCurrency } from 'Engine/format'
import { ParsedRule } from 'Engine/types'
import { isEmpty, isNil } from 'ramda'
import React, { useContext, useEffect, useState } from 'react'
import emoji from 'react-easy-emoji'
import { Trans, useTranslation } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
import { Link, useLocation } from 'react-router-dom'
import { RootState } from 'Reducers/rootReducer'
import { DottedName } from 'Rules'
import {
analysisWithDefaultsSelector,
situationSelector,
useTarget
} from 'Selectors/analyseSelectors'
import Animate from 'Ui/animate'
import AnimatedTargetValue from 'Ui/AnimatedTargetValue'
import CurrencyInput from './CurrencyInput/CurrencyInput'
import './TargetSelection.css'
export default function TargetSelection({ showPeriodSwitch = true }) {
const [initialRender, setInitialRender] = useState(true)
const analysis = useSelector(analysisWithDefaultsSelector)
const objectifs = useSelector(
(state: RootState) => state.simulation?.config.objectifs || []
)
const secondaryObjectives = useSelector(
(state: RootState) =>
state.simulation?.config['objectifs secondaires'] || []
)
const situation = useSelector(situationSelector)
const dispatch = useDispatch()
const colors = useContext(ThemeColorsContext)
const targets =
analysis?.targets.filter(
t =>
!secondaryObjectives.includes(t.dottedName) &&
t.dottedName !== 'contrat salarié . aides employeur'
) || []
useEffect(() => {
// Initialize defaultValue for target that can't be computed
// TODO: this logic shouldn't be here
targets
.filter(
target =>
(!target.formule || isEmpty(target.formule)) &&
(!isNil(target.defaultValue) ||
!isNil(target.explanation?.defaultValue)) &&
!situation[target.dottedName]
)
.forEach(target => {
dispatch(
updateSituation(
target.dottedName,
!isNil(target.defaultValue)
? target.defaultValue
: target.explanation?.defaultValue
)
)
})
setInitialRender(false)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return (
{((typeof objectifs[0] === 'string'
? [{ objectifs }]
: objectifs) as any).map(
({ icône, objectifs: groupTargets, nom }, index) => (
{nom && (
{emoji(icône)} {nom}
)}
{index === 0 && showPeriodSwitch &&
}
groupTargets.includes(dottedName)
),
initialRender
}}
/>
)
)}
)
}
let Targets = ({ targets, initialRender }) => (
{targets
.map(target => target.explanation || target)
.filter(target => {
return (
target.isApplicable !== false &&
(target.question || target.nodeValue)
)
})
.map(target => (
))}
)
const Target = ({ target, initialRender }) => {
const activeInput = useSelector((state: RootState) => state.activeTargetInput)
const dispatch = useDispatch()
const isActiveInput = activeInput === target.dottedName
const isSmallTarget = !!target.question !== !!target.formule
return (
{isSmallTarget && (
)}
{isActiveInput && (
{
dispatch(updateSituation(target.dottedName, value))
}}
unit={target.defaultUnit}
/>
)}
)
}
let Header = ({ target }) => {
const sitePaths = useContext(SitePathsContext)
const { t } = useTranslation()
const { pathname } = useLocation()
// TODO : Super hacky, we want to amend one label in the covid simulator, but
// because the label is fetched from the global state we have to do a hack
// here based on the URL.
const hackyShowPeriod = pathname === sitePaths.coronavirus
return (
{target.title || target.name}
{hackyShowPeriod && ' ' + t('mensuel')}
{target.summary}
)
}
type TargetInputOrValueProps = {
target: ParsedRule
isActiveInput: boolean
isSmallTarget: boolean
}
function TargetInputOrValue({
target,
isActiveInput,
isSmallTarget
}: TargetInputOrValueProps) {
const { language } = useTranslation().i18n
const colors = useContext(ThemeColorsContext)
const dispatch = useDispatch()
const situationValue = useSelector(situationSelector)[target.dottedName]
const targetWithValue = useTarget(target.dottedName)
const inversionFail = useSelector(analysisWithDefaultsSelector)?.cache._meta
.inversionFail
const value =
targetWithValue?.nodeValue != null && !inversionFail
? Math.round(targetWithValue.nodeValue)
: undefined
const blurValue = inversionFail && !isActiveInput
return (
{target.question ? (
<>
{!isActiveInput && }
dispatch(
updateSituation(target.dottedName, Number(evt.target.value))
)
}
onFocus={() => {
if (isSmallTarget) return
dispatch(setActiveTarget(target.dottedName))
}}
language={language}
/>
{formatCurrency(value, language)}
>
) : (
{value && Number.isNaN(value) ? '—' : formatCurrency(value, language)}
)}
{target.dottedName.includes('prix du travail') && }
{target.dottedName === 'contrat salarié . rémunération . net' && (
)}
)
}
function TitreRestaurant() {
const titresRestaurant = useTarget(
'contrat salarié . frais professionnels . titres-restaurant . montant'
)
const { language } = useTranslation().i18n
if (!titresRestaurant?.nodeValue) return null
return (
+{' '}
{formatCurrency(titresRestaurant.nodeValue, language)}
{' '}
en titres-restaurant {emoji(' 🍽')}
)
}
function AidesGlimpse() {
const aides = useTarget('contrat salarié . aides employeur')
const { language } = useTranslation().i18n
// Dans le cas où il n'y a qu'une seule aide à l'embauche qui s'applique, nous
// faisons un lien direct vers cette aide, plutôt qu'un lien vers la liste qui
// est une somme des aides qui sont toutes nulle sauf l'aide active.
const aidesNode = aides?.explanation
const aidesDetail = aides?.explanation.formule.explanation.explanation
const aidesNotNul = aidesDetail?.filter(node => node.nodeValue !== false)
const aideLink = aidesNotNul?.length === 1 ? aidesNotNul[0] : aidesNode
if (!aides?.nodeValue) return null
return (
en incluant {' '}
{formatCurrency(aides.nodeValue, language)}
{' '}
d'aides {emoji(aides.explanation?.icons ?? '')}
)
}