import { updateSituation } from 'Actions/actions' import { T } from 'Components' import InputSuggestions from 'Components/conversation/InputSuggestions' import PercentageField from 'Components/PercentageField' import PeriodSwitch from 'Components/PeriodSwitch' import RuleLink from 'Components/RuleLink' import { ThemeColoursContext } from 'Components/utils/withColours' import withSitePaths from 'Components/utils/withSitePaths' import { encodeRuleName } from 'Engine/rules' import { serialiseUnit } from 'Engine/units' import { isEmpty, isNil } from 'ramda' import React, { useEffect, useState, useContext } from 'react' import emoji from 'react-easy-emoji' import { useTranslation } from 'react-i18next' import { useDispatch, useSelector } from 'react-redux' import { Link } from 'react-router-dom' import { analysisWithDefaultsSelector, useSituation, useSituationValue, 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() { const [initialRender, setInitialRender] = useState(true) const analysis = useSelector(analysisWithDefaultsSelector) const objectifs = useSelector( state => state.simulation?.config.objectifs || [] ) const secondaryObjectives = useSelector( state => state.simulation?.config['objectifs secondaires'] || [] ) const situation = useSituation() const dispatch = useDispatch() const colours = useContext(ThemeColoursContext) 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).map( ({ icône, objectifs: groupTargets, nom }, index) => (
{nom && (

{emoji(icône)} {nom}

)}
{index === 0 && }
groupTargets.includes(dottedName) ), initialRender }} />
) )}
) } let Targets = ({ targets, initialRender }) => (
) const Target = ({ target, initialRender }) => { const activeInput = useSelector(state => state.activeTargetInput) const dispatch = useDispatch() const isActiveInput = activeInput === target.dottedName const isSmallTarget = !target.question || !target.formule || isEmpty(target.formule) return (
  • {isSmallTarget && ( )}
    {isActiveInput && ( { dispatch(updateSituation(target.dottedName, '' + value)) }} rulePeriod={target.période} colouredBackground={true} /> )}
  • ) } let Header = withSitePaths(({ target, sitePaths }) => { const ruleLink = sitePaths.documentation.index + '/' + encodeRuleName(target.dottedName) return ( {target.title || target.name}

    {target.summary}

    ) }) export const formatCurrency = (value, language) => { return value == null ? '' : Intl.NumberFormat(language, { style: 'currency', currency: 'EUR', maximumFractionDigits: 0, minimumFractionDigits: 0 }) .format(value) .replace(/^€/, '€ ') } let clickableField = Input => function WrappedClickableField({ value, ...otherProps }) { const colors = useContext(ThemeColoursContext) const { language } = useTranslation().i18n return ( <> {formatCurrency(value, language)} ) } let unitToComponent = { '€': clickableField(CurrencyInput), '%': clickableField(PercentageField) } let TargetInputOrValue = ({ target, isActiveInput, isSmallTarget }) => { const { i18n } = useTranslation() const dispatch = useDispatch() const situationValue = useSituationValue(target.dottedName) const targetWithValue = useTarget(target.dottedName) const value = targetWithValue?.nodeValue?.toFixed(0) const inversionFail = useSelector( state => analysisWithDefaultsSelector(state)?.cache.inversionFail ) const blurValue = inversionFail && !isActiveInput && value const Component = unitToComponent[serialiseUnit(target.unit)] return ( {target.question ? ( dispatch(updateSituation(target.dottedName, evt.target.value)) } onBlur={event => event.preventDefault()} // We use onMouseDown instead of onClick because that's when the browser moves the cursor onMouseDown={() => { if (isSmallTarget) return dispatch({ type: 'SET_ACTIVE_TARGET_INPUT', name: target.dottedName }) // TODO: This shouldn't be necessary: we don't need to recalculate the situation // when the user just focus another field. Removing this line is almost working // however there is a weird bug in the selection of the next question. if (value) { dispatch(updateSituation(target.dottedName, '' + value)) } }} {...(isActiveInput ? { autoFocus: true } : {})} language={i18n.language} /> ) : ( {Number.isNaN(value) ? '—' : formatCurrency(value, i18n.language)} )} {target.dottedName.includes('rémunération . total') && } ) } function AidesGlimpse() { const aides = useTarget('contrat salarié . aides employeur') if (!aides?.nodeValue) return null return (
    -{' '} {formatCurrency(aides.nodeValue)} {' '} d'aides {emoji(aides.icons)}
    ) }