🐛 Ne pas afficher la section explication en cas d'erreur inversion

Si l'erreur était bien capturée par React, l'affichage n'était pas
rétabli lorsqu'une nouvelle saisie était effectuée. Lorsque le moteur
n'arrive pas à exécuter une simulation il ne sert à rien d'essayer
d'afficher des explications et/ou contrôles.

Convertit Components/Controls vers les React hooks et TypeScript
pull/881/head
Maxime Quandalle 2020-02-09 16:03:28 +01:00
parent b1ab640e31
commit 2efdbad74d
4 changed files with 101 additions and 128 deletions

View File

@ -1,49 +1,50 @@
import { goToQuestion, hideControl } from 'Actions/actions'
import { makeJsx } from 'Engine/evaluation'
import { compose } from 'ramda'
import React from 'react'
import emoji from 'react-easy-emoji'
import { useTranslation } from 'react-i18next'
import { connect } from 'react-redux'
import { useDispatch, useSelector } from 'react-redux'
import { RootState } from 'Reducers/rootReducer'
import { analysisWithDefaultsSelector } from 'Selectors/analyseSelectors'
import animate from 'Ui/animate'
import './Controls.css'
import { Markdown } from './utils/markdown'
import { ScrollToElement } from './utils/Scroll'
function Controls({
controls,
goToQuestion,
hideControl,
foldedSteps,
hiddenControls,
inversionFail
}) {
export default function Controls() {
const { t } = useTranslation()
const foldedSteps = useSelector(
(state: RootState) => state.simulation?.foldedSteps
)
const analysis = useSelector(analysisWithDefaultsSelector)
const controls = analysis?.controls
const inversionFail = analysis?.cache._meta.inversionFail
const hiddenControls = useSelector(
(state: RootState) => state.simulation?.hiddenControls
)
const dispatch = useDispatch()
if (!controls) {
return null
}
let messages = [
...controls,
...(inversionFail
? [
{
message: t([
'simulateurs.inversionFail',
'Le montant saisi est trop faible ou aboutit à une situation impossible, essayez en un autre'
]),
level: 'avertissement'
}
]
: [])
]
let messages = inversionFail
? [
{
message: t([
'simulateurs.inversionFail',
'Le montant saisi ne permet pas de calculer un résultat, nous vous invitons à essayer une autre valeur.'
]),
level: 'avertissement'
}
]
: controls
if (!messages?.length) return null
return (
<div id="controlsBlock">
<ul style={{ margin: 0, padding: 0 }}>
{messages.map(({ level, test, message, solution, evaluated }) =>
hiddenControls.includes(test) ? null : (
hiddenControls?.includes(test) ? null : (
<animate.fromTop key={message}>
<li key={test}>
<div className="control">
@ -55,12 +56,12 @@ function Controls({
<span id="controlExplanation">{makeJsx(evaluated)}</span>
)}
{solution && !foldedSteps.includes(solution.cible) && (
{solution && !foldedSteps?.includes(solution.cible) && (
<div>
<button
key={solution.cible}
className="ui__ link-button"
onClick={() => goToQuestion(solution.cible)}
onClick={() => dispatch(goToQuestion(solution.cible))}
>
{solution.texte}
</button>
@ -69,7 +70,7 @@ function Controls({
<button
className="hide"
aria-label="close"
onClick={() => hideControl(test)}
onClick={() => dispatch(hideControl(test))}
>
×
</button>
@ -84,19 +85,3 @@ function Controls({
</div>
)
}
export default compose(
connect(
(state, props) => ({
foldedSteps: state.simulation?.foldedSteps,
controls: analysisWithDefaultsSelector(state)?.controls,
inversionFail: analysisWithDefaultsSelector(state)?.cache._meta
.inversionFail,
key: props.language,
hiddenControls: state.simulation.hiddenControls
}),
{
goToQuestion,
hideControl
}
)
)(Controls)

View File

@ -8,86 +8,77 @@ import emoji from 'react-easy-emoji'
import { Trans, useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import { RootState } from 'Reducers/rootReducer'
import { analysisWithDefaultsSelector, defaultUnitsSelector } from 'Selectors/analyseSelectors'
import {
analysisWithDefaultsSelector,
defaultUnitsSelector
} from 'Selectors/analyseSelectors'
import * as Animate from 'Ui/animate'
class ErrorBoundary extends React.Component {
state = {} as { error?: string }
static getDerivedStateFromError() {
return {
error:
'The SalaryExplanation component triggered an error. This often happens in its subcomponents reducers'
}
}
render() {
if (this.state.error)
return <div css="background: red; ">Erreur : {this.state.error}</div>
return this.props.children
}
}
export default function SalaryExplanation() {
const showDistributionFirst = useSelector(
(state: RootState) => !state.simulation?.foldedSteps.length
)
const analysis = useSelector(analysisWithDefaultsSelector)
const inversionFail = analysis?.cache._meta.inversionFail
const distributionRef = useRef<HTMLDivElement>(null)
// We can't provide an explanation if the engine has failed to run the
// simulation.
if (inversionFail) {
return null
}
return (
<ErrorBoundary>
<Animate.fromTop key={showDistributionFirst.toString()}>
{showDistributionFirst ? (
<>
<RevenueRepatitionSection />
<Animate.fromTop key={showDistributionFirst.toString()}>
{showDistributionFirst ? (
<>
<RevenueRepatitionSection />
<DistributionSection />
<PaySlipSection />
</>
) : (
<>
<RevenueRepatitionSection />
<div css="text-align: center">
<button
className="ui__ small simple button"
onClick={() =>
distributionRef.current?.scrollIntoView({
behavior: 'smooth',
block: 'start'
})
}
>
{emoji('📊')} <Trans>Voir la répartition des cotisations</Trans>
</button>
</div>
<PaySlipSection />
<div ref={distributionRef}>
<DistributionSection />
<PaySlipSection />
</>
) : (
<>
<RevenueRepatitionSection />
<div css="text-align: center">
<button
className="ui__ small simple button"
onClick={() =>
distributionRef.current?.scrollIntoView({
behavior: 'smooth',
block: 'start'
})
}
>
{emoji('📊')} <Trans>Voir la répartition des cotisations</Trans>
</button>
</div>
<PaySlipSection />
<div ref={distributionRef}>
<DistributionSection />
</div>
</>
)}
<br />
<p className="ui__ notice">
<Trans i18nKey="payslip.notice">
Le simulateur vous aide à comprendre votre bulletin de paie, sans
lui être opposable. Pour plus d&apos;informations, rendez vous
sur&nbsp;
<a href="https://www.service-public.fr/particuliers/vosdroits/F559">
service-public.fr
</a>
.
</Trans>
</p>
<p className="ui__ notice">
<Trans i18nKey="payslip.disclaimer">
Il ne prend pour l'instant pas en compte les accords et conventions
collectives, ni la myriade d'aides aux entreprises. Trouvez votre
convention collective{' '}
<a href="https://socialgouv.github.io/conventions-collectives">
ici
</a>
, et explorez les aides sur&nbsp;
<a href="https://www.aides-entreprises.fr">aides-entreprises.fr</a>.
</Trans>
</p>
</Animate.fromTop>
</ErrorBoundary>
</div>
</>
)}
<br />
<p className="ui__ notice">
<Trans i18nKey="payslip.notice">
Le simulateur vous aide à comprendre votre bulletin de paie, sans lui
être opposable. Pour plus d&apos;informations, rendez vous sur&nbsp;
<a href="https://www.service-public.fr/particuliers/vosdroits/F559">
service-public.fr
</a>
.
</Trans>
</p>
<p className="ui__ notice">
<Trans i18nKey="payslip.disclaimer">
Il ne prend pour l'instant pas en compte les accords et conventions
collectives, ni la myriade d'aides aux entreprises. Trouvez votre
convention collective{' '}
<a href="https://socialgouv.github.io/conventions-collectives">ici</a>
, et explorez les aides sur&nbsp;
<a href="https://www.aides-entreprises.fr">aides-entreprises.fr</a>.
</Trans>
</p>
</Animate.fromTop>
)
}
@ -133,8 +124,8 @@ function PaySlipSection() {
{unit?.endsWith('mois') ? (
<Trans>Fiche de paie</Trans>
) : (
<Trans>Détail annuel des cotisations</Trans>
)}
<Trans>Détail annuel des cotisations</Trans>
)}
</h2>
<PaySlip />
</section>

View File

@ -62,7 +62,7 @@ Modifier mes réponses: Change my answers
Mon entreprise: My company
Mon revenu: My income
Montant des cotisations: Amount of contributions
'Nom de l''entreprise ou SIREN ': Company name or SIREN code
"Nom de l'entreprise ou SIREN ": Company name or SIREN code
Non: 'No'
Nous n'avons rien trouvé: We didn't find any matching registered company.
Oui: 'Yes'
@ -1005,8 +1005,8 @@ simulateurs:
titre: Official income simulator for self-employed person
titre: Self-employed income simulator
inversionFail: >-
The amount entered is too small or results in an impossible situation, try
another one
The amount entered does not enable us to calculate a result, we invite you
to try another value.
précision:
bonne: Good accuracy
défaut: 'Refine the simulation by answering the following questions:'

View File

@ -34,8 +34,7 @@ import { mapOrApply } from '../utils'
// create a "selector creator" that uses deep equal instead of ===
const createDeepEqualSelector = createSelectorCreator(defaultMemoize, equals)
let configSelector = (state: RootState) =>
(state.simulation && state.simulation.config) || {}
let configSelector = (state: RootState) => state.simulation?.config || {}
// We must here compute parsedRules, flatRules, analyse which contains both targets and cache objects
export let flatRulesSelector = (state: RootState) => state.rules
@ -88,12 +87,10 @@ export let firstStepCompletedSelector = createSelector(
if (!situation) {
return true
}
const targetIsAnswered =
targetNames &&
targetNames.some(targetName => {
const rule = findRuleByDottedName(parsedRules, targetName)
return rule && rule.formule && targetName in situation
})
const targetIsAnswered = targetNames?.some(targetName => {
const rule = findRuleByDottedName(parsedRules, targetName)
return rule?.formule && targetName in situation
})
return targetIsAnswered
}
)