🐛 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 TypeScriptpull/881/head
parent
b1ab640e31
commit
2efdbad74d
|
@ -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)
|
|
@ -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'informations, rendez vous
|
||||
sur
|
||||
<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
|
||||
<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'informations, rendez vous sur
|
||||
<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
|
||||
<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>
|
||||
|
|
|
@ -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:'
|
||||
|
|
|
@ -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
|
||||
}
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue