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 }) => (
) 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 ?? '')}
    ) }