diff --git a/publicode/rules.js b/publicode/rules.js index d40c87050..e6b913330 100644 --- a/publicode/rules.js +++ b/publicode/rules.js @@ -18,14 +18,14 @@ const rules = { // TODO: rule order shouldn't matter but there is a bug if "impot" is after // "dirigeant". ...impot, + ...déclarationIndépendant, ...artisteAuteur, ...dirigeant, ...entrepriseEtablissement, ...protectionSociale, ...salarié, ...conventionsCollectives, - ...situationPersonnelle, - ...déclarationIndépendant + ...situationPersonnelle } export default rules diff --git a/publicode/rules/déclaration-revenu-indépendant.yaml b/publicode/rules/déclaration-revenu-indépendant.yaml index dba6bea1d..4c957a1a8 100644 --- a/publicode/rules/déclaration-revenu-indépendant.yaml +++ b/publicode/rules/déclaration-revenu-indépendant.yaml @@ -3,12 +3,17 @@ aide déclaration revenu indépendant 2019: description: Ces règles sont écrites pour aider à remplir les déclarations sociale et fiscale des indépendant de 2020 sur les revenus 2019 + par défaut: non + +aide déclaration revenu indépendant 2019 . professions libérale: remplace: - - règle: dirigeant - par: "'indépendant'" - règle: entreprise . catégorie d'activité . libérale règlementée par: non - formule: non + contrôles: + - si: entreprise . date de création < 01/01/2019 + avertissement: >- + Cette aide à la déclaration ne prends pas en compte les professions + libérales affiliées à la CIPAV. aide déclaration revenu indépendant 2019 . plafond sécurité sociale 2019: remplace: plafond sécurité sociale temps plein diff --git a/source/components/utils/useDisplayOnIntersecting.ts b/source/components/utils/useDisplayOnIntersecting.ts index 4709a594a..49d19c2a5 100644 --- a/source/components/utils/useDisplayOnIntersecting.ts +++ b/source/components/utils/useDisplayOnIntersecting.ts @@ -3,8 +3,12 @@ import { useEffect, useRef, useState } from 'react' export default function({ root = null, rootMargin, - threshold = 0 -}: IntersectionObserverInit): [React.RefObject, boolean] { + threshold = 0, + unobserve = true +}: IntersectionObserverInit & { unobserve?: boolean }): [ + React.RefObject, + boolean +] { const ref = useRef(null) const [wasOnScreen, setWasOnScreen] = useState(false) @@ -13,7 +17,10 @@ export default function({ ([entry]) => { if (entry.isIntersecting) { setWasOnScreen(entry.isIntersecting) - ref.current && observer.unobserve(ref.current) + ref.current && unobserve && observer.unobserve(ref.current) + } + if (!entry.isIntersecting && !unobserve) { + setWasOnScreen(entry.isIntersecting) } }, { @@ -27,9 +34,9 @@ export default function({ observer.observe(node) } return () => { - node && observer.unobserve(node) + node && unobserve && observer.unobserve(node) } - }, [root, rootMargin, threshold]) + }, [root, rootMargin, threshold, ref.current]) return [ref, wasOnScreen] } diff --git a/source/engine/RuleInput.tsx b/source/engine/RuleInput.tsx index 566cde769..58944bd2a 100644 --- a/source/engine/RuleInput.tsx +++ b/source/engine/RuleInput.tsx @@ -24,6 +24,7 @@ type Props = { onChange: (value: Value) => void useSwitch?: boolean isTarget?: boolean + autoFocus?: boolean value?: Value className?: string onSubmit?: (value: Value) => void @@ -40,6 +41,7 @@ export default function InputComponent({ value, useSwitch = false, isTarget = false, + autoFocus = false, className, onSubmit }: Props) { @@ -53,6 +55,7 @@ export default function InputComponent({ value, onChange, onSubmit, + autoFocus, className, title: rule.title, question: rule.question, diff --git a/source/sites/mon-entreprise.fr/pages/Simulateurs/dnrti.tsx b/source/sites/mon-entreprise.fr/pages/Simulateurs/dnrti.tsx index 22410d49a..1da86a312 100644 --- a/source/sites/mon-entreprise.fr/pages/Simulateurs/dnrti.tsx +++ b/source/sites/mon-entreprise.fr/pages/Simulateurs/dnrti.tsx @@ -1,9 +1,11 @@ import { setSimulationConfig, updateSituation } from 'Actions/actions' import RuleLink from 'Components/RuleLink' import 'Components/TargetSelection.css' +import useDisplayOnIntersecting from 'Components/utils/useDisplayOnIntersecting' import { formatValue } from 'Engine/format' import InputComponent from 'Engine/RuleInput' -import React, { useEffect, useState } from 'react' +import React, { useCallback, useEffect, useState } from 'react' +import Skeleton from 'react-loading-skeleton' import { useDispatch, useSelector } from 'react-redux' import { RootState } from 'Reducers/rootReducer' import { @@ -28,19 +30,52 @@ const simulationConfig = { 'aide déclaration revenu indépendant 2019 . assiette sociale' ], situation: { + dirigeant: 'indépendant', 'aide déclaration revenu indépendant 2019': 'oui' }, 'unités par défaut': ['€/an'] } +const lauchComputationWhenResultsInViewport = () => { + const [resultsRef, resultsInViewPort] = useDisplayOnIntersecting({ + threshold: 0, + unobserve: false + }) + const [currentIncome, setCurrentIncome] = useState(null) + const [displayForm, setDisplayForm] = useState(false) + const updateIncome = useCallback( + income => { + setDisplayForm(income != null) + setCurrentIncome(income) + }, + [setDisplayForm, setCurrentIncome] + ) + const dispatch = useDispatch() + useEffect(() => { + if (resultsInViewPort && displayForm) { + dispatch( + updateSituation('dirigeant . rémunération totale', currentIncome) + ) + } else { + dispatch(updateSituation('dirigeant . rémunération totale', null)) + } + }, [resultsInViewPort, displayForm, currentIncome]) + + return { updateIncome, resultsRef, displayForm } +} export default function DNRTI() { const dispatch = useDispatch() const analysis = useSelector(analysisWithDefaultsSelector) + const rules = useSelector(flatRulesSelector) const company = useSelector( (state: RootState) => state.inFranceApp.existingCompany ) dispatch(setSimulationConfig(simulationConfig, true)) - + const { + resultsRef, + displayForm, + updateIncome + } = lauchComputationWhenResultsInViewport() return ( <>

@@ -65,48 +100,63 @@ export default function DNRTI() { d'engagement - - - -

Revenus d'activité

- Quel est votre revenu professionnel en 2019 ?

+

+ Indiquez votre résultat net fiscal avant déduction des charges sociales + et exonérations fiscales. +

+ + - - - - {/* PLNR */} - - - - -

Situation personnelle

- - - - -

Exonérations

- - - - -

International

- - - {/*

DOM - Départements d'Outre-Mer

+
+ {displayForm && ( + <> + +

Votre entreprise

- Pas encore implémenté -

*/} - - + Vous pouvez renseigner votre entreprise pour pré-remplir le + formulaire +

+ + + + + + {/* PLNR */} + + + + +

Situation personnelle

+ + + + +

Exonérations

+ + + + +

International

+ + +
+
+
+ +
+ + )} ) } @@ -161,13 +211,19 @@ function SimpleField({ dottedName, question, summary }: SimpleFieldProps) { const rules = useSelector((state: RootState) => state.rules) const value = useSelector(situationSelector)[dottedName] const [currentValue, setCurrentValue] = useState(value) + const dispatchValue = useCallback( + value => { + dispatch(updateSituation(dottedName, value)) + dispatch({ + type: 'STEP_ACTION', + name: 'fold', + step: dottedName + }) + }, + [dispatch, dottedName] + ) const update = (value: unknown) => { - dispatch(updateSituation(dottedName, value)) - dispatch({ - type: 'STEP_ACTION', - name: 'fold', - step: dottedName - }) + dispatchValue(value) setCurrentValue(value) } useEffect(() => { @@ -203,12 +259,13 @@ function SimpleField({ dottedName, question, summary }: SimpleFieldProps) { } function Results() { - const results = simulationConfig.objectifs - .map(objectif => useRule(objectif)) - .filter(r => r.nodeValue) - if (!results.length) { - return null - } + const results = simulationConfig.objectifs.map(dottedName => + useSelector((state: RootState) => { + return ruleAnalysisSelector(state, { dottedName }) + }) + ) + const onGoingComputation = !results.filter(node => node.nodeValue != null) + .length return (
Aide à la déclaration 📄 - - {results.map(r => ( - <> -

- {r.title} {r.summary} -

- {r.description &&

{r.description}

} -

- - {r.nodeValue - ? formatValue({ + {onGoingComputation && ( +

+ Calcul en cours... +

+ )} + <> + + {results.map(r => ( + +

+ {r.title} {r.summary} +

+ {r.description &&

{r.description}

} +

+ + {r.nodeValue ? ( + formatValue({ value: r.nodeValue, language: 'fr', unit: '€', maximumFractionDigits: 0 - }) - : '-'} - -

- - ))} -
-
- - -
+ }) + ) : ( + + )} + +

+ + ))} +
+
+ + +
+
) } @@ -268,3 +334,6 @@ const FormBlock = styled.section` const Question = styled.div` margin-top: 1em; ` +const BigInput = styled.div` + font-size: 130%; +`