From 7b7dc15624762568fff4a44ffbb2a75cc71e661b Mon Sep 17 00:00:00 2001 From: Maxime Quandalle Date: Sun, 12 Apr 2020 23:30:58 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=85=20Ajoute=20des=20types=20TypeScript?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 3 +- source/Provider.tsx | 34 ++-- source/actions/actions.ts | 12 +- source/actions/companyStatusActions.ts | 53 +++++- source/api/sirene.ts | 12 +- source/components/Distribution.tsx | 17 +- source/components/RulePage.tsx | 2 +- source/components/conversation/DateInput.tsx | 20 ++- .../conversation/InputSuggestions.tsx | 12 +- source/components/conversation/Question.tsx | 24 +-- source/components/ui/Checkbox/index.tsx | 2 +- source/components/ui/Checklist/index.tsx | 71 ++++---- source/components/utils/Scroll.tsx | 4 +- source/engine/RuleInput.tsx | 8 +- source/rules/index.ts | 7 +- source/sites/mon-entreprise.fr/App.tsx | 2 +- .../mon-entreprise.fr/pages/Coronavirus.tsx | 17 +- .../pages/Créer/CreationChecklist.tsx | 37 ++-- .../Créer/GuideStatut/NumberOfAssociate.tsx | 13 +- .../Créer/GuideStatut/PreviousAnswers.tsx | 6 +- .../Créer/GuideStatut/SoleProprietorship.tsx | 14 +- .../AideDéclarationIndépendant/Result.tsx | 9 +- .../mon-entreprise.fr/pages/Gérer/Home.tsx | 2 +- .../ActivitésSelection.tsx | 7 +- .../ÉconomieCollaborative/NextButton.tsx | 7 +- .../ÉconomieCollaborative/VotreSituation.tsx | 2 +- .../pages/ÉconomieCollaborative/actions.ts | 12 +- source/sites/publi.codes/{App.js => App.tsx} | 5 +- .../publi.codes/{Header.js => Header.tsx} | 1 + .../publi.codes/{Landing.js => Landing.tsx} | 0 .../{LazyStudio.js => LazyStudio.tsx} | 0 source/sites/publi.codes/Studio.tsx | 7 +- .../sites/publi.codes/{entry.js => entry.tsx} | 0 source/types/app-env.d.ts | 2 +- source/types/import-markdown.d.ts | 4 + source/utils.ts | 10 +- source/webpack.common.js | 4 +- yarn.lock | 170 ++++++------------ 38 files changed, 326 insertions(+), 286 deletions(-) rename source/sites/publi.codes/{App.js => App.tsx} (91%) rename source/sites/publi.codes/{Header.js => Header.tsx} (99%) rename source/sites/publi.codes/{Landing.js => Landing.tsx} (100%) rename source/sites/publi.codes/{LazyStudio.js => LazyStudio.tsx} (100%) rename source/sites/publi.codes/{entry.js => entry.tsx} (100%) create mode 100644 source/types/import-markdown.d.ts diff --git a/package.json b/package.json index ec5d47679..799fe14a5 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,6 @@ "@babel/core": "^7.6.4", "@babel/plugin-proposal-class-properties": "^7.1.0", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.4.4", - "monaco-editor-webpack-plugin": "^1.9.0", "@babel/plugin-proposal-object-rest-spread": "^7.0.0", "@babel/plugin-proposal-optional-chaining": "^7.0.0", "@babel/plugin-syntax-dynamic-import": "^7.0.0", @@ -132,6 +131,7 @@ "@types/react-router-hash-link": "^1.2.1", "@types/react-syntax-highlighter": "^11.0.4", "@types/styled-components": "^4.1.19", + "@types/webpack": "^4.41.10", "@types/webpack-env": "^1.14.1", "akh": "^3.1.2", "autoprefixer": "^9.3.1", @@ -176,6 +176,7 @@ "mocha": "^5.0.4", "mocha-webpack": "^2.0.0-beta.0", "mock-local-storage": "^1.0.5", + "monaco-editor-webpack-plugin": "^1.9.0", "nearley-loader": "^2.0.0", "postcss-loader": "^2.1.2", "prettier": "^1.19.1", diff --git a/source/Provider.tsx b/source/Provider.tsx index 8ab9bc7fe..bfb0ef02c 100644 --- a/source/Provider.tsx +++ b/source/Provider.tsx @@ -4,7 +4,7 @@ import { TrackerProvider } from 'Components/utils/withTracker' import { createBrowserHistory } from 'history' import { AvailableLangs } from 'i18n' import i18next from 'i18next' -import React, { useEffect } from 'react' +import React, { useEffect, useMemo } from 'react' import { I18nextProvider } from 'react-i18next' import { Provider as ReduxProvider } from 'react-redux' import { Router } from 'react-router-dom' @@ -42,14 +42,14 @@ if ( } export type ProviderProps = { - tracker?: Tracker basename: string - sitePaths: SitePaths language: AvailableLangs - initialStore: RootState - onStoreCreated: (store: Store) => void - reduxMiddlewares: Array children: React.ReactNode + tracker?: Tracker + sitePaths?: SitePaths + initialStore?: RootState + onStoreCreated?: (store: Store) => void + reduxMiddlewares?: Array } export default function Provider({ @@ -62,9 +62,13 @@ export default function Provider({ onStoreCreated, children }: ProviderProps) { - const history = createBrowserHistory({ - basename: process.env.NODE_ENV === 'production' ? '' : basename - }) + const history = useMemo( + () => + createBrowserHistory({ + basename: process.env.NODE_ENV === 'production' ? '' : basename + }), + [] + ) useEffect(() => { tracker?.connectToHistory(history) return () => { @@ -82,10 +86,12 @@ export default function Provider({ ...(reduxMiddlewares ?? []) ) ) - if (language) { - i18next.changeLanguage(language) - if (initialStore) initialStore.lang = language - } + useEffect(() => { + if (language) { + i18next.changeLanguage(language) + } + }, []) + if (language && initialStore) initialStore.lang = language const store = createStore(reducers, initialStore, storeEnhancer) onStoreCreated?.(store) @@ -113,7 +119,7 @@ export default function Provider({ color={iframeCouleur && decodeURIComponent(iframeCouleur)} > - + <>{children} diff --git a/source/actions/actions.ts b/source/actions/actions.ts index 17b2d4db6..939ff6ea1 100644 --- a/source/actions/actions.ts +++ b/source/actions/actions.ts @@ -2,8 +2,9 @@ import { SitePaths } from 'Components/utils/withSitePaths' import { History } from 'history' import { RootState, SimulationConfig } from 'Reducers/rootReducer' import { ThunkAction } from 'redux-thunk' -import { DottedName } from 'Rules' +import { DottedName, Situation } from 'Rules' import { deletePersistedSimulation } from '../storage/persistSimulation' +import { CompanyStatusAction } from './companyStatusActions' export type Action = | ResetSimulationAction @@ -19,8 +20,9 @@ export type Action = | SetSituationBranchAction | UpdateDefaultUnitAction | SetActiveTargetAction + | CompanyStatusAction -export type ThunkResult = ThunkAction< +export type ThunkResult = ThunkAction< R, RootState, { history: History; sitePaths: SitePaths }, @@ -138,7 +140,11 @@ export const updateUnit = (defaultUnit: string) => defaultUnit } as const) -export function setExample(name: string, situation, dottedName: DottedName) { +export function setExample( + name: string, + situation: Situation, + dottedName: DottedName +) { return { type: 'SET_EXAMPLE', name, situation, dottedName } as const } diff --git a/source/actions/companyStatusActions.ts b/source/actions/companyStatusActions.ts index 91f335b26..043cf8d6e 100644 --- a/source/actions/companyStatusActions.ts +++ b/source/actions/companyStatusActions.ts @@ -1,11 +1,48 @@ import { dropWhile } from 'ramda' import { nextQuestionUrlSelector } from 'Selectors/companyStatusSelectors' +import { Action, ThunkResult } from './actions' -const thenGoToNextQuestion = actionCreator => (...args: unknown[]) => ( - dispatch, - getState, - { history, sitePaths } -) => { +export type CompanyStatusAction = + | CompanyIsSoleProprietorshipAction + | DefineDirectorStatusAction + | MultipleAssociatesAction + | CompanyIsMicroentrepriseAction + | SpecifyDirectorsShareAction + | ResetCompanyStatusChoiceAction + +type CompanyIsSoleProprietorshipAction = { + type: 'COMPANY_IS_SOLE_PROPRIETORSHIP' + isSoleProprietorship?: boolean +} + +type DefineDirectorStatusAction = { + type: 'DEFINE_DIRECTOR_STATUS' + status: DirectorStatus +} + +type MultipleAssociatesAction = { + type: 'COMPANY_HAS_MULTIPLE_ASSOCIATES' + multipleAssociates?: boolean +} + +type CompanyIsMicroentrepriseAction = { + type: 'COMPANY_IS_MICROENTERPRISE' + autoEntrepreneur?: boolean +} + +type SpecifyDirectorsShareAction = { + type: 'SPECIFY_DIRECTORS_SHARE' + minorityDirector?: boolean +} + +type ResetCompanyStatusChoiceAction = { + type: 'RESET_COMPANY_STATUS_CHOICE' + answersToReset?: string[] +} + +const thenGoToNextQuestion = (actionCreator: (...args: any[]) => Action) => ( + ...args: any[] +): ThunkResult => (dispatch, getState, { history, sitePaths }) => { dispatch(actionCreator(...args)) history.push(nextQuestionUrlSelector(getState(), { sitePaths })) } @@ -52,7 +89,7 @@ export const directorIsInAMinority = thenGoToNextQuestion( } as const) ) -export const goToCompanyStatusChoice = () => ( +export const goToCompanyStatusChoice = (): ThunkResult => ( dispatch, _, { history, sitePaths } @@ -63,7 +100,7 @@ export const goToCompanyStatusChoice = () => ( history.push(sitePaths.créer.index) } -export const resetCompanyStatusChoice = (from: string) => ( +export const resetCompanyStatusChoice = (from: string): ThunkResult => ( dispatch, getState ) => { @@ -77,5 +114,5 @@ export const resetCompanyStatusChoice = (from: string) => ( dispatch({ type: 'RESET_COMPANY_STATUS_CHOICE', answersToReset - }) + } as const) } diff --git a/source/api/sirene.ts b/source/api/sirene.ts index fb9fdd28a..698db2a4a 100644 --- a/source/api/sirene.ts +++ b/source/api/sirene.ts @@ -25,6 +25,16 @@ export async function searchDenominationOrSiren(value: string) { return searchFullText(value) } +type SireneData = { + etablissement: Array<{ + siren: string + is_siege: string + categorie_entreprise: string + activite_principale: string + l1_normalisee: string + }> +} + export type Etablissement = { siren: string denomination?: string @@ -39,7 +49,7 @@ async function searchFullText( if (!response.ok) { return null } - const json = await response.json() + const json: SireneData = await response.json() const etablissements = json.etablissement .filter( ({ is_siege, categorie_entreprise, activite_principale }) => diff --git a/source/components/Distribution.tsx b/source/components/Distribution.tsx index df1c4e29e..99a42cfe1 100644 --- a/source/components/Distribution.tsx +++ b/source/components/Distribution.tsx @@ -8,7 +8,6 @@ import { animated, config, useSpring } from 'react-spring' import { DottedName } from 'Rules' import { parsedRulesSelector } from 'Selectors/analyseSelectors' import répartitionSelector from 'Selectors/repartitionSelectors' -import { isIE } from '../utils' import './Distribution.css' import './PaySlip' import RuleLink from './RuleLink' @@ -73,7 +72,7 @@ export function DistributionBranch({ className="distribution-chart__item" style={{ opacity: styles.opacity }} > - +

@@ -95,15 +94,19 @@ export function DistributionBranch({ ) } -let ChartItemBar = ({ styles, color, montant }) => ( +type ChartItemBarProps = { + styles: React.CSSProperties + color: string + montant: number +} + +let ChartItemBar = ({ styles, color, montant }: ChartItemBarProps) => (

(
) -let BranchIcône = ({ icône }) => ( +let BranchIcône = ({ icône }: { icône: string }) => (
{emoji(icône)}
diff --git a/source/components/RulePage.tsx b/source/components/RulePage.tsx index aa7b6a0b6..403fcd863 100644 --- a/source/components/RulePage.tsx +++ b/source/components/RulePage.tsx @@ -20,7 +20,7 @@ export default function RulePage() { const brancheName = useSelector(situationBranchNameSelector) const valuesToShow = !useSelector(noUserInputSelector) const { name } = useParams() - const decodedRuleName = decodeRuleName(name) + const decodedRuleName = decodeRuleName(name ?? '') const renderRule = (dottedName: DottedName) => { return ( diff --git a/source/components/conversation/DateInput.tsx b/source/components/conversation/DateInput.tsx index aa1f337d9..97a431dca 100644 --- a/source/components/conversation/DateInput.tsx +++ b/source/components/conversation/DateInput.tsx @@ -1,12 +1,26 @@ import { normalizeDateString } from 'Engine/date' +import { RuleInputProps } from 'Engine/RuleInput' +import { Rule } from 'Engine/types' import React, { useCallback, useMemo } from 'react' import styled from 'styled-components' import InputSuggestions from './InputSuggestions' import SendButton from './SendButton' -export default function DateInput({ suggestions, onChange, onSubmit, value }) { +type DateInputProps = { + onChange: RuleInputProps['onChange'] + onSubmit: RuleInputProps['onSubmit'] + value: RuleInputProps['value'] + suggestions: Rule['suggestions'] +} + +export default function DateInput({ + suggestions, + onChange, + onSubmit, + value +}: DateInputProps) { const dateValue = useMemo(() => { - if (!value) return undefined + if (!value || typeof value !== 'string') return undefined const [day, month, year] = normalizeDateString(value).split('/') return `${year}-${month}-${day}` }, [value]) @@ -32,7 +46,7 @@ export default function DateInput({ suggestions, onChange, onSubmit, value }) { onFirstClick={value => { onChange(normalizeDateString(value as string)) }} - onSecondClick={() => onSubmit('suggestion')} + onSecondClick={() => onSubmit?.('suggestion')} />
diff --git a/source/components/conversation/InputSuggestions.tsx b/source/components/conversation/InputSuggestions.tsx index c19a173a8..f0e949e61 100644 --- a/source/components/conversation/InputSuggestions.tsx +++ b/source/components/conversation/InputSuggestions.tsx @@ -1,3 +1,4 @@ +import { Rule } from 'Engine/types' import { toPairs } from 'ramda' import React, { useState } from 'react' import { useTranslation } from 'react-i18next' @@ -6,7 +7,7 @@ import { defaultUnitSelector } from 'Selectors/analyseSelectors' import { convertUnit, parseUnit, Unit } from '../../engine/units' type InputSuggestionsProps = { - suggestions: Record + suggestions?: Rule['suggestions'] onFirstClick: (val: number | string) => void onSecondClick?: (val: number | string) => void unit?: Unit @@ -18,7 +19,7 @@ export default function InputSuggestions({ onFirstClick, unit }: InputSuggestionsProps) { - const [suggestion, setSuggestion] = useState() + const [suggestion, setSuggestion] = useState() const { t } = useTranslation() const defaultUnit = parseUnit(useSelector(defaultUnitSelector) ?? '') if (!suggestions) return null @@ -27,8 +28,11 @@ export default function InputSuggestions({
Suggestions : - {toPairs(suggestions).map(([text, value]: [string, number]) => { - value = unit ? convertUnit(unit, defaultUnit, value) : value + {toPairs(suggestions).map(([text, value]: [string, string | number]) => { + value = + unit && typeof value === 'number' + ? convertUnit(unit, defaultUnit, value) + : value return (
) } diff --git a/source/engine/RuleInput.tsx b/source/engine/RuleInput.tsx index ba02fa2c9..0afdcb65a 100644 --- a/source/engine/RuleInput.tsx +++ b/source/engine/RuleInput.tsx @@ -18,10 +18,10 @@ export const binaryOptionChoices = [ ] type Value = string | number | object | boolean -type Props = { +export type RuleInputProps = { rules: ParsedRules dottedName: DottedName - onChange: (value: Value) => void + onChange: (value: Value | null) => void useSwitch?: boolean isTarget?: boolean autoFocus?: boolean @@ -44,7 +44,7 @@ export default function RuleInput({ autoFocus = false, className, onSubmit -}: Props) { +}: RuleInputProps) { let rule = rules[dottedName] let unit = rule.unit || rule.defaultUnit let language = useTranslation().i18n.language @@ -139,7 +139,7 @@ export let buildVariantTree = (allRules, path) => { shouldBeExpanded ? { canGiveUp, - children: (variants as any).map(v => rec(path + ' . ' + v)) + children: variants.map(v => rec(path + ' . ' + v)) } : null ) diff --git a/source/rules/index.ts b/source/rules/index.ts index fbbe7f4fb..30c5fac25 100644 --- a/source/rules/index.ts +++ b/source/rules/index.ts @@ -1,7 +1,10 @@ // Currenty we systematically bundle all the rules even if we only need a // sub-section of them. We might support "code-splitting" the rules in the // future. -import { Rules as GenericRules } from 'Engine/types' +import { + EvaluatedRule as GenericEvaluatedRule, + Rules as GenericRules +} from 'Engine/types' import artisteAuteur from './artiste-auteur.yaml' import base from './base.yaml' import chômagePartiel from './chômage-partiel.yaml' @@ -21,6 +24,8 @@ import situationPersonnelle from './situation-personnelle.yaml' export type DottedName = keyof typeof jsonRules export type Rules = GenericRules +export type EvaluatedRule = GenericEvaluatedRule +export type Situation = Partial> const rules: Rules = { ...base, diff --git a/source/sites/mon-entreprise.fr/App.tsx b/source/sites/mon-entreprise.fr/App.tsx index 059de7589..33c54aff7 100644 --- a/source/sites/mon-entreprise.fr/App.tsx +++ b/source/sites/mon-entreprise.fr/App.tsx @@ -58,7 +58,7 @@ const middlewares = [ type InFranceRouteProps = { basename: ProviderProps['basename'] language: ProviderProps['language'] - rules: ProviderProps['initialStore']['rules'] + rules: NonNullable['rules'] } function InFranceRoute({ basename, language, rules }: InFranceRouteProps) { diff --git a/source/sites/mon-entreprise.fr/pages/Coronavirus.tsx b/source/sites/mon-entreprise.fr/pages/Coronavirus.tsx index 75f4d70bc..c9f1aab54 100644 --- a/source/sites/mon-entreprise.fr/pages/Coronavirus.tsx +++ b/source/sites/mon-entreprise.fr/pages/Coronavirus.tsx @@ -8,13 +8,12 @@ import { Markdown } from 'Components/utils/markdown' import { ScrollToTop } from 'Components/utils/Scroll' import { formatValue } from 'Engine/format' import { getRuleFromAnalysis } from 'Engine/ruleUtils' -import { EvaluatedRule } from 'Engine/types' import React, { useContext, useEffect, useState } from 'react' import { Helmet } from 'react-helmet' import { Trans, useTranslation } from 'react-i18next' import { useDispatch, useSelector } from 'react-redux' import { useLocation } from 'react-router' -import { DottedName } from 'Rules' +import { EvaluatedRule } from 'Rules' import { analysisWithDefaultsSelector } from 'Selectors/analyseSelectors' import styled from 'styled-components' import Animate from 'Ui/animate' @@ -191,7 +190,17 @@ function ExplanationSection() { ) } -function ComparaisonTable({ rows: [head, ...body] }) { +type ComparaisonTableProps = { + rows: [Array, ...Array] +} + +type Line = Array< + EvaluatedRule & { + additionalText?: React.ReactNode + } +> + +function ComparaisonTable({ rows: [head, ...body] }: ComparaisonTableProps) { const columns = head.filter(x => x !== '') const [currentColumnIndex, setCurrentColumnIndex] = useState( columns.length - 1 @@ -261,7 +270,7 @@ function ComparaisonTable({ rows: [head, ...body] }) { ) } -function ValueWithLink(rule: EvaluatedRule) { +function ValueWithLink(rule: EvaluatedRule) { const { language } = useTranslation().i18n return ( diff --git a/source/sites/mon-entreprise.fr/pages/Créer/CreationChecklist.tsx b/source/sites/mon-entreprise.fr/pages/Créer/CreationChecklist.tsx index 4142495f8..e17b61ebb 100644 --- a/source/sites/mon-entreprise.fr/pages/Créer/CreationChecklist.tsx +++ b/source/sites/mon-entreprise.fr/pages/Créer/CreationChecklist.tsx @@ -9,24 +9,25 @@ import React, { useContext } from 'react' import emoji from 'react-easy-emoji' import { Helmet } from 'react-helmet' import { Trans, useTranslation } from 'react-i18next' -import { connect, useSelector } from 'react-redux' +import { useDispatch, useSelector } from 'react-redux' import { Link } from 'react-router-dom' import { RootState } from 'Reducers/rootReducer' +import { LegalStatus } from 'Selectors/companyStatusSelectors' import * as Animate from 'Ui/animate' import { CheckItem, Checklist } from 'Ui/Checklist' import StatutDescription from './StatutDescription' -function CreateCompany({ - statut, - onChecklistInitialization, - onItemCheck, - onStatusChange -}) { +type CreateCompanyProps = { + statut: LegalStatus +} + +export default function CreateCompany({ statut }: CreateCompanyProps) { const { t, i18n } = useTranslation() const sitePaths = useContext(SitePathsContext) const companyCreationChecklist = useSelector( (state: RootState) => state.inFranceApp.companyCreationChecklist ) + const dispatch = useDispatch() // TODO : add this logic inside selector const isAutoentrepreneur = statut.startsWith('auto-entrepreneur') @@ -74,7 +75,7 @@ function CreateCompany({