From 7e2a4085a7c2a3c617c60bed9709cd5ae3f0ca32 Mon Sep 17 00:00:00 2001 From: Maxime Quandalle Date: Sun, 10 Nov 2019 16:57:44 +0100 Subject: [PATCH] Poursuite de la migration TypeScript MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Utilisation de la version stable de TypeScript 3.7 * Début de migration du State Redux. Plutôt que de redéfinir les types en doublon par rapport aux actions et reducers, on utilise les valeurs retournées par ces fonctions comme source pour les types globaux. * Modification de tsconfig pour meilleur typage dans VS Code * Meilleur typage de l'environnement : suppression de @types/node qui était trop large (contient tout l'environnement serveur), et remplacement par @types/webpack-env. Par ailleurs typage des variables d'environnement utilisées. * Début de migration de l'économie collaborative * Migration de nombreux composants UI * Mise à jour de dépendances pour récupérer un meilleur typage * Ajout d'un hook pour configurer les simulateurs * Suppression du higher-order component "withSitePaths", on utilise systématiquement le hook useContext. L'essentiel de l'application est maintenant migré, reste le moteur ! --- package.json | 15 +- source/{Provider.js => Provider.tsx} | 37 +- source/Tracker.ts | 19 +- source/actions/actions.js | 98 ----- source/actions/actions.ts | 157 +++++++ .../companyCreationChecklistActions.js | 21 - .../companyCreationChecklistActions.ts | 29 ++ source/actions/existingCompanyActions.js | 2 +- ...listAction.js => hiringChecklistAction.ts} | 12 +- source/api/{sirene.js => sirene.ts} | 15 +- source/components/Banner.js | 30 -- source/components/Banner.tsx | 29 ++ .../{CompanyDetails.js => CompanyDetails.tsx} | 4 +- .../{CurrencyInput.js => CurrencyInput.tsx} | 21 +- .../{FeedbackForm.js => FeedbackForm.tsx} | 26 +- source/components/Feedback/PageFeedback.js | 170 -------- source/components/Feedback/PageFeedback.tsx | 144 +++++++ .../{FindCompany.js => FindCompany.tsx} | 7 +- .../{LegalNotice.js => LegalNotice.tsx} | 0 .../{Mecanisms.js => Mecanisms.tsx} | 0 ...tterRegister.js => NewsletterRegister.tsx} | 5 +- source/components/{Overlay.js => Overlay.tsx} | 26 +- ...PercentageField.js => PercentageField.tsx} | 0 .../{PeriodSwitch.js => PeriodSwitch.tsx} | 4 +- source/components/PreviousSimulationBanner.js | 54 --- .../components/PreviousSimulationBanner.tsx | 36 ++ source/components/QuickLinks.js | 72 ---- source/components/QuickLinks.tsx | 52 +++ source/components/RuleLink.tsx | 9 +- .../components/{RulePage.js => RulePage.tsx} | 38 +- ...ryExplanation.js => SalaryExplanation.tsx} | 21 +- ...meComparaison.js => SchemeComparaison.tsx} | 185 ++++---- source/components/SearchBar.js | 18 +- .../{SearchButton.js => SearchButton.tsx} | 21 +- ...lateurWarning.js => SimulateurWarning.tsx} | 20 +- .../{Simulation.js => Simulation.tsx} | 21 +- ...StackedBarChart.js => StackedBarChart.tsx} | 17 +- ...TargetSelection.js => TargetSelection.tsx} | 63 ++- source/components/{Targets.js => Targets.tsx} | 19 +- source/components/{Value.js => Value.tsx} | 20 +- source/components/conversation/Aide.js | 64 --- source/components/conversation/Aide.tsx | 62 +++ .../{Conversation.js => Conversation.tsx} | 57 ++- .../{Explicable.js => Explicable.tsx} | 40 +- ...eAnswersButton.js => SeeAnswersButton.tsx} | 15 +- .../{SendButton.js => SendButton.tsx} | 12 +- source/components/index.js | 6 - source/components/index.tsx | 8 + source/components/rule/Namespace.tsx | 11 +- source/components/rule/References.tsx | 4 +- source/components/rule/Rule.js | 32 +- source/components/rule/RuleSource.tsx | 5 +- .../simulationConfigs/useSimulationConfig.ts | 16 + .../simulationConfigs/withSimulationConfig.js | 28 -- source/components/ui/AnimatedTargetValue.tsx | 12 +- source/components/ui/Checklist/index.tsx | 7 +- source/components/ui/Progress.tsx | 12 +- source/components/ui/SocialIcon.tsx | 30 +- source/components/utils/markdown.tsx | 6 +- source/components/utils/persistState.ts | 5 +- ...secting.js => useDisplayOnIntersecting.ts} | 10 +- source/components/utils/withColours.tsx | 2 +- source/components/utils/withSitePaths.tsx | 28 +- source/components/utils/withTracker.tsx | 18 +- source/engine/{format.js => format.ts} | 26 +- .../mecanismViews/{common.js => common.tsx} | 51 ++- source/engine/{units.js => units.ts} | 35 +- source/i18n.ts | 2 + ...nceAppReducer.js => inFranceAppReducer.ts} | 63 +-- .../{rootReducer.js => rootReducer.ts} | 163 ++++--- .../{storageReducer.js => storageReducer.ts} | 7 +- ...nalyseSelectors.js => analyseSelectors.ts} | 88 ++-- ...Selectors.js => companyStatusSelectors.ts} | 73 ++-- ...gressSelectors.js => progressSelectors.ts} | 3 +- source/selectors/storageSelectors.js | 23 - source/selectors/storageSelectors.ts | 33 ++ source/server.js | 2 +- source/sites/mon-entreprise.fr/entry.en.tsx | 1 - source/sites/mon-entreprise.fr/history.js | 0 .../layout/Footer/Footer.tsx | 7 +- .../layout/Footer/Privacy.tsx | 2 +- .../middlewares/trackSimulatorActions.ts | 2 +- .../pages/Créer/AfterRegistration.tsx | 11 +- .../pages/Créer/CreationChecklist.tsx | 48 ++- .../Créer/GuideStatut/PickLegalStatus.tsx | 17 +- .../Créer/GuideStatut/PreviousAnswers.tsx | 9 +- .../Créer/GuideStatut/SoleProprietorship.tsx | 15 +- .../mon-entreprise.fr/pages/Créer/Home.tsx | 23 +- .../pages/Créer/StatutDescription.tsx | 2 +- .../mon-entreprise.fr/pages/Dev/Couleur.tsx | 13 +- .../pages/Dev/IntegrationTest.tsx | 11 +- .../mon-entreprise.fr/pages/Dev/Sitemap.tsx | 37 +- .../pages/Documentation/ExampleSituations.tsx | 17 +- .../pages/Gérer/Embaucher.tsx | 405 +++++++++--------- .../mon-entreprise.fr/pages/Gérer/Home.tsx | 146 ++++--- .../pages/Gérer/SchemeSelection.tsx | 14 +- .../mon-entreprise.fr/pages/Gérer/index.tsx | 3 +- .../pages/Iframes/IframeFooter.tsx | 7 +- .../pages/Iframes/SimulateurEmbauche.tsx | 10 +- .../pages/Landing/Landing.tsx | 14 +- .../pages/Simulateurs/AssimiléSalarié.tsx | 26 +- .../pages/Simulateurs/AutoEntrepreneur.tsx | 8 +- .../pages/Simulateurs/Home.tsx | 218 +++++----- .../pages/Simulateurs/Indépendant.tsx | 9 +- .../pages/Simulateurs/Salarié.tsx | 27 +- .../pages/Simulateurs/index.tsx | 125 +++--- .../pages/integration/Library.tsx | 15 +- .../pages/integration/Options.tsx | 3 +- .../pages/integration/iframe.css | 1 - .../{Activité.js => Activité.tsx} | 47 +- .../ActivitésSelection.js | 160 ------- .../ActivitésSelection.tsx | 199 +++++++++ ...xonération.js => ExceptionsExonération.tsx} | 24 +- .../{NextButton.js => NextButton.tsx} | 22 +- .../{VotreSituation.js => VotreSituation.tsx} | 99 +++-- .../pages/ÉconomieCollaborative/actions.js | 22 - .../pages/ÉconomieCollaborative/actions.ts | 32 ++ .../{activitésData.js => activitésData.ts} | 2 +- .../{index.js => index.tsx} | 0 .../{reducer.js => reducer.ts} | 24 +- source/sites/mon-entreprise.fr/sitePaths.ts | 34 +- source/types/ActionsTypes.js | 40 -- source/types/Analysis.js | 4 - source/types/CDD.js | 26 -- source/types/ContratSalarié.js | 18 - source/types/Entreprise.js | 5 - source/types/RegleTypes.ts | 4 - source/types/Situation.ts | 10 - source/types/State.ts | 26 -- source/types/app-env.d.ts | 8 + source/types/companyCreationChecklistTypes.js | 16 - source/types/{typescript => }/css-prop.d.ts | 0 source/types/hiringChecklistTypes.js | 13 - .../types/{typescript => }/import-images.d.ts | 0 source/types/{typescript => }/import-yaml.ts | 0 .../{typescript => }/react-easy-emoji.d.ts | 0 source/types/rule.ts | 16 + source/types/shared.js | 2 - source/utils.ts | 8 +- tsconfig.json | 13 +- yarn.lock | 85 ++-- 141 files changed, 2501 insertions(+), 2300 deletions(-) rename source/{Provider.js => Provider.tsx} (76%) delete mode 100644 source/actions/actions.js create mode 100644 source/actions/actions.ts delete mode 100644 source/actions/companyCreationChecklistActions.js create mode 100644 source/actions/companyCreationChecklistActions.ts rename source/actions/{hiringChecklistAction.js => hiringChecklistAction.ts} (50%) rename source/api/{sirene.js => sirene.ts} (69%) delete mode 100644 source/components/Banner.js create mode 100644 source/components/Banner.tsx rename source/components/{CompanyDetails.js => CompanyDetails.tsx} (89%) rename source/components/CurrencyInput/{CurrencyInput.js => CurrencyInput.tsx} (83%) rename source/components/Feedback/{FeedbackForm.js => FeedbackForm.tsx} (77%) delete mode 100644 source/components/Feedback/PageFeedback.js create mode 100644 source/components/Feedback/PageFeedback.tsx rename source/components/{FindCompany.js => FindCompany.tsx} (93%) rename source/components/{LegalNotice.js => LegalNotice.tsx} (100%) rename source/components/{Mecanisms.js => Mecanisms.tsx} (100%) rename source/components/{NewsletterRegister.js => NewsletterRegister.tsx} (96%) rename source/components/{Overlay.js => Overlay.tsx} (68%) rename source/components/{PercentageField.js => PercentageField.tsx} (100%) rename source/components/{PeriodSwitch.js => PeriodSwitch.tsx} (88%) delete mode 100644 source/components/PreviousSimulationBanner.js create mode 100644 source/components/PreviousSimulationBanner.tsx delete mode 100644 source/components/QuickLinks.js create mode 100644 source/components/QuickLinks.tsx rename source/components/{RulePage.js => RulePage.tsx} (64%) rename source/components/{SalaryExplanation.js => SalaryExplanation.tsx} (88%) rename source/components/{SchemeComparaison.js => SchemeComparaison.tsx} (86%) rename source/components/{SearchButton.js => SearchButton.tsx} (75%) rename source/components/{SimulateurWarning.js => SimulateurWarning.tsx} (87%) rename source/components/{Simulation.js => Simulation.tsx} (84%) rename source/components/{StackedBarChart.js => StackedBarChart.tsx} (89%) rename source/components/{TargetSelection.js => TargetSelection.tsx} (84%) rename source/components/{Targets.js => Targets.tsx} (70%) rename source/components/{Value.js => Value.tsx} (78%) delete mode 100644 source/components/conversation/Aide.js create mode 100644 source/components/conversation/Aide.tsx rename source/components/conversation/{Conversation.js => Conversation.tsx} (60%) rename source/components/conversation/{Explicable.js => Explicable.tsx} (55%) rename source/components/conversation/{SeeAnswersButton.js => SeeAnswersButton.tsx} (59%) rename source/components/conversation/{SendButton.js => SendButton.tsx} (70%) delete mode 100644 source/components/index.js create mode 100644 source/components/index.tsx create mode 100644 source/components/simulationConfigs/useSimulationConfig.ts delete mode 100644 source/components/simulationConfigs/withSimulationConfig.js rename source/components/utils/{useDisplayOnIntersecting.js => useDisplayOnIntersecting.ts} (70%) rename source/engine/{format.js => format.ts} (77%) rename source/engine/mecanismViews/{common.js => common.tsx} (83%) rename source/engine/{units.js => units.ts} (75%) rename source/reducers/{inFranceAppReducer.js => inFranceAppReducer.ts} (77%) rename source/reducers/{rootReducer.js => rootReducer.ts} (56%) rename source/reducers/{storageReducer.js => storageReducer.ts} (69%) rename source/selectors/{analyseSelectors.js => analyseSelectors.ts} (79%) rename source/selectors/{companyStatusSelectors.js => companyStatusSelectors.ts} (74%) rename source/selectors/{progressSelectors.js => progressSelectors.ts} (70%) delete mode 100644 source/selectors/storageSelectors.js create mode 100644 source/selectors/storageSelectors.ts delete mode 100644 source/sites/mon-entreprise.fr/history.js rename source/sites/mon-entreprise.fr/pages/ÉconomieCollaborative/{Activité.js => Activité.tsx} (81%) delete mode 100644 source/sites/mon-entreprise.fr/pages/ÉconomieCollaborative/ActivitésSelection.js create mode 100644 source/sites/mon-entreprise.fr/pages/ÉconomieCollaborative/ActivitésSelection.tsx rename source/sites/mon-entreprise.fr/pages/ÉconomieCollaborative/{ExceptionsExonération.js => ExceptionsExonération.tsx} (56%) rename source/sites/mon-entreprise.fr/pages/ÉconomieCollaborative/{NextButton.js => NextButton.tsx} (69%) rename source/sites/mon-entreprise.fr/pages/ÉconomieCollaborative/{VotreSituation.js => VotreSituation.tsx} (64%) delete mode 100644 source/sites/mon-entreprise.fr/pages/ÉconomieCollaborative/actions.js create mode 100644 source/sites/mon-entreprise.fr/pages/ÉconomieCollaborative/actions.ts rename source/sites/mon-entreprise.fr/pages/ÉconomieCollaborative/{activitésData.js => activitésData.ts} (95%) rename source/sites/mon-entreprise.fr/pages/ÉconomieCollaborative/{index.js => index.tsx} (100%) rename source/sites/mon-entreprise.fr/pages/ÉconomieCollaborative/{reducer.js => reducer.ts} (71%) delete mode 100644 source/types/ActionsTypes.js delete mode 100644 source/types/Analysis.js delete mode 100644 source/types/CDD.js delete mode 100644 source/types/ContratSalarié.js delete mode 100644 source/types/Entreprise.js delete mode 100644 source/types/RegleTypes.ts delete mode 100644 source/types/Situation.ts delete mode 100644 source/types/State.ts create mode 100644 source/types/app-env.d.ts delete mode 100644 source/types/companyCreationChecklistTypes.js rename source/types/{typescript => }/css-prop.d.ts (100%) delete mode 100644 source/types/hiringChecklistTypes.js rename source/types/{typescript => }/import-images.d.ts (100%) rename source/types/{typescript => }/import-yaml.ts (100%) rename source/types/{typescript => }/react-easy-emoji.d.ts (100%) create mode 100644 source/types/rule.ts delete mode 100644 source/types/shared.js diff --git a/package.json b/package.json index 859198eb0..70b6470cc 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "react-i18next": "^11.0.0", "react-loading-skeleton": "^1.1.2", "react-markdown": "^4.1.0", - "react-number-format": "^4.0.8", + "react-number-format": "^4.3.1", "react-redux": "^7.0.3", "react-router": "^5.1.1", "react-router-dom": "^5.1.1", @@ -53,8 +53,8 @@ "react-transition-group": "^2.2.1", "react-virtualized": "^9.20.0", "react-virtualized-select": "^3.1.3", - "reduce-reducers": "^0.1.2", - "redux": "^3.7.2", + "reduce-reducers": "^1.0.4", + "redux": "^4.0.4", "redux-thunk": "^2.3.0", "regenerator-runtime": "^0.13.3", "reselect": "^4.0.0", @@ -103,17 +103,20 @@ "@babel/preset-flow": "^7.0.0-beta.51", "@babel/preset-react": "^7.0.0", "@babel/preset-typescript": "^7.6.0", + "@types/classnames": "^2.2.9", "@types/iframe-resizer": "^3.5.7", - "@types/node": "^12.11.7", "@types/ramda": "^0.26.33", + "@types/raven-for-redux": "^1.1.1", "@types/react": "^16.9.11", "@types/react-addons-css-transition-group": "^15.0.5", + "@types/react-color": "^3.0.1", "@types/react-dom": "^16.9.3", "@types/react-helmet": "^5.0.13", "@types/react-redux": "^7.1.5", "@types/react-router": "^5.1.2", "@types/react-router-dom": "^5.1.0", "@types/styled-components": "^4.1.19", + "@types/webpack-env": "^1.14.1", "akh": "^3.1.2", "autoprefixer": "^9.3.1", "babel-eslint": "^11.0.0-beta.0", @@ -155,7 +158,7 @@ "mock-local-storage": "^1.0.5", "nearley-loader": "^2.0.0", "postcss-loader": "^2.1.2", - "prettier": "^1.16.4", + "prettier": "^1.19.1", "ramda-fantasy": "^0.8.0", "raw-loader": "^0.5.1", "react-hot-loader": "^4.12.15", @@ -167,7 +170,7 @@ "style-loader": "^0.23.1", "styled-components": "^4.2.0", "toml-loader": "^1.0.0", - "typescript": "^3.7.1-rc", + "typescript": "^3.7.2", "url-loader": "^1.0.1", "webpack": "^4.39.3", "webpack-cli": "^3.1.2", diff --git a/source/Provider.js b/source/Provider.tsx similarity index 76% rename from source/Provider.js rename to source/Provider.tsx index 94e3d7f0e..613dce56a 100644 --- a/source/Provider.js +++ b/source/Provider.tsx @@ -1,17 +1,25 @@ import { ThemeColoursProvider } from 'Components/utils/withColours' -import { SitePathProvider } from 'Components/utils/withSitePaths' +import { SitePathProvider, SitePaths } from 'Components/utils/withSitePaths' import { TrackerProvider } from 'Components/utils/withTracker' -import { createBrowserHistory } from 'history' +import { createBrowserHistory, History } from 'history' +import { AvailableLangs } from 'i18n' import i18next from 'i18next' import React, { PureComponent } from 'react' import { I18nextProvider } from 'react-i18next' import { Provider as ReduxProvider } from 'react-redux' import { Router } from 'react-router-dom' -import reducers from 'Reducers/rootReducer' -import { applyMiddleware, compose, createStore } from 'redux' +import reducers, { RootState } from 'Reducers/rootReducer' +import { applyMiddleware, compose, createStore, Middleware, Store } from 'redux' import thunk from 'redux-thunk' +import Tracker from 'Tracker' import { inIframe } from './utils' +declare global { + interface Window { + __REDUX_DEVTOOLS_EXTENSION_COMPOSE__: any + } +} + const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose if ( @@ -33,8 +41,20 @@ if ( }) } -export default class Provider extends PureComponent { - constructor(props) { +type ProviderProps = { + tracker: Tracker + basename: string + sitePaths: SitePaths + language: AvailableLangs + initialStore: RootState + onStoreCreated: (store: Store) => void + reduxMiddlewares: Array +} + +export default class Provider extends PureComponent { + history: History + store: Store + constructor(props: ProviderProps) { super(props) this.history = createBrowserHistory({ basename: process.env.NODE_ENV === 'production' ? '' : this.props.basename @@ -56,7 +76,7 @@ export default class Provider extends PureComponent { this.props.initialStore.lang = this.props.language } this.store = createStore(reducers, this.props.initialStore, storeEnhancer) - this.props.onStoreCreated && this.props.onStoreCreated(this.store) + this.props.onStoreCreated?.(this.store) // Remove loader var css = document.createElement('style') @@ -82,7 +102,8 @@ export default class Provider extends PureComponent { // If IE < 11 display nothing + colour={iframeCouleur && decodeURIComponent(iframeCouleur)} + > diff --git a/source/Tracker.ts b/source/Tracker.ts index 7da1add4c..2c12d1564 100644 --- a/source/Tracker.ts +++ b/source/Tracker.ts @@ -1,17 +1,14 @@ +import { History, Location } from 'history' import { debounce } from './utils' declare global { - interface Window { _paq: any; } + interface Window { + _paq: any + } } -function push(args: ['trackPageView']): void -function push(args: ['trackEvent']): void -function push(args: ['trackEvent', string, string]): void -function push(args: ['trackEvent', string, string, string]): void -function push(args: ['trackEvent', string, string, string, string, string]): void -function push(args: ['trackEvent', string, string, string, number]): void -function push() {} -type PushType = typeof push +type PushArgs = ['trackPageView'] | ['trackEvent', ...Array] +type PushType = (args: PushArgs) => void export default class Tracker { push: PushType @@ -23,7 +20,7 @@ export default class Tracker { this.push = debounce(200, pushFunction) as PushType } - connectToHistory(history) { + connectToHistory(history: History) { this.unlistenFromHistory = history.listen(loc => { this.track(loc) }) @@ -52,5 +49,5 @@ export default class Tracker { } export const devTracker = new Tracker( - (console?.log?.bind(console)) ?? (() => {}) // eslint-disable-line no-console + console?.log?.bind(console) ?? (() => {}) // eslint-disable-line no-console ) diff --git a/source/actions/actions.js b/source/actions/actions.js deleted file mode 100644 index 95846d8dc..000000000 --- a/source/actions/actions.js +++ /dev/null @@ -1,98 +0,0 @@ -import type { - ResetSimulationAction, - LoadPreviousSimulationAction, - StepAction, - DeletePreviousSimulationAction, - SetSimulationConfigAction, - SetSituationBranchAction -} from 'Types/ActionsTypes' -import { deletePersistedSimulation } from '../storage/persistSimulation' -import type { Thunk } from 'Types/ActionsTypes' - -export const resetSimulation = () => (dispatch: any => void): void => { - dispatch( - ({ - type: 'RESET_SIMULATION' - }: ResetSimulationAction) - ) -} - -export const goToQuestion = (question: string): StepAction => ({ - type: 'STEP_ACTION', - name: 'unfold', - step: question -}) - -export const validateStepWithValue = ( - dottedName, - value: any -): Thunk => dispatch => { - dispatch(updateSituation(dottedName, value)) - dispatch({ - type: 'STEP_ACTION', - name: 'fold', - step: dottedName - }) -} - -export const setSituationBranch = (id: number): SetSituationBranchAction => ({ - type: 'SET_SITUATION_BRANCH', - id -}) - -export const setSimulationConfig = ( - config: Object -): Thunk => (dispatch, _, { history }): void => { - const url = history.location.pathname - dispatch({ - type: 'SET_SIMULATION', - url, - config - }) -} - -export const deletePreviousSimulation = () => ( - dispatch: DeletePreviousSimulationAction => void -) => { - dispatch({ - type: 'DELETE_PREVIOUS_SIMULATION' - }) - deletePersistedSimulation() -} - -export const updateSituation = (fieldName, value) => ({ - type: 'UPDATE_SITUATION', - fieldName, - value -}) - -export const updatePeriod = toPeriod => ({ - type: 'UPDATE_PERIOD', - toPeriod -}) - -// $FlowFixMe -export function setExample(name, situation, dottedName) { - return { type: 'SET_EXAMPLE', name, situation, dottedName } -} - -export const goBackToSimulation = (): Thunk => ( - dispatch, - getState, - { history } -) => { - dispatch({ type: 'SET_EXEMPLE', name: null }) - history.push(getState().simulation.url) -} - -export function loadPreviousSimulation(): LoadPreviousSimulationAction { - return { - type: 'LOAD_PREVIOUS_SIMULATION' - } -} - -export function hideControl(id: string) { - return { type: 'HIDE_CONTROL', id } -} - -export const EXPLAIN_VARIABLE = 'EXPLAIN_VARIABLE' diff --git a/source/actions/actions.ts b/source/actions/actions.ts new file mode 100644 index 000000000..9f59adb78 --- /dev/null +++ b/source/actions/actions.ts @@ -0,0 +1,157 @@ +import { SitePaths } from 'Components/utils/withSitePaths' +import { History } from 'history' +import { RootState } from 'Reducers/rootReducer' +import { ThunkAction } from 'redux-thunk' +import { DottedName } from 'Types/rule' +import { deletePersistedSimulation } from '../storage/persistSimulation' + +export type Action = + | ResetSimulationAction + | StepAction + | UpdateAction + | SetSimulationConfigAction + | DeletePreviousSimulationAction + | SetExempleAction + | ExplainVariableAction + | UpdatePeriodAction + | HideControlAction + | LoadPreviousSimulationAction + | SetSituationBranchAction + | SetActiveTargetAction + +type ThunkResult = ThunkAction< + R, + RootState, + { history: History; sitePaths: SitePaths }, + Action +> + +type StepAction = { + type: 'STEP_ACTION' + name: 'fold' | 'unfold' + step: string +} + +type SetSimulationConfigAction = { + type: 'SET_SIMULATION' + url: string + config: Object +} + +type DeletePreviousSimulationAction = { + type: 'DELETE_PREVIOUS_SIMULATION' +} + +type SetExempleAction = { + type: 'SET_EXAMPLE' + name: null | string + situation?: object + dottedName?: string +} + +type ResetSimulationAction = ReturnType +type UpdateAction = ReturnType +type UpdatePeriodAction = ReturnType +type LoadPreviousSimulationAction = ReturnType +type SetSituationBranchAction = ReturnType +type SetActiveTargetAction = ReturnType +type HideControlAction = ReturnType +type ExplainVariableAction = ReturnType + +export const resetSimulation = () => + ({ + type: 'RESET_SIMULATION' + } as const) + +export const goToQuestion = (question: string) => + ({ + type: 'STEP_ACTION', + name: 'unfold', + step: question + } as const) + +export const validateStepWithValue = ( + dottedName: DottedName, + value: any +): ThunkResult => dispatch => { + dispatch(updateSituation(dottedName, value)) + dispatch({ + type: 'STEP_ACTION', + name: 'fold', + step: dottedName + }) +} + +export const setSituationBranch = (id: number) => + ({ + type: 'SET_SITUATION_BRANCH', + id + } as const) + +export const setSimulationConfig = (config: Object): ThunkResult => ( + dispatch, + _, + { history } +): void => { + const url = history.location.pathname + dispatch({ + type: 'SET_SIMULATION', + url, + config + }) +} + +export const setActiveTarget = (targetName: string) => + ({ + type: 'SET_ACTIVE_TARGET_INPUT', + name: targetName + } as const) + +export const deletePreviousSimulation = (): ThunkResult => dispatch => { + dispatch({ + type: 'DELETE_PREVIOUS_SIMULATION' + }) + deletePersistedSimulation() +} + +export const updateSituation = (fieldName: DottedName, value: any) => + ({ + type: 'UPDATE_SITUATION', + fieldName, + value + } as const) + +export const updatePeriod = (toPeriod: string) => + ({ + type: 'UPDATE_PERIOD', + toPeriod + } as const) + +export function setExample(name: string, situation, dottedName: string) { + return { type: 'SET_EXAMPLE', name, situation, dottedName } as const +} + +export const goBackToSimulation = (): ThunkResult => ( + dispatch, + getState, + { history } +) => { + dispatch({ type: 'SET_EXAMPLE', name: null }) + history.push(getState().simulation.url) +} + +export function loadPreviousSimulation() { + return { + type: 'LOAD_PREVIOUS_SIMULATION' + } as const +} + +export function hideControl(id: string) { + return { type: 'HIDE_CONTROL', id } as const +} + +export const explainVariable = (variableName = null) => + ({ + type: 'EXPLAIN_VARIABLE', + variableName + } as const) diff --git a/source/actions/companyCreationChecklistActions.js b/source/actions/companyCreationChecklistActions.js deleted file mode 100644 index 6788cdfe2..000000000 --- a/source/actions/companyCreationChecklistActions.js +++ /dev/null @@ -1,21 +0,0 @@ -import type { - InitializeCompanyCreationChecklistAction, - CheckCompanyCreationItemAction -} from 'Types/companyCreationChecklistTypes' - -export const initializeCompanyCreationChecklist = ( - statusName: string, - checklistItems: Array -) => - ({ - type: 'INITIALIZE_COMPANY_CREATION_CHECKLIST', - checklistItems, - statusName - }: InitializeCompanyCreationChecklistAction) - -export const checkCompanyCreationItem = (name: string, checked: boolean) => - ({ - type: 'CHECK_COMPANY_CREATION_ITEM', - name, - checked - }: CheckCompanyCreationItemAction) diff --git a/source/actions/companyCreationChecklistActions.ts b/source/actions/companyCreationChecklistActions.ts new file mode 100644 index 000000000..4fc5c8b25 --- /dev/null +++ b/source/actions/companyCreationChecklistActions.ts @@ -0,0 +1,29 @@ +import { LegalStatus } from 'Selectors/companyStatusSelectors' + +export type Action = + | InitializeCompanyCreationChecklistAction + | CheckCompanyCreationItemAction + +type InitializeCompanyCreationChecklistAction = ReturnType< + typeof initializeCompanyCreationChecklist +> +type CheckCompanyCreationItemAction = ReturnType< + typeof checkCompanyCreationItem +> + +export const initializeCompanyCreationChecklist = ( + statusName: LegalStatus, + checklistItems: Array +) => + ({ + type: 'INITIALIZE_COMPANY_CREATION_CHECKLIST', + checklistItems, + statusName + } as const) + +export const checkCompanyCreationItem = (name: string, checked: boolean) => + ({ + type: 'CHECK_COMPANY_CREATION_ITEM', + name, + checked + } as const) diff --git a/source/actions/existingCompanyActions.js b/source/actions/existingCompanyActions.js index c46469c7f..0ddcfa36a 100644 --- a/source/actions/existingCompanyActions.js +++ b/source/actions/existingCompanyActions.js @@ -1,6 +1,6 @@ import { fetchCompanyDetails } from '../api/sirene' -const fetchCommuneDetails = function (codeCommune) { +const fetchCommuneDetails = function(codeCommune) { return fetch( `https://geo.api.gouv.fr/communes/${codeCommune}?fields=departement,region` ).then(response => { diff --git a/source/actions/hiringChecklistAction.js b/source/actions/hiringChecklistAction.ts similarity index 50% rename from source/actions/hiringChecklistAction.js rename to source/actions/hiringChecklistAction.ts index de2de028c..0445463d9 100644 --- a/source/actions/hiringChecklistAction.js +++ b/source/actions/hiringChecklistAction.ts @@ -1,17 +1,17 @@ -import type { - InitializeHiringChecklistAction, - CheckHiringItemAction -} from 'Types/hiringChecklistTypes' +export type Action = InitializeHiringChecklistAction | CheckHiringItemAction + +type InitializeHiringChecklistAction = ReturnType +type CheckHiringItemAction = ReturnType export const initializeHiringChecklist = (checklistItems: Array) => ({ type: 'INITIALIZE_HIRING_CHECKLIST', checklistItems - }: InitializeHiringChecklistAction) + } as const) export const checkHiringItem = (name: string, checked: boolean) => ({ type: 'CHECK_HIRING_ITEM', name, checked - }: CheckHiringItemAction) + } as const) diff --git a/source/api/sirene.js b/source/api/sirene.ts similarity index 69% rename from source/api/sirene.js rename to source/api/sirene.ts index 9493efe96..8df00bf7a 100644 --- a/source/api/sirene.js +++ b/source/api/sirene.ts @@ -1,7 +1,7 @@ -const isSIREN = input => input.match(/^[\s]*([\d][\s]*){9}$/) -const isSIRET = input => input.match(/^[\s]*([\d][\s]*){14}$/) +const isSIREN = (input: string) => input.match(/^[\s]*([\d][\s]*){9}$/) +const isSIRET = (input: string) => input.match(/^[\s]*([\d][\s]*){14}$/) -export async function fetchCompanyDetails(siren) { +export async function fetchCompanyDetails(siren: string) { const response = await fetch( `https://entreprise.data.gouv.fr/api/sirene/v3/unites_legales/${siren.replace( /[\s]/g, @@ -15,7 +15,7 @@ export async function fetchCompanyDetails(siren) { return json.unite_legale } -export async function searchDenominationOrSiren(value) { +export async function searchDenominationOrSiren(value: string) { if (isSIRET(value)) { value = value.replace(/[\s]/g, '').slice(0, 9) } @@ -25,7 +25,12 @@ export async function searchDenominationOrSiren(value) { return searchFullText(value) } -async function searchFullText(text) { +export type Etablissement = { + siren: string + denomination?: string +} + +async function searchFullText(text: string): Promise> { const response = await fetch( `https://entreprise.data.gouv.fr/api/sirene/v1/full_text/${text}?per_page=5` ) diff --git a/source/components/Banner.js b/source/components/Banner.js deleted file mode 100644 index 9514ea217..000000000 --- a/source/components/Banner.js +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react' -import emoji from 'react-easy-emoji' -import { connect } from 'react-redux' -import { firstStepCompletedSelector } from 'Selectors/analyseSelectors' -import Animate from 'Ui/animate' -import './Banner.css' -import type { Node } from 'react' -import type { State } from 'Types/State' -type PropTypes = { - hidden: boolean, - children: Node, - icon?: string -} - -let Banner = ({ hidden = false, children, icon }: PropTypes) => - !hidden ? ( - -
- {icon && emoji(icon)} -

{children}

-
-
- ) : null - -export default connect( - (state: State, { hidden }: PropTypes) => ({ - hidden: hidden || firstStepCompletedSelector(state) - }), - {} -)(Banner) diff --git a/source/components/Banner.tsx b/source/components/Banner.tsx new file mode 100644 index 000000000..d751ae5d1 --- /dev/null +++ b/source/components/Banner.tsx @@ -0,0 +1,29 @@ +import React from 'react' +import emoji from 'react-easy-emoji' +import { useSelector } from 'react-redux' +import { firstStepCompletedSelector } from 'Selectors/analyseSelectors' +import Animate from 'Ui/animate' +import './Banner.css' + +type BannerProps = { + children: React.ReactNode + hidden?: boolean + icon?: string +} + +export default function Banner({ + children, + hidden: hiddenProp = false, + icon +}: BannerProps) { + const hiddenState = useSelector(firstStepCompletedSelector) + const hidden = hiddenProp || hiddenState + return !hidden ? ( + +
+ {icon && emoji(icon)} +

{children}

+
+
+ ) : null +} diff --git a/source/components/CompanyDetails.js b/source/components/CompanyDetails.tsx similarity index 89% rename from source/components/CompanyDetails.js rename to source/components/CompanyDetails.tsx index 96523d295..88966a7ea 100644 --- a/source/components/CompanyDetails.js +++ b/source/components/CompanyDetails.tsx @@ -2,9 +2,9 @@ import { T } from 'Components' import React, { useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Skeleton from 'react-loading-skeleton' -import { fetchCompanyDetails } from '../api/sirene' +import { Etablissement, fetchCompanyDetails } from '../api/sirene' -export default function CompanyDetails({ siren, denomination }) { +export default function CompanyDetails({ siren, denomination }: Etablissement) { const { i18n } = useTranslation() const DateFormatter = useMemo( () => diff --git a/source/components/CurrencyInput/CurrencyInput.js b/source/components/CurrencyInput/CurrencyInput.tsx similarity index 83% rename from source/components/CurrencyInput/CurrencyInput.js rename to source/components/CurrencyInput/CurrencyInput.tsx index 907db76a6..f8d892146 100644 --- a/source/components/CurrencyInput/CurrencyInput.js +++ b/source/components/CurrencyInput/CurrencyInput.tsx @@ -1,10 +1,18 @@ import classnames from 'classnames' -import React, { useRef, useState } from 'react' import { currencyFormat } from 'Engine/format' -import NumberFormat from 'react-number-format' +import React, { useRef, useState } from 'react' +import NumberFormat, { NumberFormatProps } from 'react-number-format' import { debounce } from '../../utils' import './CurrencyInput.css' +type CurrencyInputProps = NumberFormatProps & { + value?: string | number + debounce?: number + onChange?: (event: React.ChangeEvent) => void + currencySymbol?: string + language?: Parameters[0] +} + export default function CurrencyInput({ value: valueProp = '', debounce: debounceTimeout, @@ -13,7 +21,7 @@ export default function CurrencyInput({ language, className, ...forwardedProps -}) { +}: CurrencyInputProps) { const [initialValue, setInitialValue] = useState(valueProp) const [currentValue, setCurrentValue] = useState(valueProp) const onChangeDebounced = useRef( @@ -23,7 +31,7 @@ export default function CurrencyInput({ // the DOM `event` in its custom `onValueChange` handler const nextValue = useRef(null) - const inputRef = useRef() + const inputRef = useRef() // When the component is rendered with a new "value" prop, we reset our local state if (valueProp !== initialValue) { @@ -31,7 +39,7 @@ export default function CurrencyInput({ setInitialValue(valueProp) } - const handleChange = event => { + const handleChange = (event: React.ChangeEvent) => { // Only trigger the `onChange` event if the value has changed -- and not // only its formating, we don't want to call it when a dot is added in `12.` // for instance @@ -63,7 +71,8 @@ export default function CurrencyInput({
5 ? { style: { width } } : {})} - onClick={() => inputRef.current.focus()}> + onClick={() => inputRef.current.focus()} + > {!currentValue && isCurrencyPrefixed && currencySymbol} void, tracker: Tracker, onCancel: () => void } +type Props = { onEnd: () => void; onCancel: () => void } -function FeedbackForm({ onEnd, onCancel, tracker }: Props) { - const formRef = useRef() +export default function FeedbackForm({ onEnd, onCancel }: Props) { + const formRef = useRef() + const tracker = useContext(TrackerContext) - const handleFormSubmit = e => { + const handleFormSubmit = (e: React.FormEvent): void => { tracker.push(['trackEvent', 'Feedback', 'written feedback submitted']) e.preventDefault() fetch('/', { method: 'POST', - // $FlowFixMe body: new FormData(formRef.current) }) onEnd() @@ -28,7 +26,8 @@ function FeedbackForm({ onEnd, onCancel, tracker }: Props) { onClick={() => onCancel()} className="ui__ link-button" style={{ textDecoration: 'none', marginLeft: '0.3rem' }} - aria-label="close"> + aria-label="close" + > X
@@ -37,7 +36,8 @@ function FeedbackForm({ onEnd, onCancel, tracker }: Props) { style={{ flex: 1 }} method="post" ref={formRef} - onSubmit={handleFormSubmit}> + onSubmit={handleFormSubmit} + >