From 3b0cb008abe27627eae24c0f83a4736961632284 Mon Sep 17 00:00:00 2001 From: Maxime Quandalle Date: Wed, 29 Apr 2020 16:19:20 +0200 Subject: [PATCH] Ajout de types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Correction d'une vingtaine d'errors en mode strict. Correction d'une typo sur la prop du mécanisme Allègement --- source/components/BarChart.tsx | 41 ++++++++++--------- source/components/Documentation/Algorithm.tsx | 9 ++-- .../components/Documentation/RuleSource.tsx | 3 +- source/components/PaySlip.tsx | 13 +++--- source/components/SearchBar.tsx | 4 +- source/components/TargetSelection.tsx | 11 +++-- source/components/utils/markdown.tsx | 7 +++- source/components/utils/useNextQuestion.tsx | 3 +- source/engine/RuleInput.tsx | 14 ++++--- source/engine/evaluation.tsx | 3 +- source/engine/index.ts | 2 +- source/engine/mecanismViews/Allègement.js | 5 +-- source/engine/ruleUtils.ts | 2 +- source/engine/translateRules.ts | 29 +++++++++---- source/engine/types.ts | 12 ++++++ source/reducers/rootReducer.ts | 3 +- source/rules/index.ts | 4 ++ source/selectors/simulationSelectors.ts | 20 +++++---- .../mon-entreprise.fr/pages/Budget/Budget.tsx | 8 ++-- source/sites/publi.codes/Studio.tsx | 2 +- 20 files changed, 120 insertions(+), 75 deletions(-) diff --git a/source/components/BarChart.tsx b/source/components/BarChart.tsx index 1c70f7d57..0b0a095bc 100644 --- a/source/components/BarChart.tsx +++ b/source/components/BarChart.tsx @@ -8,17 +8,17 @@ import { useTranslation } from 'react-i18next' const ANIMATION_SPRING = config.gentle -let ChartItemBar = ({ styles, color, numberToPlot, unit }) => { +type ChartItemBarProps = { + numberToPlot: number + unit?: string + style: React.CSSProperties +} + +function ChartItemBar({ style, numberToPlot, unit }: ChartItemBarProps) { const language = useTranslation().i18n.language return (
- +
{
) } -let BranchIcône = ({ icône }) => ( -
- {emoji(icône)} -
-) + +function BranchIcon({ icon }: { icon: string }) { + return ( +
+ {emoji(icon)} +
+ ) +} type BarChartBranchProps = { value: number @@ -73,7 +76,7 @@ export default function BarChartBranch({ className="distribution-chart__item" style={{ opacity: styles.opacity }} > - {icon && } + {icon && }

{title} @@ -81,12 +84,12 @@ export default function BarChartBranch({ {description && {description}}

diff --git a/source/components/Documentation/Algorithm.tsx b/source/components/Documentation/Algorithm.tsx index b5cf89a41..e5bb0d7ab 100644 --- a/source/components/Documentation/Algorithm.tsx +++ b/source/components/Documentation/Algorithm.tsx @@ -3,13 +3,14 @@ import { any, identity, path } from 'ramda' import React from 'react' import { Trans } from 'react-i18next' import './Algorithm.css' +import { EvaluatedRule, ParsedRule } from 'Rules' let Conditions = ({ 'rendu non applicable': disabledBy, parentDependencies, 'applicable si': applicable, 'non applicable si': notApplicable -}) => { +}: EvaluatedRule) => { let listElements = [ ...parentDependencies.map( parentDependency => @@ -21,7 +22,7 @@ let Conditions = ({ ) ), ...disabledBy?.explanation?.isDisabledBy?.map( - (dependency, i) => + (dependency: EvaluatedRule, i: number) => dependency?.nodeValue === true && ( ) @@ -40,7 +41,7 @@ let Conditions = ({ ) : null } -function ShowIfDisabled({ dependency }) { +function ShowIfDisabled({ dependency }: { dependency: EvaluatedRule }) { return (
  • @@ -51,7 +52,7 @@ function ShowIfDisabled({ dependency }) { ) } -export default function Algorithm({ rule }) { +export default function Algorithm({ rule }: { rule: EvaluatedRule }) { let formula = rule.formule || (rule.category === 'variable' && rule.explanation.formule), diff --git a/source/components/Documentation/RuleSource.tsx b/source/components/Documentation/RuleSource.tsx index a5ef6da7f..7e0c41bfb 100644 --- a/source/components/Documentation/RuleSource.tsx +++ b/source/components/Documentation/RuleSource.tsx @@ -1,7 +1,6 @@ -import { ParsedRule } from 'Engine/types' import yaml from 'yaml' import React from 'react' -import rules from 'Rules' +import rules, { ParsedRule } from 'Rules' import PublicodeHighlighter from '../ui/PublicodeHighlighter' type RuleSourceProps = Pick diff --git a/source/components/PaySlip.tsx b/source/components/PaySlip.tsx index 4cc256df2..25effc55e 100644 --- a/source/components/PaySlip.tsx +++ b/source/components/PaySlip.tsx @@ -7,6 +7,7 @@ import { DottedName } from 'Rules' import './PaySlip.css' import { Line, SalaireBrutSection, SalaireNetSection } from './PaySlipSections' import RuleLink from './RuleLink' +import { ParsedRules, ParsedRule } from 'Rules' export const SECTION_ORDER = [ 'protection sociale . santé', @@ -21,9 +22,9 @@ export const SECTION_ORDER = [ type Section = typeof SECTION_ORDER[number] -function getSection(rule): Section { +function getSection(rule: ParsedRule): Section { const section = ('protection sociale . ' + - (rule.cotisation?.branche ?? rule.taxe?.branche)) as Section + rule.cotisation?.branche) as Section if (SECTION_ORDER.includes(section)) { return section } @@ -31,7 +32,7 @@ function getSection(rule): Section { } export function getCotisationsBySection( - parsedRules + parsedRules: ParsedRules ): Array<[Section, DottedName[]]> { const cotisations = [ ...parsedRules['contrat salarié . cotisations . patronales'].formule @@ -41,7 +42,7 @@ export function getCotisationsBySection( ] .map(cotisation => cotisation.dottedName) .filter(Boolean) - .reduce((acc, cotisation) => { + .reduce((acc, cotisation: DottedName) => { const sectionName = getSection(parsedRules[cotisation]) return { ...acc, @@ -154,12 +155,12 @@ function Cotisation({ dottedName }: { dottedName: DottedName }) { const partSalariale = useEvaluation( 'contrat salarié . cotisations . salariales' )?.formule.explanation.explanation.find( - cotisation => cotisation.dottedName === dottedName + (cotisation: ParsedRule) => cotisation.dottedName === dottedName ) const partPatronale = useEvaluation( 'contrat salarié . cotisations . patronales' )?.formule.explanation.explanation.find( - cotisation => cotisation.dottedName === dottedName + (cotisation: ParsedRule) => cotisation.dottedName === dottedName ) if (!partPatronale?.nodeValue && !partSalariale?.nodeValue) { return null diff --git a/source/components/SearchBar.tsx b/source/components/SearchBar.tsx index eef6d5ec7..9f3fbdf48 100644 --- a/source/components/SearchBar.tsx +++ b/source/components/SearchBar.tsx @@ -36,7 +36,7 @@ export default function SearchBar({ const { i18n } = useTranslation() const history = useHistory() const useDefaultValues = useContext(UseDefaultValuesContext) - const handleKeyDown = e => { + const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && results.length > 0) { finallyCallback && finallyCallback() history.push({ @@ -257,7 +257,7 @@ export default function SearchBar({ `} value={input} placeholder={i18n.t('Entrez des mots clefs ici')} - onKeyDown={e => handleKeyDown(e)} + onKeyDown={handleKeyDown} onChange={e => { let input = e.target.value setInput(input) diff --git a/source/components/TargetSelection.tsx b/source/components/TargetSelection.tsx index 214af06d5..b270f652a 100644 --- a/source/components/TargetSelection.tsx +++ b/source/components/TargetSelection.tsx @@ -10,15 +10,15 @@ import { } from 'Components/utils/EngineContext' import { SitePathsContext } from 'Components/utils/SitePathsContext' import { formatCurrency, formatValue } from 'Engine/format' -import { EvaluatedRule } from 'Engine/types' +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 } from 'Reducers/rootReducer' -import { DottedName } from 'Rules' +import { RootState, SimulationConfig } from 'Reducers/rootReducer' +import { DottedName, ParsedRule } from 'Rules' import { situationSelector, targetUnitSelector @@ -29,7 +29,6 @@ import CurrencyInput from './CurrencyInput/CurrencyInput' import './TargetSelection.css' export default function TargetSelection({ showPeriodSwitch = true }) { - const [initialRender, setInitialRender] = useState(true) const objectifs = useSelector( (state: RootState) => state.simulation?.config.objectifs || [] ) @@ -40,7 +39,7 @@ export default function TargetSelection({ showPeriodSwitch = true }) { {((typeof objectifs[0] === 'string' ? [{ objectifs }] : objectifs) as any).map( - ({ icône, objectifs: targets, nom }, index) => ( + ({ icône, objectifs: targets, nom }, index: number) => (
    @@ -151,7 +150,7 @@ const Target = ({ dottedName }: TargetProps) => { ) } -let Header = ({ target }) => { +let Header = ({ target }: { target: ParsedRule }) => { const sitePaths = useContext(SitePathsContext) const { t } = useTranslation() const { pathname } = useLocation() diff --git a/source/components/utils/markdown.tsx b/source/components/utils/markdown.tsx index 3fe2a51e4..13671b9ce 100644 --- a/source/components/utils/markdown.tsx +++ b/source/components/utils/markdown.tsx @@ -154,7 +154,12 @@ function HeadingWithAnchorLink({ ) } -function Heading({ level, children, ...otherProps }) { +type HeadingProps = { + level: number + children: React.ReactNode +} & React.ComponentProps<'h1'> + +function Heading({ level, children, ...otherProps }: HeadingProps) { return React.createElement(`h${level}`, otherProps, children) } diff --git a/source/components/utils/useNextQuestion.tsx b/source/components/utils/useNextQuestion.tsx index cb2fc7f8d..ce0c18e34 100644 --- a/source/components/utils/useNextQuestion.tsx +++ b/source/components/utils/useNextQuestion.tsx @@ -22,7 +22,6 @@ import { sortWith, takeWhile, toPairs, - values, zipWith } from 'ramda' import { useSelector } from 'react-redux' @@ -71,7 +70,7 @@ const similarity = (rule1: string = '', rule2: string = '') => export function getNextQuestions( missingVariables: MissingVariables, questionConfig: SimulationConfig['questions'] = {}, - answeredQuestions = [], + answeredQuestions: Array = [], situation: Simulation['situation'] = {} ): Array { const { diff --git a/source/engine/RuleInput.tsx b/source/engine/RuleInput.tsx index a0e34d68b..688c6aec0 100644 --- a/source/engine/RuleInput.tsx +++ b/source/engine/RuleInput.tsx @@ -7,7 +7,7 @@ import CurrencyInput from 'Components/CurrencyInput/CurrencyInput' import PercentageField from 'Components/PercentageField' import ToggleSwitch from 'Components/ui/ToggleSwitch' import { EngineContext } from 'Components/utils/EngineContext' -import { ParsedRules } from 'Engine/types' +import { ParsedRules, ParsedRule } from 'Engine/types' import React, { useContext } from 'react' import { useTranslation } from 'react-i18next' import { DottedName } from 'Rules' @@ -130,10 +130,14 @@ export default function RuleInput({ return } -let getVariant = rule => rule?.formule?.explanation['possibilités'] +let getVariant = (rule: ParsedRule) => + rule?.formule?.explanation['possibilités'] -export let buildVariantTree = (allRules, path) => { - let rec = path => { +export let buildVariantTree = ( + allRules: ParsedRules, + path: Name +) => { + let rec = (path: Name) => { let node = allRules[path] if (!node) throw new Error(`La règle ${path} est introuvable`) let variant = getVariant(node) @@ -144,7 +148,7 @@ export let buildVariantTree = (allRules, path) => { !!variant ? { canGiveUp, - children: variants.map(v => rec(path + ' . ' + v)) + children: variants.map((v: string) => rec(`${path} . ${v}` as Name)) } : null ) diff --git a/source/engine/evaluation.tsx b/source/engine/evaluation.tsx index 6d9999291..3a0e5d83e 100644 --- a/source/engine/evaluation.tsx +++ b/source/engine/evaluation.tsx @@ -119,7 +119,7 @@ export const evaluateArrayWithFilter = (evaluationFilter, reducer, start) => ( }) } -export let defaultNode = nodeValue => ({ +export let defaultNode = (nodeValue: EvaluatedNode['nodeValue']) => ({ nodeValue, // eslint-disable-next-line jsx: ({ nodeValue }: EvaluatedNode) => ( @@ -211,7 +211,6 @@ type DefaultValues = { [name in Names]: any } | {} export function collectDefaults( parsedRules: ParsedRules ): DefaultValues { - const cache = { _meta: { contextRule: [] as string[] } } return (Object.values(parsedRules) as Array>).reduce( (acc, parsedRule) => { if (parsedRule?.['par défaut'] == null) { diff --git a/source/engine/index.ts b/source/engine/index.ts index 35e4e54a9..d5dddf381 100644 --- a/source/engine/index.ts +++ b/source/engine/index.ts @@ -84,7 +84,7 @@ export default class Engine { ) if (Object.keys(result.defaultValue?.missingVariable ?? {}).length) { - throw new evaluationError( + throw evaluationError( context, "Impossible d'évaluer l'expression car celle ci fait appel à des variables manquantes" ) diff --git a/source/engine/mecanismViews/Allègement.js b/source/engine/mecanismViews/Allègement.js index 902fa8062..010de66b6 100644 --- a/source/engine/mecanismViews/Allègement.js +++ b/source/engine/mecanismViews/Allègement.js @@ -3,10 +3,7 @@ import React from 'react' import { makeJsx } from '../evaluation' import { Node } from './common' -export default function Allègement({ - nodeValue, - explanations: rawExplanation -}) { +export default function Allègement({ nodeValue, explanation: rawExplanation }) { // Don't display attributes with default values let explanation = map(k => (k && !k.isDefault ? k : null), rawExplanation) return ( diff --git a/source/engine/ruleUtils.ts b/source/engine/ruleUtils.ts index 5d7d19b58..e22bf9116 100644 --- a/source/engine/ruleUtils.ts +++ b/source/engine/ruleUtils.ts @@ -2,7 +2,7 @@ import { dropLast, last, pipe, propEq, range, take } from 'ramda' import { coerceArray } from '../utils' import { EvaluatedRule, Rule, Rules } from './types' -export const splitName = str => str.split(' . ') +export const splitName = (str: string) => str.split(' . ') export const joinName = strs => strs.join(' . ') export const parentName = pipe( splitName, diff --git a/source/engine/translateRules.ts b/source/engine/translateRules.ts index 88dfad0bf..6a4819aa4 100644 --- a/source/engine/translateRules.ts +++ b/source/engine/translateRules.ts @@ -1,11 +1,19 @@ import { assoc, mapObjIndexed } from 'ramda' import { Rule, Rules } from './types' +type Translation = Record +type translateAttribute = ( + prop: string, + rule: Rule, + translation: Translation, + lang: string +) => Rule + /* Traduction */ -const translateContrôle = (prop, rule, translation, lang) => +const translateContrôle: translateAttribute = (prop, rule, translation, lang) => assoc( 'contrôles', - rule.contrôles.map((control, i) => ({ + rule.contrôles!.map((control, i) => ({ ...control, message: translation[`${prop}.${i}.${lang}`]?.replace( /^\[automatic\] /, @@ -15,10 +23,15 @@ const translateContrôle = (prop, rule, translation, lang) => rule ) -const translateSuggestion = (prop, rule, translation, lang) => +const translateSuggestion: translateAttribute = ( + prop, + rule, + translation, + lang +) => assoc( 'suggestions', - Object.entries(rule.suggestions).reduce( + Object.entries(rule.suggestions!).reduce( (acc, [name, value]) => ({ ...acc, [translation[`${prop}.${name}.${lang}`]?.replace( @@ -41,9 +54,9 @@ export const attributesToTranslate = [ 'note' ] -const translateProp = (lang: string, translation: Object) => ( +const translateProp = (lang: string, translation: Translation) => ( rule: Rule, - prop + prop: string ) => { if (prop === 'contrôles' && rule?.contrôles) { return translateContrôle(prop, rule, translation, lang) @@ -58,7 +71,7 @@ const translateProp = (lang: string, translation: Object) => ( function translateRule( lang: string, - translations: { [Name in Names]: Object }, + translations: { [Name in Names]: Translation }, name: Names, rule: Rule ): Rule { @@ -74,7 +87,7 @@ function translateRule( export default function translateRules( lang: string, - translations: { [Name in Names]: Object }, + translations: { [Name in Names]: Translation }, rules: Rules ): Rules { const translatedRules = mapObjIndexed( diff --git a/source/engine/types.ts b/source/engine/types.ts index 70c998b8b..53f722a6e 100644 --- a/source/engine/types.ts +++ b/source/engine/types.ts @@ -48,6 +48,15 @@ export type ParsedRule = Rule & { category?: string rulePropType?: string jsx?: Function + cotisation?: Partial<{ + 'dû par': string + branche: string + destinataire: string + responsable: string + }> + taxe?: { + 'dû par': string + } } export type ParsedRules = { @@ -87,4 +96,7 @@ export type EvaluatedRule< EvaluatedNode & { isApplicable: boolean explanation: Explanation + 'rendu non applicable': EvaluatedRule + 'applicable si': EvaluatedNode + 'non applicable si': EvaluatedNode } diff --git a/source/reducers/rootReducer.ts b/source/reducers/rootReducer.ts index d6be5eb41..f77612799 100644 --- a/source/reducers/rootReducer.ts +++ b/source/reducers/rootReducer.ts @@ -66,6 +66,7 @@ export type SimulationConfig = { objectifs: | Array | Array<{ icône: string; nom: string; objectifs: Array }> + 'objectifs cachés': Array situation: Simulation['situation'] bloquant?: Array questions?: Partial>> @@ -140,7 +141,7 @@ function simulation( case 'UPDATE_SITUATION': const targets = without( ['entreprise . charges'], - objectifsSelector({ simulation: state }) + objectifsSelector({ simulation: state } as RootState) ) const situation = state.situation const { fieldName: dottedName, value } = action diff --git a/source/rules/index.ts b/source/rules/index.ts index 983de7265..f1e19918c 100644 --- a/source/rules/index.ts +++ b/source/rules/index.ts @@ -3,6 +3,8 @@ // future. import { EvaluatedRule as GenericEvaluatedRule, + ParsedRule as GenericParsedRule, + ParsedRules as GenericParsedRules, Rules as GenericRules } from 'Engine/types' import artisteAuteur from './artiste-auteur.yaml' @@ -25,6 +27,8 @@ import situationPersonnelle from './situation-personnelle.yaml' export type DottedName = keyof typeof jsonRules export type Rules = GenericRules +export type ParsedRules = GenericParsedRules +export type ParsedRule = GenericParsedRule export type EvaluatedRule = GenericEvaluatedRule export type Situation = Partial> diff --git a/source/selectors/simulationSelectors.ts b/source/selectors/simulationSelectors.ts index 127544573..fcd126701 100644 --- a/source/selectors/simulationSelectors.ts +++ b/source/selectors/simulationSelectors.ts @@ -1,9 +1,12 @@ import { DottedName } from './../rules/index' import { createSelector } from 'reselect' +import { RootState, SimulationConfig } from 'Reducers/rootReducer' + +export const configSelector = (state: RootState): Partial => + state.simulation?.config ?? {} -export const configSelector = state => state.simulation?.config ?? {} export const objectifsSelector = createSelector([configSelector], config => { - const primaryObjectifs = ((config.objectifs ?? []) as any) + const primaryObjectifs = (config.objectifs ?? ([] as any)) .map((obj: DottedName | { objectifs: Array }) => typeof obj === 'string' ? [obj] : obj.objectifs ) @@ -12,10 +15,13 @@ export const objectifsSelector = createSelector([configSelector], config => { const objectifs = [...primaryObjectifs, ...(config['objectifs cachés'] ?? [])] return objectifs }) + const emptySituation = {} -export const situationSelector = state => + +export const situationSelector = (state: RootState) => state.simulation?.situation ?? emptySituation -export const configSituationSelector = state => + +export const configSituationSelector = (state: RootState) => configSelector(state).situation ?? emptySituation export const firstStepCompletedSelector = createSelector( @@ -30,11 +36,11 @@ export const firstStepCompletedSelector = createSelector( } ) -export const targetUnitSelector = state => +export const targetUnitSelector = (state: RootState) => state.simulation?.targetUnit ?? '€/mois' -export const currentQuestionSelector = state => +export const currentQuestionSelector = (state: RootState) => state.simulation?.unfoldedStep ?? null -export const answeredQuestionsSelector = state => +export const answeredQuestionsSelector = (state: RootState) => state.simulation?.foldedSteps ?? [] diff --git a/source/sites/mon-entreprise.fr/pages/Budget/Budget.tsx b/source/sites/mon-entreprise.fr/pages/Budget/Budget.tsx index 8cf10e970..4aae6ecd7 100644 --- a/source/sites/mon-entreprise.fr/pages/Budget/Budget.tsx +++ b/source/sites/mon-entreprise.fr/pages/Budget/Budget.tsx @@ -25,9 +25,9 @@ const ressources = { } export default function Budget() { - const [selectedYear, setSelectedYear] = useState('2020') - const years = ['2019', '2020'] + const years = ['2019', '2020'] as const const quarters = ['T1', 'T2', 'T3', 'T4'] + const [selectedYear, setSelectedYear] = useState('2020') const categories = uniq( quarters .map(q => Object.keys(budget[2020][q] ?? {})) @@ -44,7 +44,9 @@ export default function Budget() { {emoji('📅')} Année{' '}