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 { EngineContext, useEvaluation, useInversionFail } from 'Components/utils/EngineContext' import { SitePathsContext } from 'Components/utils/SitePathsContext' import { formatCurrency, formatValue } from 'Engine/format' import { EvaluatedRule, EvaluatedNode } from 'Engine/types' import { isNil } from 'ramda' import React, { useCallback, 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, SimulationConfig } from 'Reducers/rootReducer' import { DottedName, ParsedRule } from 'Rules' import { situationSelector, targetUnitSelector } from 'Selectors/simulationSelectors' 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 objectifs = useSelector( (state: RootState) => state.simulation?.config.objectifs || [] ) const colors = useContext(ThemeColorsContext) return (
{((typeof objectifs[0] === 'string' ? [{ objectifs }] : objectifs) as any).map( ({ icône, objectifs: targets, nom }, index: number) => (
{nom && (

{emoji(icône)} {nom}

)}
{index === 0 && showPeriodSwitch && }
    {' '} {targets.map(target => ( ))}
) )}
) } type TargetProps = { dottedName: DottedName } const Target = ({ dottedName }: TargetProps) => { const activeInput = useSelector((state: RootState) => state.activeTargetInput) const target = useEvaluation(dottedName, { unit: useSelector(targetUnitSelector) }) const dispatch = useDispatch() const onSuggestionClick = useCallback( value => { dispatch(updateSituation(target.dottedName, value)) }, [target.dottedName, dispatch] ) const isSmallTarget = !!target.question !== !!target.formule if ( target.nodeValue === false || (isSmallTarget && !target.question && !target.nodeValue) ) { return null } const isActiveInput = activeInput === target.dottedName return (
  • {isSmallTarget && ( )}
    {isActiveInput && (
    )}
  • ) } const Header = ({ target }: { target: ParsedRule }) => { 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: EvaluatedRule 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 targetUnit = useSelector(targetUnitSelector) const engine = useContext(EngineContext) const value = typeof situationValue === 'string' ? Math.round( engine.evaluate(situationValue, { unit: targetUnit }) .nodeValue as number ) : situationValue != null ? situationValue : target?.nodeValue != null ? Math.round(+target.nodeValue) : undefined const blurValue = useInversionFail() && !isActiveInput const onChange = useCallback( evt => dispatch( updateSituation(target.dottedName, +evt.target.value + ' ' + targetUnit) ), [targetUnit, target, dispatch] ) return ( {target.question ? ( <> {!isActiveInput && } { 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 targetUnit = useSelector(targetUnitSelector) const titresRestaurant = useEvaluation( 'contrat salarié . frais professionnels . titres-restaurant . montant', { unit: targetUnit } ) const { language } = useTranslation().i18n if (!titresRestaurant?.nodeValue) return null return (
    +{' '} {formatValue({ nodeValue: titresRestaurant.nodeValue, unit: '€', language })} {' '} en titres-restaurant {emoji(' 🍽')}
    ) } function AidesGlimpse() { const targetUnit = useSelector(targetUnitSelector) const aides = useEvaluation('contrat salarié . aides employeur', { unit: targetUnit }) 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 aidesDetail = aides?.formule.explanation.explanation const aidesNotNul = aidesDetail?.filter(node => node.nodeValue !== false) const aideLink = aidesNotNul?.length === 1 ? aidesNotNul[0] : aides if (!aides?.nodeValue) return null return (
    en incluant{' '} {formatValue({ nodeValue: aides.nodeValue, unit: '€', language })} {' '} d'aides {emoji(aides?.icons ?? '')}
    ) }