🔥 Migration vers TypeScript

Outils
======

Ce commit retire le tooling de Flow, et ajoute le support de TypeScript
pour les fichiers .ts et .tsx. Il n'est pas nécessaire de tout migrer
d'un coup ce qui facilite la transition. On garde en effet le
compilateur Babel avec un preset TypeScript (ce qui permet donc de
retirer à la fois les types Flow et TypeScript) plutôt que d'utiliser le
compilateur standard pour la conversion du code. Cela permet aussi de
mieux s'intégrer avec d'autres outils, notamment les test-runners.

Ajout d'une nouvelle commande `yarn run type-check`, intégrée dans
CircleCI.

Par ailleurs ajout du support de l'opérateur ?? pour donner des valeurs
par défaut (nullish-coalescing-operator).

Typage des libraires tierces
============================

Les principales libraires que nous utilisons ont un typage TypeScript de
bon niveau, ce qui facilite l'intégration. J'ai mis à jour react-i18next
et i18next afin de corriger un problème de typage.

Typage du code
==============

Le typage est loin d'être complet dans ce commit, en particulier il
manque les types relatifs au state Redux, ainsi qu'au moteur (règle,
explication). Néanmoins le typage des contextes fonctionne, en
particulier sitePaths (avec un type récursif non trivial !) qui a déjà
permis de détecter un lien mort.

Le typage des "paths" (Components/, Règles/, etc.) fonctionne bien, y
compris avec l'auto-complétion automatique des import par Typescript.

TypeScript se révèle déjà bien agréable dans VSCode (auto-complétion,
refacto, etc.) ! Reste à migrer progressivement le reste du code !
pull/764/head
Maxime Quandalle 2019-10-26 18:21:09 +02:00
parent ac4e3cd615
commit 6de970f0e3
156 changed files with 1797 additions and 2237 deletions

View File

@ -18,7 +18,6 @@ parser: babel-eslint
plugins:
- react
- react-hooks
- flowtype
env:
browser: true
commonjs: true
@ -38,7 +37,6 @@ globals:
extends:
- eslint:recommended
- plugin:flowtype/recommended
- plugin:react/recommended
- prettier
- prettier/flowtype

View File

@ -1,33 +0,0 @@
[ignore]
<PROJECT_ROOT>/documentation/.*
<PROJECT_ROOT>/node_modules/.*
[include]
[libs]
[options]
esproposal.class_static_fields=enable
esproposal.class_instance_fields=enable
esproposal.optional_chaining=enable
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FixMe
suppress_type=$FlowExpectedError
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
module.name_mapper='^Engine\(.*\)$' -> '<PROJECT_ROOT>/source/engine\1'
module.name_mapper='^Règles\(.*\)$' -> '<PROJECT_ROOT>/source/règles\1'
module.name_mapper='^Ui\(.*\)$' -> '<PROJECT_ROOT>/source/components/ui\1'
module.name_mapper='^Components\(.*\)$' -> '<PROJECT_ROOT>/source/components\1'
module.name_mapper='^Selectors\(.*\)$' -> '<PROJECT_ROOT>/source/selectors\1'
module.name_mapper='^Reducers\(.*\)$' -> '<PROJECT_ROOT>/source/reducers\1'
module.name_mapper='^Actions\(.*\)$' -> '<PROJECT_ROOT>/source/actions\1'
module.name_mapper='^Types\(.*\)$' -> '<PROJECT_ROOT>/source/types\1'
module.name_mapper='^Images\(.*\)$' -> '<PROJECT_ROOT>/source/images\1'

View File

@ -1,12 +1,12 @@
{
"printWidth": 80,
"trailingComma": "none",
"bracketSpacing": true,
"jsxBracketSameLine": true,
"useTabs": true,
"semi": false,
"singleQuote": true,
"arrowParens": "avoid",
"proseWrap": "preserve",
"tabWidth": 2
}
"printWidth": 80,
"trailingComma": "none",
"bracketSpacing": true,
"jsxBracketSameLine": true,
"useTabs": true,
"semi": false,
"singleQuote": true,
"arrowParens": "avoid",
"proseWrap": "preserve",
"tabWidth": 2
}

View File

@ -1,6 +1,5 @@
{
"recommendations": [
"flowtype.flow-for-vscode",
"esbenp.prettier-vscode",
"ban.spellright",
"jpoissonnier.vscode-styled-components"

View File

@ -4,9 +4,11 @@
"fr"
],
"spellright.documentTypes": [
"yaml"
"yaml",
"git-commit"
],
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
}
},
"typescript.tsdk": "node_modules/typescript/lib"
}

View File

@ -18,13 +18,13 @@ L'application est écrite en JavaScript, elle est exécuté uniquement côté cl
Nous utilisons :
- [TypeScript](https://www.typescriptlang.org) pour ajouter un système de typage à notre code JavaScript. Le typage n'est pas utilisé partout et il n'est pas obligatoire de le prendre en compte pour contribuer.
- [Yarn](https://yarnpkg.com/fr) pour la gestion des dépendances (à la place de NPM qui est souvent utilisé dans les applications JavaScript)
- [React](https://reactjs.org) pour la gestion de l'interface utilisateur
- [Redux](https://redux.js.org) pour gérer le “state” de l'application côté client
- [Prettier](https://prettier.io/) pour formater le code source, l'idéal est de configurer votre éditeur de texte pour que les fichiers soit formatés automatiquement quand vous sauvegardez un fichier. Si vous utilisez [VS Code](https://code.visualstudio.com/) cette configuration est automatique.
- [Webpack](https://webpack.js.org) pour le “bundling”
- [Eslint](http://eslint.org) qui permet par exemple d'éviter de garder des variables inutilisées
- [Flow](https://flow.org/) pour ajouter un système de typage à notre code JavaScript. Le typage Flow n'est pas utilisé partout et il n'est pas obligatoire de le prendre en compte pour contribuer.
- [Ramda](https://ramdajs.com) comme libraire d'utilitaires pour manipuler les listes/objects/etc (c'est une alternative à lodash ou underscore)
- [Mocha](https://mochajs.org), [Jest](https://jestjs.io) et [Cypress](https://www.cypress.io) pour les l'execution des tests. Plus d'informations dans la section consacrée aux tests.

View File

@ -9,12 +9,13 @@ module.exports = {
}
],
'@babel/react',
'@babel/preset-typescript',
'@babel/flow'
],
plugins: [
'@babel/plugin-proposal-class-properties',
'@babel/plugin-proposal-do-expressions',
'@babel/plugin-proposal-optional-chaining',
'@babel/plugin-proposal-nullish-coalescing-operator',
'@babel/plugin-proposal-object-rest-spread',
'@babel/plugin-syntax-dynamic-import',
'react-hot-loader/babel',

View File

@ -30,13 +30,20 @@ commands:
- run: CYPRESS_baseUrl=<< parameters.base_url >> yarn run cypress run --parallel --record --key 21660df5-36a5-4c49-b23d-801799b0c759 --env language=<< parameters.language >> --config integrationFolder=cypress/integration/<< parameters.integration_folder >>
jobs:
type-check:
docker:
- image: node
steps:
- install
- run: |
yarn run type-check
unit-test:
docker:
- image: node
steps:
- install
- run: |
yarn run flow-typed install
git config --global core.quotepath false
yarn test
yarn test-regressions
@ -86,6 +93,7 @@ workflows:
version: 2
test:
jobs:
- type-check
- unit-test
- end-to-end-test
- production-end-to-end-test:

View File

@ -177,7 +177,7 @@ module.exports = {
// ie ['jest-transform-nearley', 'babel-jest'], so we removed ES6 module from nearley output.
'\\.ne$': 'jest-transform-nearley',
'\\.yaml$': 'yaml-jest',
'\\.js?$': 'babel-jest'
'\\.(js|tsx?)$': 'babel-jest'
},
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation

View File

@ -1,37 +0,0 @@
{
"exclude": [
"node_modules"
],
"include": [
"source/**/*"
],
"paths": {
"Engine": [
"source/engine"
],
"Règles": [
"source/règles"
],
"Actions": [
"source/actions"
],
"Ui": [
"source/components/ui"
],
"Components": [
"source/components"
],
"Selectors": [
"source/selectors"
],
"Reducers": [
"source/reducers"
],
"Types": [
"source/types"
],
"Images": [
"source/image"
]
}
}

View File

@ -26,7 +26,7 @@
"core-js": "^3.2.1",
"focus-trap-react": "^3.1.2",
"fuse.js": "^3.4.2",
"i18next": "^14.1.1",
"i18next": "^18.0.1",
"iframe-resizer": "^4.1.1",
"js-yaml": "^3.13.1",
"moo": "^0.5.0",
@ -41,7 +41,7 @@
"react-easy-emoji": "^1.2.0",
"react-helmet": "6.0.0-beta",
"react-highlight-words": "^0.11.0",
"react-i18next": "^10.0.1",
"react-i18next": "^11.0.0",
"react-loading-skeleton": "^1.1.2",
"react-markdown": "^4.1.0",
"react-number-format": "^4.0.8",
@ -85,6 +85,7 @@
"test-expressions": "yarn test-common --grep 'Suite expressions'",
"test-units": "mocha-webpack --webpack-config source/webpack.test.js --require source-map-support/register --include componentTestSetup.js --require mock-local-storage --require test/helpers/browser.js test/units.test.js",
"test-regressions": "jest",
"type-check": "tsc --noEmit",
"compile-lib": "yarn webpack --config source/webpack.lib.js",
"compile-dev": "FR_SITE='http://localhost:5000${path}' EN_SITE='http://localhost:5001${path}' yarn run compile",
"mon-entreprise:serve": "PORT=5000 serve --config serve.mon-entreprise.json --no-clipboard",
@ -96,13 +97,25 @@
"devDependencies": {
"@babel/core": "^7.6.4",
"@babel/plugin-proposal-class-properties": "^7.1.0",
"@babel/plugin-proposal-do-expressions": "^7.0.0",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.4.4",
"@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",
"@babel/preset-env": "^7.6.3",
"@babel/preset-flow": "^7.0.0-beta.51",
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.6.0",
"@types/iframe-resizer": "^3.5.7",
"@types/node": "^12.11.7",
"@types/ramda": "^0.26.33",
"@types/react": "^16.9.11",
"@types/react-addons-css-transition-group": "^15.0.5",
"@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",
"akh": "^3.1.2",
"autoprefixer": "^9.3.1",
"babel-eslint": "^11.0.0-beta.0",
@ -123,13 +136,10 @@
"enzyme-adapter-react-16": "^1.1.1",
"eslint": "^6.5.1",
"eslint-config-prettier": "^4.0.0",
"eslint-plugin-flowtype": "^3.2.1",
"eslint-plugin-react": "^7.12.4",
"eslint-plugin-react-hooks": "^2.0.1",
"express": "^4.16.3",
"file-loader": "^1.1.11",
"flow-bin": "^0.92.0",
"flow-typed": "^2.4.0",
"html-loader": "^0.5.5",
"html-webpack-plugin": "^3.2.0",
"http-server": "^0.11.1",
@ -159,6 +169,7 @@
"style-loader": "^0.23.1",
"styled-components": "^4.2.0",
"toml-loader": "^1.0.0",
"typescript": "^3.7.1-rc",
"url-loader": "^1.0.1",
"webpack": "^4.39.3",
"webpack-cli": "^3.1.2",

View File

@ -23,9 +23,11 @@ if (
navigator.serviceWorker
.register('/sw.js')
.then(registration => {
// eslint-disable-next-line no-console
console.log('SW registered: ', registration)
})
.catch(registrationError => {
// eslint-disable-next-line no-console
console.log('SW registration failed: ', registrationError)
})
})

View File

@ -1,13 +1,17 @@
/* @flow */
import { debounce } from './utils'
import type { BrowserHistory } from 'history/createBrowserHistory'
type PushType = (
| ['trackPageView']
| ['trackEvent', string, string]
| ['trackEvent', string, string, string]
| ['trackEvent', string, string, string, number]
) => void
declare global {
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
export default class Tracker {
push: PushType
@ -16,10 +20,10 @@ export default class Tracker {
constructor(pushFunction: PushType = args => window._paq.push(args)) {
if (typeof window !== 'undefined') window._paq = window._paq || []
this.push = debounce(200, pushFunction)
this.push = debounce(200, pushFunction) as PushType
}
connectToHistory(history: BrowserHistory) {
connectToHistory(history) {
this.unlistenFromHistory = history.listen(loc => {
this.track(loc)
})
@ -48,5 +52,5 @@ export default class Tracker {
}
export const devTracker = new Tracker(
(console && console.log && console.log.bind(console)) || (() => {}) // eslint-disable-line no-console
(console?.log?.bind(console)) ?? (() => {}) // eslint-disable-line no-console
)

View File

@ -1,4 +1,3 @@
/* @flow */
import type {
ResetSimulationAction,
LoadPreviousSimulationAction,

View File

@ -1,4 +1,3 @@
/* @flow */
import type {
InitializeCompanyCreationChecklistAction,
CheckCompanyCreationItemAction

View File

@ -1,4 +1,3 @@
/* @flow */
import { dropWhile, last } from 'ramda'
import { nextQuestionUrlSelector } from 'Selectors/companyStatusSelectors'

View File

@ -1,4 +1,3 @@
/* @flow */
import type {
InitializeHiringChecklistAction,
CheckHiringItemAction

View File

@ -1,5 +1,3 @@
/* @flow */
import React from 'react'
import emoji from 'react-easy-emoji'
import { connect } from 'react-redux'
@ -24,9 +22,9 @@ let Banner = ({ hidden = false, children, icon }: PropTypes) =>
</Animate.fadeIn>
) : null
export default (connect(
export default connect(
(state: State, { hidden }: PropTypes) => ({
hidden: hidden || firstStepCompletedSelector(state)
}),
{}
)(Banner): React$ComponentType<PropTypes>)
)(Banner)

View File

@ -1,8 +1,7 @@
import { React, T } from 'Components'
import { useEffect, useMemo, useState } from 'react'
import { T } from 'Components'
import React, { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Skeleton from 'react-loading-skeleton'
// $FlowFixMe
import 'react-select/dist/react-select.css'
import { fetchCompanyDetails } from '../api/sirene'
@ -33,8 +32,8 @@ export default function CompanyDetails({ siren, denomination }) {
<small>({siren})</small>
</>
) : (
<Skeleton width={400} />
)}
<Skeleton width={400} />
)}
</h3>
<p className="ui__ notice">
@ -43,8 +42,8 @@ export default function CompanyDetails({ siren, denomination }) {
{company ? (
DateFormatter.format(new Date(company.date_creation))
) : (
<Skeleton width={80} />
)}
<Skeleton width={80} />
)}
</strong>
, <T>domiciliée à</T>{' '}
{company ? (
@ -53,10 +52,9 @@ export default function CompanyDetails({ siren, denomination }) {
{company.etablissement_siege.code_postal})
</>
) : (
<Skeleton width={100} />
)}
<Skeleton width={100} />
)}
</p>
</>
)
}

View File

@ -1,4 +1,3 @@
/* @flow */
import { ScrollToElement } from 'Components/utils/Scroll'
import withTracker from 'Components/utils/withTracker'

View File

@ -1,5 +1,3 @@
/* @flow */
import withTracker from 'Components/utils/withTracker'
import React, { Component } from 'react'
import { Trans } from 'react-i18next'
@ -166,7 +164,7 @@ class PageFeedback extends Component<Props, State> {
const PageFeedbackWithRouter = ({ location, ...props }) => (
<PageFeedback {...props} location={location} key={location.pathname} />
)
export default (compose(
export default compose(
withRouter,
withTracker
)(PageFeedbackWithRouter): React$ComponentType<OwnProps>)
)(PageFeedbackWithRouter)

View File

@ -1,9 +1,8 @@
import { setEntreprise } from 'Actions/existingCompanyActions'
import { React, T } from 'Components'
import { T } from 'Components'
import CompanyDetails from 'Components/CompanyDetails'
import { useCallback, useMemo, useState } from 'react'
import React, { useCallback, useMemo, useState } from 'react'
import { useDispatch } from 'react-redux'
// $FlowFixMe
import 'react-select/dist/react-select.css'
import { searchDenominationOrSiren } from '../api/sirene'
import { debounce } from '../utils'
@ -13,7 +12,7 @@ export default function Search() {
const [isLoading, setLoadingState] = useState(false)
const handleSearch = useCallback(
function (value) {
function(value) {
searchDenominationOrSiren(value).then(results => {
setLoadingState(false)
setSearchResults(results)
@ -67,7 +66,11 @@ export default function Search() {
debouncedHandleSearch(e.target.value)
}}
/>
{!isLoading && searchResults === null && <p><T>Aucun résultat</T></p>}
{!isLoading && searchResults === null && (
<p>
<T>Aucun résultat</T>
</p>
)}
{searchResults &&
searchResults.map(({ siren, denomination }) => (

View File

@ -1,4 +1,3 @@
/* @flow */
import type { FicheDePaie } from 'Types/ResultViewTypes'
import withColours from 'Components/utils/withColours'
import Value from 'Components/Value'

View File

@ -1,5 +1,6 @@
import { React, T } from 'Components'
import { T } from 'Components'
import Value from 'Components/Value'
import React from 'react'
import RuleLink from './RuleLink'
export let SalaireBrutSection = ({ getRule }) => {

View File

@ -1,4 +1,3 @@
/* @flow */
import {
deletePreviousSimulation,
loadPreviousSimulation

View File

@ -1,4 +1,3 @@
/* @flow */
import { goToQuestion } from 'Actions/actions'
import { T } from 'Components'
import { compose, contains, filter, reject, toPairs } from 'ramda'
@ -59,7 +58,7 @@ const QuickLinks = ({
)
}
export default (connect(
export default connect(
(state, props) => ({
key: props.language,
currentQuestion: currentQuestionSelector(state),
@ -70,4 +69,4 @@ export default (connect(
{
goToQuestion
}
)(QuickLinks): React$ComponentType<OwnProps>)
)(QuickLinks)

View File

@ -1,5 +1,6 @@
import { React, T } from 'Components'
import { T } from 'Components'
import image from 'Images/map-directions.png'
import React from 'react'
import emoji from 'react-easy-emoji'
import { Link } from 'react-router-dom'

View File

@ -1,42 +0,0 @@
/* @flow */
import withColours from 'Components/utils/withColours'
import withSitePaths from 'Components/utils/withSitePaths'
import { encodeRuleName, nameLeaf } from 'Engine/rules'
import { compose } from 'ramda'
import React from 'react'
import { Link } from 'react-router-dom'
import './RuleLink.css'
import type { Règle } from 'Types/RegleTypes'
import type { ThemeColours } from 'Components/utils/withColours'
type Props = Règle & {
sitePaths: Object,
style: CSSStyleDeclaration,
colours: ThemeColours
}
const RuleLink = ({
dottedName,
title,
colours: { colour },
style,
sitePaths,
children
}: Props) => {
const newPath =
sitePaths.documentation.index + '/' + encodeRuleName(dottedName)
return (
<Link
to={newPath}
className="rule-link"
title={title}
style={{ color: colour, ...style }}>
{children || title || nameLeaf(dottedName)}
</Link>
)
}
export default compose(
withSitePaths,
withColours
)(RuleLink)

View File

@ -0,0 +1,34 @@
import { ThemeColoursContext } from 'Components/utils/withColours'
import { SitePathsContext } from 'Components/utils/withSitePaths'
import { encodeRuleName, nameLeaf } from 'Engine/rules'
import React, { useContext } from 'react'
import { Link } from 'react-router-dom'
import { Règle } from 'Types/RegleTypes'
import './RuleLink.css'
type RuleLinkProps = Règle & {
style: React.CSSProperties
children: React.ReactNode
}
export default function RuleLink({
dottedName,
title,
style,
children
}: RuleLinkProps) {
const sitePaths = useContext(SitePathsContext)
const { colour } = useContext(ThemeColoursContext)
const newPath =
sitePaths.documentation.index + '/' + encodeRuleName(dottedName)
return (
<Link
to={newPath}
className="rule-link"
title={title}
style={{ color: colour, ...style }}>
{children || title || nameLeaf(dottedName)}
</Link>
)
}

View File

@ -1,4 +1,3 @@
/* @flow */
import { setSituationBranch } from 'Actions/actions'
import {
defineDirectorStatus,
@ -639,7 +638,7 @@ const RuleValueLink = compose(
}
)
export default (compose(
export default compose(
withSimulationConfig(ComparaisonConfig),
connect(
state => ({
@ -657,4 +656,4 @@ export default (compose(
setSituationBranch
}
)
)(SchemeComparaison): React$Component<OwnProps>)
)(SchemeComparaison)

View File

@ -1,5 +1,5 @@
import withSitePaths from 'Components/utils/withSitePaths'
import { encodeRuleName } from 'Engine/rules.js'
import { encodeRuleName } from 'Engine/rules'
import Fuse from 'fuse.js'
import { compose, pick, sortBy } from 'ramda'
import React, { useMemo, useRef, useState } from 'react'

View File

@ -5,14 +5,14 @@ import PeriodSwitch from 'Components/PeriodSwitch'
import RuleLink from 'Components/RuleLink'
import { ThemeColoursContext } from 'Components/utils/withColours'
import withSitePaths from 'Components/utils/withSitePaths'
import { formatCurrency } from 'Engine/format'
import { encodeRuleName } from 'Engine/rules'
import { isEmpty, isNil } from 'ramda'
import React, { useEffect, useState, useContext } from 'react'
import React, { useContext, useEffect, useState } from 'react'
import emoji from 'react-easy-emoji'
import { useTranslation } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
import { Link } from 'react-router-dom'
import { formatCurrency } from 'Engine/format'
import {
analysisWithDefaultsSelector,
useSituation,

View File

@ -1,6 +1,7 @@
import { React, T } from 'Components'
import { useTranslation } from 'react-i18next'
import { T } from 'Components'
import { formatValue } from 'Engine/format'
import React from 'react'
import { useTranslation } from 'react-i18next'
// let booleanTranslations = { true: '✅', false: '❌' }

View File

@ -3,7 +3,7 @@ import ReactSelect from 'react-select'
import 'react-select/dist/react-select.css'
import { FormDecorator } from '../FormDecorator'
import './Select.css'
import SelectOption from './SelectOption.js'
import SelectOption from './SelectOption'
function ReactSelectWrapper({
value,

View File

@ -1,8 +1,6 @@
import React, { Component } from 'react'
import emoji from 'react-easy-emoji'
import React from 'react'
import { Trans } from 'react-i18next'
let T = ({ k, ...props }) => <Trans i18nKey={k} {...props} />
export { React, Component, T, emoji }
export { T }

View File

@ -1,7 +1,8 @@
import classNames from 'classnames'
import { React, T } from 'Components'
import { T } from 'Components'
import { makeJsx } from 'Engine/evaluation'
import { any, identity, path } from 'ramda'
import React from 'react'
import { Trans } from 'react-i18next'
import './Algorithm.css'
// The showValues prop is passed as a context. It used to be delt in CSS (not(.showValues) display: none), both coexist right now

View File

@ -1,7 +1,7 @@
import { toPairs } from 'ramda'
import React from 'react'
import { capitalise0 } from '../../utils'
import references from 'Règles/ressources/références/références.yaml'
import { capitalise0 } from '../../utils'
import './References.css'
const findRefKey = link =>
@ -32,7 +32,11 @@ function Ref({ name, link }) {
)
}
export default function References({ refs }) {
interface ReferencesProps {
refs: { [key: string]: string }
}
export default function References({ refs }: ReferencesProps) {
let references = toPairs(refs)
return (
<ul className="references">

View File

@ -1,13 +1,13 @@
/* @flow */
import { formatCurrency } from 'Engine/format'
import React, { useRef } from 'react'
import ReactCSSTransitionGroup from 'react-addons-css-transition-group'
import { useTranslation } from 'react-i18next'
import { formatCurrency } from 'Engine/format'
import { usePeriod } from 'Selectors/analyseSelectors'
import './AnimatedTargetValue.css'
type Props = {
value: ?number
interface AnimatedTargetValueProps {
value?: number
children: React.ReactNode
}
function formatDifference(difference, language) {
@ -15,8 +15,11 @@ function formatDifference(difference, language) {
return prefix + formatCurrency(difference, language)
}
export default function AnimatedTargetValue({ value, children }: Props) {
const previousValue = useRef()
export default function AnimatedTargetValue({
value,
children
}: AnimatedTargetValueProps) {
const previousValue = useRef<number>()
const { language } = useTranslation().i18n
// We don't want to show the animated if the difference comes from a change in the period
@ -54,7 +57,7 @@ export default function AnimatedTargetValue({ value, children }: Props) {
}
const Evaporate = React.memo(
({ children, style }: { children: string, style: Object }) => (
({ children, style }: { children: string; style: Object }) => (
<ReactCSSTransitionGroup
transitionName="evaporate"
transitionEnterTimeout={2500}

View File

@ -1,12 +0,0 @@
/* @flow */
import { React } from 'Components'
import './button.css'
import type { ElementConfig } from 'react'
export const LinkButton = (props: ElementConfig<'button'>) => (
<button {...props} className={'ui__ link-button ' + props.className} />
)
export const Button = (props: ElementConfig<'button'>) => (
<button {...props} className={'ui__ button ' + props.className} />
)

View File

@ -0,0 +1,10 @@
import React from 'react'
import './button.css'
export const LinkButton = (props: React.HTMLAttributes<HTMLButtonElement>) => (
<button {...props} className={'ui__ link-button ' + props.className} />
)
export const Button = (props: React.HTMLAttributes<HTMLButtonElement>) => (
<button {...props} className={'ui__ button ' + props.className} />
)

View File

@ -10,7 +10,7 @@ export default function Checkbox(props) {
style={{ display: 'none' }}
{...props}
/>
<label htmlFor={props.id} className="ui__ checkbox" tabIndex="0">
<label htmlFor={props.id} className="ui__ checkbox" tabIndex={0}>
<svg width="1em" height="1em" viewBox="0 0 18 18">
<path d="M1,9 L1,3.5 C1,2 2,1 3.5,1 L14.5,1 C16,1 17,2 17,3.5 L17,14.5 C17,16 16,17 14.5,17 L3.5,17 C2,17 1,16 1,14.5 L1,9 Z" />
<polyline points="1 9 7 14 15 4" />

View File

@ -1,36 +1,31 @@
/* @flow */
import classnames from 'classnames'
import Animate from 'Ui/animate'
import { Markdown } from 'Components/utils/markdown'
import { ScrollToElement } from 'Components/utils/Scroll'
import withTracker from 'Components/utils/withTracker'
import React, { Component, useState } from 'react'
import Animate from 'Ui/animate'
import { TrackerContext } from 'Components/utils/withTracker'
import React, { Component, useContext, useState } from 'react'
import Checkbox from '../Checkbox'
import './index.css'
import type Tracker from '../../../Tracker'
import type { ChildrenArray, Node, Element } from 'react'
type CheckItemProps = {
title: Node,
name: string,
explanations: Node,
onChange?: boolean => void,
tracker: Tracker,
title: React.ReactNode
name: string
explanations?: React.ReactNode
onChange?: (checked: boolean) => void
defaultChecked?: boolean
}
function CheckItemComponent({
export function CheckItem({
title,
name,
explanations,
onChange,
tracker,
defaultChecked
}: CheckItemProps) {
const tracker = useContext(TrackerContext)
const [displayExplanations, setDisplayExplanations] = useState(false)
const handleChecked = (e: SyntheticInputEvent<HTMLInputElement>) => {
const handleChecked = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.checked) {
setDisplayExplanations(false)
}
@ -82,16 +77,15 @@ function CheckItemComponent({
</ScrollToElement>
)
}
export const CheckItem = withTracker(CheckItemComponent)
type ChecklistProps = {
children: ChildrenArray<null | false | Element<typeof CheckItem>>,
onItemCheck: (string, boolean) => void,
onInitialization: (Array<string>) => void,
defaultChecked: { [string]: boolean }
children: React.ReactNode
onItemCheck: (string, boolean) => void
onInitialization: (arg: Array<string>) => void
defaultChecked: { [key: string]: boolean }
}
export class Checklist extends Component<ChecklistProps> {
checklist: Array<Element<typeof CheckItem>>
checklist: any
static defaultProps = {
defaultChecked: {},
onItemCheck: () => {},
@ -101,28 +95,22 @@ export class Checklist extends Component<ChecklistProps> {
super(props)
this.checklist = React.Children.toArray(props.children)
.filter(Boolean)
.map(
// $FlowFixMe
(child: Element<typeof CheckItem>) =>
// $FlowFixMe
React.cloneElement(child, {
// $FlowFixMe
onChange: checked => props.onItemCheck(child.props.name, checked),
// $FlowFixMe
defaultChecked:
child.props.defaultChecked ||
props.defaultChecked[child.props.name]
})
.map((child: any) =>
React.cloneElement(child, {
onChange: checked => props.onItemCheck(child.props.name, checked),
defaultChecked:
child.props.defaultChecked || props.defaultChecked[child.props.name]
})
)
props.onInitialization &&
// $FlowFixMe
props.onInitialization(this.checklist.map(child => child.props.name))
props.onInitialization(
this.checklist.map((child: any) => child.props.name)
)
}
render() {
return (
<ul className="ui__ no-bullet checklist">
{this.checklist.map(checkItem => (
// $FlowFixMe
{this.checklist.map((checkItem: any) => (
<li key={checkItem.props.name}>{checkItem}</li>
))}
</ul>

View File

@ -4,7 +4,7 @@ import './InfoBulle.css'
export default function InfoBulle({ children }) {
return (
<span style={{ position: 'relative', verticalAlign: 'bottom' }}>
<span className="info-bulle__interrogation-mark" tabIndex="0">
<span className="info-bulle__interrogation-mark" tabIndex={0}>
?
</span>
<span className="info-bulle__text">{children}</span>

View File

@ -1,7 +1,5 @@
/* @flow */
import withColours from 'Components/utils/withColours'
import withColours, { ThemeColours } from 'Components/utils/withColours'
import React from 'react'
import type { ThemeColours } from 'Components/utils/withColours'
type OwnProps = {
media: 'email' | 'facebook' | 'linkedin' | 'github' | 'twitter'

View File

@ -1,161 +0,0 @@
/* @flow */
import React, { useEffect, useState } from 'react'
import { animated, config as configPresets, Spring, Trail, Transition } from 'react-spring/renderprops'
import type { SpringConfig } from 'react-spring/renderprops'
import type { Node } from 'react'
type Props = {
children: Node,
config?: SpringConfig,
style?: Object,
className?: string,
delay?: number
}
// Todo : better animate with fromRight on desktop
export const fromBottom = ({
children,
config = configPresets.stiff,
style: inheritedStyle = {},
delay = 0
}: Props) => (
<Trail
keys={React.Children.map(children, (_, i) => i)}
native={true}
delay={delay}
config={config}
from={{ opacity: 0, y: 10 }}
leave={{ opacity: 0, y: -10 }}
to={{ opacity: 1, y: 0 }}
items={children}>
{/* eslint-disable-next-line react/display-name */}
{item => ({ y, ...style }) => (
<animated.div
style={{
transform: y.interpolate(y =>
y !== 0 ? `translate3d(0, ${y}px,0)` : 'none'
),
...style,
...inheritedStyle
}}>
{item}
</animated.div>
)}
</Trail>
)
export const fromTop = ({
children,
config = configPresets.stiff,
style: inheritedStyle = {},
delay = 0
}: Props) => (
<Trail
keys={React.Children.map(children, (_, i) => i)}
native={true}
delay={delay}
config={config}
leave={{ opacity: 0, y: 20 }}
from={{ opacity: 0, y: -20 }}
to={{ opacity: 1, y: 0 }}
items={children}>
{/* eslint-disable-next-line react/display-name */}
{item => ({ y, ...style }) => (
<animated.div
style={{
transform: y.interpolate(y =>
y ? `translate3d(0, ${y}px,0)` : 'none'
),
...style,
...inheritedStyle
}}>
{item}
</animated.div>
)}
</Trail>
)
export const leftToRight = ({
children,
config = configPresets.stiff,
delay = 0
}: Props) => (
<Transition
from={{
opacity: 0,
transform: 'translateX(100%)'
}}
native
keys={location.pathname}
enter={{
opacity: 1,
transform: 'translateX(0%)'
}}
config={config}
delay={delay}
leave={{
opacity: 0,
position: 'absolute',
transform: 'translateX(-100%)'
}}>
{style => <animated.div style={style}>{children}</animated.div>}
</Transition>
)
export const fadeIn = ({
children,
config = configPresets.default,
delay = 0
}: Props) => (
<Spring
native={true}
delay={delay}
config={config}
from={{ opacity: 0 }}
to={{
opacity: 1
}}>
{style => <animated.div style={style}>{children}</animated.div>}
</Spring>
)
export function appear({
children,
className,
unless = false,
config = configPresets.default,
delay = 0,
style
}) {
// TODO: We should rename this function Appear
// eslint-disable-next-line react-hooks/rules-of-hooks
const [show, setShow] = useState(unless)
// eslint-disable-next-line react-hooks/rules-of-hooks
useEffect(() => {
window.setTimeout(() => setShow(true), 0)
}, [])
return (
<Spring
delay={delay}
native
config={config}
to={{
opacity: show ? 1 : 0,
height: show ? 'auto' : '0px'
}}>
{animStyle => (
<animated.div style={{ ...style, ...animStyle }} className={className}>
{children}
</animated.div>
)}
</Spring>
)
}
export default {
appear,
fromBottom,
leftToRight,
fromTop,
fadeIn
}

View File

@ -0,0 +1,163 @@
import React, { useEffect, useState } from 'react'
import {
animated,
config as configPresets,
interpolate,
Spring,
SpringConfig,
Trail,
Transition
} from 'react-spring/renderprops'
type Props = {
children: React.ReactNode
config?: SpringConfig
style?: Object
className?: string
delay?: number
}
// Todo : better animate with fromRight on desktop
export const fromBottom = ({
children,
config = configPresets.stiff,
style: inheritedStyle = {},
delay = 0
}: Props) => (
<Trail
keys={React.Children.map(children, (_, i) => i)}
native={true}
delay={delay}
config={config}
from={{ opacity: 0, y: 10 }}
to={{ opacity: 1, y: 0 }}
items={children}>
{item => ({ y, ...style }) => (
<animated.div
style={{
transform: interpolate([y], y =>
y !== 0 ? `translate3d(0, ${y}px,0)` : 'none'
),
...style,
...inheritedStyle
}}>
{item}
</animated.div>
)}
</Trail>
)
export const fromTop = ({
children,
config = configPresets.stiff,
style: inheritedStyle = {},
delay = 0
}: Props) => (
<Trail
keys={React.Children.map(children, (_, i) => i)}
native={true}
delay={delay}
config={config}
from={{ opacity: 0, y: -20 }}
to={{ opacity: 1, y: 0 }}
items={children}>
{item => ({ y, ...style }) => (
<animated.div
style={{
transform: interpolate([y], y =>
y !== 0 ? `translate3d(0, ${y}px,0)` : 'none'
),
...style,
...inheritedStyle
}}>
{item}
</animated.div>
)}
</Trail>
)
export const leftToRight = ({
children,
config = configPresets.stiff,
delay = 0
}: Props) => (
<Transition
from={{
opacity: 0,
transform: 'translateX(100%)'
}}
native
keys={location.pathname}
enter={{
opacity: 1,
transform: 'translateX(0%)'
}}
config={config}
delay={delay}
leave={{
opacity: 0,
position: 'absolute',
transform: 'translateX(-100%)'
}}
items={children}>
{item => style => <animated.div style={style}>{item}</animated.div>}
</Transition>
)
export const fadeIn = ({
children,
config = configPresets.default,
delay = 0
}: Props) => (
<Spring
native={true}
delay={delay}
config={config}
from={{ opacity: 0 }}
to={{
opacity: 1
}}>
{style => <animated.div style={style}>{children}</animated.div>}
</Spring>
)
export function appear({
children,
className,
unless = false,
config = configPresets.default,
delay = 0,
style
}: Props & { unless?: boolean }) {
// TODO: We should rename this function Appear
// eslint-disable-next-line react-hooks/rules-of-hooks
const [show, setShow] = useState(unless)
// eslint-disable-next-line react-hooks/rules-of-hooks
useEffect(() => {
window.setTimeout(() => setShow(true), 0)
}, [])
return (
<Spring
delay={delay}
native
config={config}
to={{
opacity: show ? 1 : 0,
height: show ? 'auto' : '0px'
}}>
{animStyle => (
<animated.div style={{ ...style, ...animStyle }} className={className}>
{children}
</animated.div>
)}
</Spring>
)
}
export default {
appear,
fromBottom,
leftToRight,
fromTop,
fadeIn
}

View File

@ -1,5 +1,5 @@
import React from 'react'
import ReactMarkdown from 'react-markdown'
import ReactMarkdown, { ReactMarkdownProps } from 'react-markdown'
import { Link } from 'react-router-dom'
function LinkRenderer({ href, children }) {
@ -14,12 +14,19 @@ function LinkRenderer({ href, children }) {
}
}
interface MarkdownProps {
source: string
className?: string
renderers?: ReactMarkdownProps['renderers']
[other_props: string]: any
}
export const Markdown = ({
source,
className = '',
renderers = {},
otherProps
}) => (
}: MarkdownProps) => (
<ReactMarkdown
source={source}
className={`markdown ${className}`}

View File

@ -13,12 +13,12 @@ export const getInitialState = key => {
try {
return JSON.parse(safeLocalStorage.getItem(key))
} catch (e) {
console.warn(e);
return null;
console.warn(e)
return null
}
}
export const usePersistingState = (key, defaultState) => {
export const usePersistingState = (key: string, defaultState?: any) => {
const initialState = getInitialState(key)
return persistState(key)(
useState(initialState != null ? initialState : defaultState)

View File

@ -1,22 +1,6 @@
/* @flow */
import convert from 'color-convert'
import SetCssColour from 'Components/utils/SetCssColour'
import React, { Component, createContext } from 'react'
export type ThemeColours = {
colour: string,
textColour: string,
inverseTextColour: string,
lighterTextColour: string,
lighterInverseTextColour: string,
textColourOnWhite: string,
grayColour: string,
darkColour: string,
lightColour: string,
lighterColour: string,
lightestColour: string,
darkestColour: string
}
import React, { createContext } from 'react'
/*
Hex to RGB conversion:
@ -31,7 +15,7 @@ let cutHex = h => (h.charAt(0) == '#' ? h.substring(1, 7) : h),
Given a background color, should you write on it in black or white ?
Taken from http://stackoverflow.com/questions/3942878/how-to-decide-font-color-in-white-or-black-depending-on-background-color#comment61936401_3943023
*/
function findContrastedTextColour(color, simple) {
function findContrastedTextColour(color: string, simple: boolean) {
let r = hexToR(color),
g = hexToG(color),
b = hexToB(color)
@ -49,18 +33,21 @@ function findContrastedTextColour(color, simple) {
return L > 0.179 ? '#000000' : '#ffffff'
}
const lightenColour = (hex, x) => {
const lightenColour = (hex: string, x: number) => {
const [h, s, l] = convert.hex.hsl(hex.split('#')[1])
return '#' + convert.hsl.hex([h, s, Math.max(2, Math.min(l + x, 98))])
}
const generateDarkenVariations = (numberOfVariation, [h, s, l]) => {
const generateDarkenVariations = (
numberOfVariation: number,
[h, s, l]: [number, number, number]
) => {
return [...Array(numberOfVariation).keys()].map(
i => '#' + convert.hsl.hex([h, s, l * 0.8 ** i])
)
}
const deriveAnalogousPalettes = hex => {
const deriveAnalogousPalettes = (hex: string) => {
const [h, s, l] = convert.hex.hsl(hex.split('#')[1])
return [
generateDarkenVariations(4, [(h - 45) % 360, 0.75 * s, l]),
@ -68,7 +55,7 @@ const deriveAnalogousPalettes = hex => {
]
}
const generateTheme = (themeColour?: ?string): ThemeColours => {
const generateTheme = (themeColour?: string) => {
let // Use the default theme colour if the host page hasn't made a choice
colour = themeColour || '#2975D1',
lightColour = lightenColour(colour, 10),
@ -105,13 +92,13 @@ const generateTheme = (themeColour?: ?string): ThemeColours => {
}
}
export const ThemeColoursContext: React$Context<ThemeColours> = createContext(
generateTheme()
)
export type ThemeColours = ReturnType<typeof generateTheme>
export const ThemeColoursContext = createContext<ThemeColours>(generateTheme())
type ProviderProps = {
colour: string,
children: React$Node
colour: string
children: React.ReactNode
}
export function ThemeColoursProvider({ colour, children }: ProviderProps) {
@ -124,20 +111,26 @@ export function ThemeColoursProvider({ colour, children }: ProviderProps) {
)
}
export default function withThemeColours<Props: { colours: ThemeColours }> (
WrappedComponent: React$ComponentType<Props>
): React$ComponentType < $Diff < Props, { colours: ThemeColours } >> {
class WithThemeColours extends Component <
$Diff < Props, { colours: ThemeColours }>
> {
displayName = `withThemeColours(${WrappedComponent.displayName || ''})`
render() {
return (
<ThemeColoursContext.Consumer>
{colours => <WrappedComponent {...this.props} colours={colours} />}
</ThemeColoursContext.Consumer>
)
}
}
return WithThemeColours
interface WithColoursProps {
colours: ThemeColours
}
export default function withThemeColours<P extends object>(
WrappedComponent: React.ComponentType<P>
) {
class WithThemeColours extends React.Component<
Omit<P, keyof WithColoursProps>
> {
displayName = `withThemeColours(${WrappedComponent.displayName || ''})`
render() {
return (
<ThemeColoursContext.Consumer>
{colours => (
<WrappedComponent {...(this.props as P)} colours={colours} />
)}
</ThemeColoursContext.Consumer>
)
}
}
return WithThemeColours
}

View File

@ -1,34 +0,0 @@
/* @flow */
import React, { Component, createContext } from 'react'
import i18n from '../../i18n'
export const SitePathsContext: React$Context<SitePaths> = createContext({})
export const SitePathProvider = SitePathsContext.Provider
export default function withSitePaths<Props: { sitePaths: SitePaths }> (
WrappedComponent: React$ComponentType<Props>
): React$ComponentType < $Diff < Props, { sitePaths: SitePaths } >> {
class WithSitePaths extends Component <
$Diff < Props, { sitePaths: SitePaths }>
> {
displayName = `withSitePaths(${WrappedComponent.displayName || ''})`
constructor(props) {
super(props)
i18n.on('languageChanged', () => {
this.forceUpdate()
})
}
render() {
return (
<SitePathsContext.Consumer>
{sitePaths => (
<WrappedComponent {...this.props} sitePaths={sitePaths} />
)}
</SitePathsContext.Consumer>
)
}
}
return WithSitePaths
}
export type SitePaths = Object

View File

@ -0,0 +1,30 @@
import React, { createContext } from 'react'
import { SitePathsType } from 'sites/mon-entreprise.fr/sitePaths'
export const SitePathsContext = createContext<Partial<SitePathsType>>({})
export const SitePathProvider = SitePathsContext.Provider
export interface WithSitePathsProps {
sitePaths: SitePathsType
}
export default function withSitePaths<P extends object>(
WrappedComponent: React.ComponentType<P>
) {
class WithSitePaths extends React.Component<P & WithSitePathsProps> {
displayName = `withSitePaths(${WrappedComponent.displayName || ''})`
render() {
return (
<SitePathsContext.Consumer>
{sitePaths => (
<WrappedComponent {...(this.props as P)} sitePaths={sitePaths} />
)}
</SitePathsContext.Consumer>
)
}
}
return WithSitePaths
}
export type SitePaths = SitePathsType

View File

@ -1,19 +0,0 @@
/* @flow */
import React, { createContext } from 'react'
import Tracker, { devTracker } from '../../Tracker'
export const TrackerContext: React$Context<Tracker> = createContext(devTracker)
export const TrackerProvider = TrackerContext.Provider
export default function withTracker<Config: { tracker: Tracker }>(
Component: React$ComponentType<Config>
): React$ComponentType<$Diff<Config, { tracker: Tracker }>> {
return function ConnectTracker(props: $Diff<Config, { tracker: Tracker }>) {
return (
<TrackerContext.Consumer>
{(tracker: Tracker) => <Component {...props} tracker={tracker} />}
</TrackerContext.Consumer>
)
}
}

View File

@ -0,0 +1,19 @@
import React, { createContext } from 'react'
import Tracker, { devTracker } from '../../Tracker'
export const TrackerContext = createContext(devTracker)
export const TrackerProvider = TrackerContext.Provider
export interface WithTrackerProps {
tracker: Tracker
}
export default function withTracker(Component: React.ComponentType) {
return function ConnectTracker(props) {
return (
<TrackerContext.Consumer>
{(tracker: Tracker) => <Component {...props} tracker={tracker} />}
</TrackerContext.Consumer>
)
}
}

View File

@ -1,7 +1,7 @@
// This file exports the functions of the public computing library
import { safeLoad } from 'js-yaml'
import { collectDefaults, enrichRule, rulesFr } from './rules'
import { analyseMany, parseAll } from './traverse.js'
import { analyseMany, parseAll } from './traverse'
// The public evaluation function takes a nested object of input values
let inputToStateSelector = rules => input => dottedName =>

View File

@ -1,4 +1,3 @@
/* @flow */
import { omit } from 'ramda'
import { combineReducers } from 'redux'

View File

@ -1,4 +1,3 @@
/* @flow */
import { findRuleByDottedName } from 'Engine/rules'
import { compose, defaultTo, dissoc, identity, lensPath, omit, over, set, uniq, without } from 'ramda'

View File

@ -1,4 +1,3 @@
/* @flow */
import type { State } from 'Types/State'
import type { Action } from 'Types/ActionsTypes'

View File

@ -36,5 +36,6 @@ app.use(
app.use(require('webpack-hot-middleware')(compiler))
app.listen(8080, function() {
// eslint-disable-next-line no-console
console.log('Mon-entreprise listening on port 8080!\n')
})

View File

@ -1,40 +1,37 @@
import Route404 from 'Components/Route404'
import withSitePaths from 'Components/utils/withSitePaths'
import { rules as baseRulesEn, rulesFr as baseRulesFr } from 'Engine/rules'
import 'iframe-resizer'
import { compose } from 'ramda'
import createRavenMiddleware from 'raven-for-redux'
import Raven from 'raven-js'
import React, { useEffect } from 'react'
import { Helmet } from 'react-helmet'
import { useTranslation } from 'react-i18next'
import { Route, Switch } from 'react-router-dom'
import 'Ui/index.css'
import Provider from '../../Provider'
import { persistEverything, retrievePersistedState } from '../../storage/persistEverything'
import { persistSimulation, retrievePersistedSimulation } from '../../storage/persistSimulation'
import Tracker, { devTracker } from '../../Tracker'
import { getSessionStorage, inIframe } from '../../utils'
import './App.css'
import Footer from './layout/Footer/Footer'
import Header from './layout/Header'
import trackSimulatorActions from './middlewares/trackSimulatorActions'
import Créer from './pages/Créer'
import Couleur from './pages/Dev/Couleur'
import IntegrationTest from './pages/Dev/IntegrationTest'
import Personas from './pages/Dev/Personas'
import Sitemap from './pages/Dev/Sitemap'
import Documentation from './pages/Documentation'
import Gérer from './pages/Gérer'
import Iframes from './pages/Iframes'
import Integration from './pages/integration/index'
import Landing from './pages/Landing/Landing.js'
import Simulateurs from './pages/Simulateurs'
import ÉconomieCollaborative from './pages/ÉconomieCollaborative'
import redirects from './redirects'
import { constructLocalizedSitePath } from './sitePaths'
import Route404 from 'Components/Route404';
import { SitePathsContext } from 'Components/utils/withSitePaths';
import { rules as baseRulesEn, rulesFr as baseRulesFr } from 'Engine/rules';
import 'iframe-resizer';
import createRavenMiddleware from 'raven-for-redux';
import Raven from 'raven-js';
import React, { useContext, useEffect } from 'react';
import { Helmet } from 'react-helmet';
import { useTranslation } from 'react-i18next';
import { Route, Switch } from 'react-router-dom';
import 'Ui/index.css';
import Provider from '../../Provider';
import { persistEverything, retrievePersistedState } from '../../storage/persistEverything';
import { persistSimulation, retrievePersistedSimulation } from '../../storage/persistSimulation';
import Tracker, { devTracker } from '../../Tracker';
import { getSessionStorage, inIframe } from '../../utils';
import './App.css';
import Footer from './layout/Footer/Footer';
import Header from './layout/Header';
import trackSimulatorActions from './middlewares/trackSimulatorActions';
import Créer from './pages/Créer';
import Couleur from './pages/Dev/Couleur';
import IntegrationTest from './pages/Dev/IntegrationTest';
import Personas from './pages/Dev/Personas';
import Sitemap from './pages/Dev/Sitemap';
import Documentation from './pages/Documentation';
import Gérer from './pages/Gérer';
import Iframes from './pages/Iframes';
import Integration from './pages/integration/index';
import Landing from './pages/Landing/Landing';
import Simulateurs from './pages/Simulateurs';
import ÉconomieCollaborative from './pages/ÉconomieCollaborative';
import redirects from './redirects';
import { constructLocalizedSitePath } from './sitePaths';
if (process.env.NODE_ENV === 'production') {
Raven.config(
@ -54,7 +51,7 @@ const middlewares = [
function InFranceRoute({ basename, language }) {
useEffect(() => {
getSessionStorage() ?.setItem('lang', language)
getSessionStorage()?.setItem('lang', language)
}, [language])
const paths = constructLocalizedSitePath(language)
const rules = language === 'en' ? baseRulesEn : baseRulesFr
@ -92,8 +89,9 @@ let RouterSwitch = () => {
)
}
const App = compose(withSitePaths)(({ sitePaths }) => {
const App = () => {
const { t } = useTranslation()
const sitePaths = useContext(SitePathsContext)
return (
<div className="app-container">
<Helmet titleTemplate={`%s | ${t(['siteName', 'Mon-entreprise.fr'])}`} />
@ -104,18 +102,12 @@ const App = compose(withSitePaths)(({ sitePaths }) => {
<Switch>
{redirects}
<Route path={sitePaths.créer.index} component={Créer} />
<Route
path={sitePaths.gérer.index}
component={Gérer}
/>
<Route path={sitePaths.gérer.index} component={Gérer} />
<Route
path={sitePaths.économieCollaborative.index}
component={ÉconomieCollaborative}
/>
<Route
path={sitePaths.simulateurs.index}
component={Simulateurs}
/>
<Route path={sitePaths.simulateurs.index} component={Simulateurs} />
<Route
path={sitePaths.documentation.index}
component={Documentation}
@ -138,7 +130,7 @@ const App = compose(withSitePaths)(({ sitePaths }) => {
</div>
</div>
)
})
}
let ExportedApp = InFranceRoute

View File

@ -1,22 +1,18 @@
/* @flow */
import PageFeedback from 'Components/Feedback/PageFeedback'
import LegalNotice from 'Components/LegalNotice'
import NewsletterRegister from 'Components/NewsletterRegister'
import withSitePaths from 'Components/utils/withSitePaths'
import { SitePathsContext } from 'Components/utils/withSitePaths'
import { lensPath, view } from 'ramda'
import React from 'react'
import React, { useContext } from 'react'
import emoji from 'react-easy-emoji'
import { Helmet } from 'react-helmet'
import { Link } from 'react-router-dom'
import SocialIcon from 'Ui/SocialIcon'
import i18n from '../../../../i18n'
import { hrefLangLink } from '../../sitePaths'
import './Footer.css'
import { Link } from 'react-router-dom'
import Privacy from './Privacy'
type OwnProps = {}
const feedbackBlacklist = [
['index'],
['entreprise', 'statutJuridique', 'index'],
@ -26,14 +22,15 @@ const feedbackBlacklist = [
['simulateurs', 'salarié']
].map(lensPath)
const Footer = ({ sitePaths }) => {
const Footer = () => {
const sitePaths = useContext(SitePathsContext)
const hrefLink =
hrefLangLink[i18n.language][
decodeURIComponent(
(process.env.NODE_ENV === 'production'
? window.location.protocol + '//' + window.location.host
: '') + window.location.pathname
).replace(/\/$/, '')
decodeURIComponent(
(process.env.NODE_ENV === 'production'
? window.location.protocol + '//' + window.location.host
: '') + window.location.pathname
).replace(/\/$/, '')
] || []
return (
<div className="footer-container">
@ -108,8 +105,8 @@ const Footer = ({ sitePaths }) => {
) : hrefLang === 'en' ? (
<> Switch to English {emoji('🇬🇧')}</>
) : (
hrefLang
)}
hrefLang
)}
</a>
))}
</p>
@ -118,4 +115,4 @@ const Footer = ({ sitePaths }) => {
</div>
)
}
export default (withSitePaths(Footer): React$ComponentType<OwnProps>)
export default Footer

View File

@ -1,50 +0,0 @@
import { SitePathsContext } from 'Components/utils/withSitePaths';
import logoEnSvg from 'Images/logo-mycompany.svg';
import logoSvg from 'Images/logo.svg';
import marianneSvg from 'Images/marianne.svg';
import urssafSvg from 'Images/urssaf.svg';
import React, { useContext } from 'react';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
export default function Header() {
const sitePaths = useContext(SitePathsContext)
const { i18n: { language } } = useTranslation();
return (
<div className="ui__ container" style={{
display: 'flex',
alignItems: 'center'
}}
>
<Link style={{ height: '4rem' }} to={sitePaths.index}>
<img
alt="logo mon-entreprise.fr"
style={{
padding: '0.5rem 0',
height: '100%'
}}
src={language === 'fr' ? logoSvg : logoEnSvg}
/>
</Link>
<div style={{ flex: 1 }} />
<a
href="https://beta.gouv.fr"
target="_blank"
style={{
height: '4rem',
padding: '1rem'
}}>
<img alt="logo marianne" style={{ height: '100%' }} src={marianneSvg} />
</a>
<a
href="https://www.urssaf.fr"
target="_blank"
style={{
height: '4rem',
padding: '1rem'
}}
className="landing-header__institutional-logo">
<img alt="logo urssaf" style={{ height: '100%' }} src={urssafSvg} />
</a>
</div>)
}

View File

@ -0,0 +1,52 @@
import { SitePathsContext } from 'Components/utils/withSitePaths'
import logoEnSvg from 'Images/logo-mycompany.svg'
import logoSvg from 'Images/logo.svg'
import marianneSvg from 'Images/marianne.svg'
import urssafSvg from 'Images/urssaf.svg'
import React, { useContext } from 'react'
import { useTranslation } from 'react-i18next'
import { Link } from 'react-router-dom'
export default function Header() {
const sitePaths = useContext(SitePathsContext)
const { language } = useTranslation().i18n
return (
<div
className="ui__ container"
style={{
display: 'flex',
alignItems: 'center'
}}>
<Link style={{ height: '4rem' }} to={sitePaths.index}>
<img
alt="logo mon-entreprise.fr"
style={{
padding: '0.5rem 0',
height: '100%'
}}
src={language === 'fr' ? logoSvg : logoEnSvg}
/>
</Link>
<div style={{ flex: 1 }} />
<a
href="https://beta.gouv.fr"
target="_blank"
style={{
height: '4rem',
padding: '1rem'
}}>
<img alt="logo marianne" style={{ height: '100%' }} src={marianneSvg} />
</a>
<a
href="https://www.urssaf.fr"
target="_blank"
style={{
height: '4rem',
padding: '1rem'
}}
className="landing-header__institutional-logo">
<img alt="logo urssaf" style={{ height: '100%' }} src={urssafSvg} />
</a>
</div>
)
}

View File

@ -1,13 +1,10 @@
/* @flow */
import {
currentQuestionSelector,
situationSelector
} from 'Selectors/analyseSelectors'
import type { Tracker } from 'Components/utils/withTracker'
import Tracker from 'Tracker'
export default (tracker: Tracker) => {
// $FlowFixMe
return ({ getState }) => next => action => {
next(action)
const newState = getState()
@ -49,7 +46,7 @@ export default (tracker: Tracker) => {
...(action.type === 'UPDATE_PERIOD'
? ['période', action.toPeriod]
: [action.fieldName, action.value])
])
] as any)
}
if (action.type === 'START_CONVERSATION') {
tracker.push([

View File

@ -1,17 +1,18 @@
/* @flow */
import { React, T } from 'Components';
import { ScrollToTop } from 'Components/utils/Scroll';
import { SitePathsContext } from 'Components/utils/withSitePaths';
import { useContext } from 'react';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import { NavLink } from 'react-router-dom';
import Animate from 'Ui/animate';
import siret from './siret.jpg';
import { T } from 'Components'
import { ScrollToTop } from 'Components/utils/Scroll'
import { SitePathsContext } from 'Components/utils/withSitePaths'
import React, { useContext } from 'react'
import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import { NavLink } from 'react-router-dom'
import Animate from 'Ui/animate'
import siret from './siret.jpg'
export default function AfterRegistration() {
const sitePaths = useContext(SitePathsContext);
const statutChoisi = useSelector(state => state.inFranceApp.companyStatusChoice);
const sitePaths = useContext(SitePathsContext)
const statutChoisi = useSelector<any, any>(
state => state.inFranceApp.companyStatusChoice
)
const { t } = useTranslation()
const isAutoentrepreneur = statutChoisi.match('auto-entrepreneur')
@ -72,8 +73,7 @@ export default function AfterRegistration() {
).{' '}
<span
style={
statutChoisi &&
statutChoisi.match(/auto-entrepreneur|EI/)
statutChoisi && statutChoisi.match(/auto-entrepreneur|EI/)
? { display: 'none' }
: {}
}>
@ -90,32 +90,31 @@ export default function AfterRegistration() {
</p>
</T>
</p>
{statutChoisi &&
!statutChoisi.includes('auto-entrepreneur') && (
<>
<h2>
<T k="après.kbis.titre">Le Kbis</T>
</h2>
<p>
<T k="après.kbis.description.1">
C'est le document officiel qui atteste de{' '}
<strong>l'existence légale d'une entreprise commerciale</strong>
. Le plus souvent, pour être valable par les procédures
administratives, il doit dater de moins de 3 mois.{' '}
<a href="https://www.service-public.fr/professionnels-entreprises/vosdroits/F21000">
Plus d'infos.
</a>
</T>
</p>
<p>
<T k="après.kbis.description.2">
Ce document est généralement demandé lors de la candidature à un
appel d'offre public, de l'ouverture d'un compte bancaire,
d'achats d'équipement professionnel auprès de fournisseurs, etc.
</T>
</p>
</>
)}
{statutChoisi && !statutChoisi.includes('auto-entrepreneur') && (
<>
<h2>
<T k="après.kbis.titre">Le Kbis</T>
</h2>
<p>
<T k="après.kbis.description.1">
C'est le document officiel qui atteste de{' '}
<strong>l'existence légale d'une entreprise commerciale</strong>.
Le plus souvent, pour être valable par les procédures
administratives, il doit dater de moins de 3 mois.{' '}
<a href="https://www.service-public.fr/professionnels-entreprises/vosdroits/F21000">
Plus d'infos.
</a>
</T>
</p>
<p>
<T k="après.kbis.description.2">
Ce document est généralement demandé lors de la candidature à un
appel d'offre public, de l'ouverture d'un compte bancaire,
d'achats d'équipement professionnel auprès de fournisseurs, etc.
</T>
</p>
</>
)}
</Animate.fromBottom>
)
}

View File

@ -1,39 +1,33 @@
/* @flow */
import { checkCompanyCreationItem, initializeCompanyCreationChecklist } from 'Actions/companyCreationChecklistActions'
import {
checkCompanyCreationItem,
initializeCompanyCreationChecklist
} from 'Actions/companyCreationChecklistActions'
import { goToCompanyStatusChoice } from 'Actions/companyStatusActions'
import { React, T } from 'Components'
import { T } from 'Components'
import Scroll from 'Components/utils/Scroll'
import withSitePaths from 'Components/utils/withSitePaths'
import { compose } from 'ramda'
import { SitePathsContext } from 'Components/utils/withSitePaths'
import React, { useContext } from 'react'
import emoji from 'react-easy-emoji'
import { Helmet } from 'react-helmet'
import { useTranslation } from 'react-i18next'
import { connect } from 'react-redux'
import { connect, useSelector } from 'react-redux'
import { Link } from 'react-router-dom'
import * as Animate from 'Ui/animate'
import { CheckItem, Checklist } from 'Ui/Checklist'
import StatutDescription from './StatutDescription'
import type { LegalStatus } from 'Selectors/companyStatusSelectors'
import type { TFunction } from 'react-i18next'
type Props = {
statut: LegalStatus,
onChecklistInitialization: (string, Array<string>) => void,
onStatusChange: () => void,
sitePaths: Object,
onItemCheck: (name: string, checked: boolean) => void,
t: TFunction,
companyCreationChecklist: { [string]: boolean }
}
const CreateCompany = ({
function CreateCompany({
statut,
onChecklistInitialization,
onItemCheck,
sitePaths,
companyCreationChecklist,
onStatusChange
}: Props) => {
}) {
const { t, i18n } = useTranslation()
const sitePaths = useContext(SitePathsContext)
const companyCreationChecklist = useSelector<any, any>(
state => state.inFranceApp.companyCreationChecklist
)
// TODO : add this logic inside selector
const isAutoentrepreneur = statut.startsWith('auto-entrepreneur')
const multipleAssociates = ['SARL', 'SAS', 'SA'].includes(statut)
@ -41,23 +35,17 @@ const CreateCompany = ({
const titre = isAutoentrepreneur
? t(
[
'entreprise.page.autoEntrepreneur.titre',
'Devenir {{autoEntrepreneur}}'
],
{
autoEntrepreneur: statut
}
)
: t(
[
'entreprise.page.entreprise.titre',
'Créer une {{status}}'
],
{
[
'entreprise.page.autoEntrepreneur.titre',
'Devenir {{autoEntrepreneur}}'
],
{
autoEntrepreneur: statut
}
)
: t(['entreprise.page.entreprise.titre', 'Créer une {{status}}'], {
status: statut
}
)
})
return (
<Animate.fromBottom>
<Helmet>
@ -67,25 +55,27 @@ const CreateCompany = ({
content={
isAutoentrepreneur
? t(
[
'entreprise.page.autoEntrepreneur.description',
'La liste complète des démarches à faire pour devenir {{autoEntrepreneur}}.'
],
{ autoEntrepreneur: t(statut) }
)
[
'entreprise.page.autoEntrepreneur.description',
'La liste complète des démarches à faire pour devenir {{autoEntrepreneur}}.'
],
{ autoEntrepreneur: t(statut) }
)
: t(
[
'entreprise.page.description',
"La liste complète des démarches à faire pour créer une {{statut}} auprès de l'administration française."
],
{ statut: t(statut) }
)
[
'entreprise.page.description',
"La liste complète des démarches à faire pour créer une {{statut}} auprès de l'administration française."
],
{ statut: t(statut) }
)
}
/>
</Helmet>
<Scroll.toTop />
<div css="transform: translateY(2rem);">
<button onClick={onStatusChange} className="ui__ simple small push-left button">
<button
onClick={onStatusChange}
className="ui__ simple small push-left button">
<T k="entreprise.retour"> Choisir un autre statut</T>
</button>
</div>
@ -108,10 +98,8 @@ const CreateCompany = ({
</p>
<Checklist
key={statut}
onInitialization={items =>
onChecklistInitialization(statut, items)
}
onItemCheck={onItemCheck}
onInitialization={items => onChecklistInitialization(statut, items)}
onItemCheck={x => onItemCheck}
defaultChecked={companyCreationChecklist}>
<CheckItem
name="legalStatus"
@ -438,7 +426,9 @@ const CreateCompany = ({
}
/>
</Checklist>
<h2>{emoji('🧰')} <T>Ressources utiles</T></h2>
<h2>
{emoji('🧰')} <T>Ressources utiles</T>
</h2>
<div
css={`
display: flex;
@ -448,78 +438,94 @@ const CreateCompany = ({
flex: 1;
}
`}>
{isAutoentrepreneur && <Link
className="ui__ interactive card button-choice lighter-bg"
to={{ pathname: sitePaths.simulateurs['auto-entrepreneur'], state: { fromCréer: true } }}>
<T k="entreprise.ressources.simu.autoEntrepreneur"><p>Simulateur de revenus auto-entrepreneur</p>
<small>
Simuler le montant de vos cotisations sociales et de votre impôt et estimez votre futur revenu net.
</small>
</T>
</Link>
}
{['EI', 'EIRL', 'EURL'].includes(statut) && <Link
className="ui__ interactive card button-choice lighter-bg"
to={{ pathname: sitePaths.simulateurs.indépendant, state: { fromCréer: true } }}>
<T k="entreprise.ressources.simu.indépendant">
<p>Simulateur de cotisations indépendant</p>
<small>
Simuler le montant de vos cotisations sociales pour bien préparer votre business plan.
</small></T>
</Link>
}
{['SAS', 'SASU'].includes(statut) && <Link
className="ui__ interactive card button-choice lighter-bg"
to={{ pathname: sitePaths.simulateurs['assimilé-salarié'], state: { fromCréer: true } }}>
<T k="entreprise.ressources.simu.assimilé">
<p>Simulateur de cotisations assimilé-salarié</p>
<small>
Simuler le montant de vos cotisations sociales pour bien préparer votre business plan.
</small>
</T>
</Link>
}
{isAutoentrepreneur && (
<Link
className="ui__ interactive card button-choice lighter-bg"
to={{
pathname: sitePaths.simulateurs['auto-entrepreneur'],
state: { fromCréer: true }
}}>
<T k="entreprise.ressources.simu.autoEntrepreneur">
<p>Simulateur de revenus auto-entrepreneur</p>
<small>
Simuler le montant de vos cotisations sociales et de votre impôt
et estimez votre futur revenu net.
</small>
</T>
</Link>
)}
{['EI', 'EIRL', 'EURL'].includes(statut) && (
<Link
className="ui__ interactive card button-choice lighter-bg"
to={{
pathname: sitePaths.simulateurs.indépendant,
state: { fromCréer: true }
}}>
<T k="entreprise.ressources.simu.indépendant">
<p>Simulateur de cotisations indépendant</p>
<small>
Simuler le montant de vos cotisations sociales pour bien
préparer votre business plan.
</small>
</T>
</Link>
)}
{['SAS', 'SASU'].includes(statut) && (
<Link
className="ui__ interactive card button-choice lighter-bg"
to={{
pathname: sitePaths.simulateurs['assimilé-salarié'],
state: { fromCréer: true }
}}>
<T k="entreprise.ressources.simu.assimilé">
<p>Simulateur de cotisations assimilé-salarié</p>
<small>
Simuler le montant de vos cotisations sociales pour bien
préparer votre business plan.
</small>
</T>
</Link>
)}
<Link
className="ui__ interactive card button-choice lighter-bg"
to={sitePaths.créer.après}>
<T k="entreprise.ressources.après">
<p>Après la création</p>
<small>
SIREN, SIRET, code APE, KBis. Un petit glossaire des termes que vous pourrez (éventuellement) rencontrer après la création.
SIREN, SIRET, code APE, KBis. Un petit glossaire des termes que
vous pourrez (éventuellement) rencontrer après la création.
</small>
</T>
</Link>
{i18n.language === 'fr' && (<a
target="_blank"
className="ui__ interactive card button-choice lighter-bg"
href="https://www.urssaf.fr/portail/files/live/sites/urssaf/files/documents/SSI-Guide-Objectif-Entreprise.pdf">
<p>Guide de création URSSAF </p>
<small>
Des conseils sur comment
préparer son projet pour se lancer dans la création et une
présentation détaillée de votre protection sociale.
</small><br />
<div css="text-align: right">
<small className="ui__ label">PDF</small>
</div>
</a>)}
{i18n.language === 'fr' && (
<a
target="_blank"
className="ui__ interactive card button-choice lighter-bg"
href="https://www.urssaf.fr/portail/files/live/sites/urssaf/files/documents/SSI-Guide-Objectif-Entreprise.pdf">
<p>Guide de création URSSAF </p>
<small>
Des conseils sur comment préparer son projet pour se lancer dans
la création et une présentation détaillée de votre protection
sociale.
</small>
<br />
<div css="text-align: right">
<small className="ui__ label">PDF</small>
</div>
</a>
)}
</div>
</Animate.fromBottom >
</Animate.fromBottom>
)
}
export default compose(
withSitePaths,
connect(
state => ({
companyCreationChecklist: state.inFranceApp.companyCreationChecklist,
}),
{
onChecklistInitialization: initializeCompanyCreationChecklist,
onItemCheck: checkCompanyCreationItem,
onStatusChange: goToCompanyStatusChoice
}
)
export default connect(
null,
{
onChecklistInitialization: initializeCompanyCreationChecklist,
onItemCheck: checkCompanyCreationItem,
onStatusChange: goToCompanyStatusChoice
}
)(CreateCompany)
let StatutsExample = ({ statut }) => {

View File

@ -1,72 +0,0 @@
/* @flow */
import { isAutoentrepreneur } from 'Actions/companyStatusActions'
import { React, T } from 'Components'
import SchemeComparaison from 'Components/SchemeComparaison'
import { compose } from 'ramda'
import { Helmet } from 'react-helmet'
import { withTranslation } from 'react-i18next'
import { connect } from 'react-redux'
import type { TFunction } from 'react-i18next'
type Props = {
isAutoentrepreneur: (?boolean) => void,
t: TFunction
}
const Autoentrepreneur = ({ isAutoentrepreneur, t }: Props) => (
<>
<Helmet>
<title>{t('autoentrepreneur.page.titre', 'Auto-entrepreneur')}</title>
<meta
name="description"
content={
<T k="autoentrepreneur.page.description">
Un auto-entrepreneur bénéficie d'un système simplifié de déclaration
et de paiement, pour lesquelles les impôts et cotisations sociales
sont basés sur le chiffre d'affaires réalisé chaque mois. C'est un
choix intéressant si vous n'avez pas besoin de beaucoup de capital
et que vous souhaitez démarrer rapidement.
</T>
}
/>
</Helmet>
<h2>
<T k="autoentrepreneur.titre">
Entreprise individuelle ou auto-entrepreneur
</T>
</h2>
<T k="autoentrepreneur.description">
<p>
À la différence de l'entreprise individuelle, l'auto-entrepreneur
bénéficie d'un régime simplifié de déclaration et de paiement : les
cotisations sociales et l'impôt sur le revenu sont calculés sur le
chiffre d'affaires encaissé.
</p>
<p>
<strong>Note</strong> : Certaines activités sont exclues de ce statut (
<a href="https://www.afecreation.fr/pid10375/pour-quelles-activites.html#principales-exclusions">
{' '}
voir la liste
</a>
). Certaines activités sont réglementées avec une qualification ou une
expérience professionnelle (
<a href="https://www.afecreation.fr/pid316/activites-reglementees.html">
voir la liste
</a>
).
</p>
</T>
<div className="ui__ full-width">
<SchemeComparaison hideAssimiléSalarié />
</div>
</>
)
export default compose(
withTranslation(),
connect(
null,
{ isAutoentrepreneur }
)
)(Autoentrepreneur)

View File

@ -0,0 +1,53 @@
import { T } from 'Components'
import SchemeComparaison from 'Components/SchemeComparaison'
import React from 'react'
import { Helmet } from 'react-helmet'
import { useTranslation } from 'react-i18next'
export default function Autoentrepreneur() {
const { t } = useTranslation()
return (
<>
<Helmet>
<title>{t('autoentrepreneur.page.titre', 'Auto-entrepreneur')}</title>
<meta
name="description"
content={t(
'autoentrepreneur.page.description',
"Un auto-entrepreneur bénéficie d'un système simplifié de déclaration et de paiement, pour lesquelles les impôts et cotisations sociales sont basés sur le chiffre d'affaires réalisé chaque mois. C'est un choix intéressant si vous n'avez pas besoin de beaucoup de capital et que vous souhaitez démarrer rapidement."
)}
/>
</Helmet>
<h2>
<T k="autoentrepreneur.titre">
Entreprise individuelle ou auto-entrepreneur
</T>
</h2>
<T k="autoentrepreneur.description">
<p>
À la différence de l'entreprise individuelle, l'auto-entrepreneur
bénéficie d'un régime simplifié de déclaration et de paiement : les
cotisations sociales et l'impôt sur le revenu sont calculés sur le
chiffre d'affaires encaissé.
</p>
<p>
<strong>Note</strong> : Certaines activités sont exclues de ce statut
(
<a href="https://www.afecreation.fr/pid10375/pour-quelles-activites.html#principales-exclusions">
{' '}
voir la liste
</a>
). Certaines activités sont réglementées avec une qualification ou une
expérience professionnelle (
<a href="https://www.afecreation.fr/pid316/activites-reglementees.html">
voir la liste
</a>
).
</p>
</T>
<div className="ui__ full-width">
<SchemeComparaison hideAssimiléSalarié />
</div>
</>
)
}

View File

@ -1,19 +1,10 @@
/* @flow */
import { defineDirectorStatus } from 'Actions/companyStatusActions'
import { React, T } from 'Components'
import { T } from 'Components'
import SchemeComparaison from 'Components/SchemeComparaison'
import { compose } from 'ramda'
import React from 'react'
import { Helmet } from 'react-helmet'
import { useTranslation } from 'react-i18next'
import { connect } from 'react-redux'
import type { DirectorStatus } from 'Types/companyTypes'
type Props = {
defineDirectorStatus: (?DirectorStatus) => void,
sitePaths: Object
}
const DefineDirectorStatus = ({ defineDirectorStatus }: Props) => {
export default function DefineDirectorStatus() {
const { t } = useTranslation()
return (
<>
@ -25,7 +16,7 @@ const DefineDirectorStatus = ({ defineDirectorStatus }: Props) => {
name="description"
content={t(
'statut du dirigeant.page.description',
'Ce choix est important parce qu\'il détermine le régime de sécurité sociale et la couverture sociale de l\'administrateur. Chaque option a des implications juridiques et conduit à un statut différent lors de la création de votre entreprise.'
"Ce choix est important parce qu'il détermine le régime de sécurité sociale et la couverture sociale de l'administrateur. Chaque option a des implications juridiques et conduit à un statut différent lors de la création de votre entreprise."
)}
/>
</Helmet>
@ -45,10 +36,3 @@ const DefineDirectorStatus = ({ defineDirectorStatus }: Props) => {
</>
)
}
export default compose(
connect(
null,
{ defineDirectorStatus }
)
)(DefineDirectorStatus)

View File

@ -1,18 +1,13 @@
/* @flow */
import { directorIsInAMinority } from 'Actions/companyStatusActions';
import { React, T } from 'Components';
import { compose } from 'ramda';
import { Helmet } from 'react-helmet';
import { withTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import type { TFunction } from 'react-i18next'
import { directorIsInAMinority } from 'Actions/companyStatusActions'
import { T } from 'Components'
import React from 'react'
import { Helmet } from 'react-helmet'
import { useTranslation } from 'react-i18next'
import { useDispatch } from 'react-redux'
type Props = {
directorIsInAMinority: (?boolean) => void,
t: TFunction
}
const MinorityDirector = ({ directorIsInAMinority, t }: Props) => {
export default function MinorityDirector() {
const { t } = useTranslation()
const dispatch = useDispatch()
return (
<>
<Helmet>
@ -26,7 +21,7 @@ const MinorityDirector = ({ directorIsInAMinority, t }: Props) => {
name="description"
content={t(
'gérant minoritaire.page.description',
'Certaines règles particulières s\'appliquent en fonction du nombre d\'actions détenues par l\'administrateur, ce qui peut conduire à un statut différent lors de la création de votre société'
"Certaines règles particulières s'appliquent en fonction du nombre d'actions détenues par l'administrateur, ce qui peut conduire à un statut différent lors de la création de votre société"
)}
/>
</Helmet>
@ -40,12 +35,14 @@ const MinorityDirector = ({ directorIsInAMinority, t }: Props) => {
</p>
<ul>
<li>
<strong>Gérant majoritaire</strong> : Vous êtes l'administrateur majoritaire
(ou faite partie d'un conseil d'administration majoritaire).
</li>
<strong>Gérant majoritaire</strong> : Vous êtes l'administrateur
majoritaire (ou faite partie d'un conseil d'administration
majoritaire).
</li>
<li>
<strong>Gérant minoritaire</strong> : Vous êtes administrateur minoritaire ou égalitaire (ou faites partie d'un conseil d'administration
minoritaire ou égalitaire).
<strong>Gérant minoritaire</strong> : Vous êtes administrateur
minoritaire ou égalitaire (ou faites partie d'un conseil
d'administration minoritaire ou égalitaire).
</li>
</ul>
</T>
@ -53,14 +50,14 @@ const MinorityDirector = ({ directorIsInAMinority, t }: Props) => {
<div className="ui__ answer-group">
<button
onClick={() => {
directorIsInAMinority(false)
dispatch(directorIsInAMinority(false))
}}
className="ui__ button">
<T>Gérant majoritaire</T>
</button>
<button
onClick={() => {
directorIsInAMinority(true)
dispatch(directorIsInAMinority(true))
}}
className="ui__ button">
<T>Gérant minoritaire</T>
@ -69,11 +66,3 @@ const MinorityDirector = ({ directorIsInAMinority, t }: Props) => {
</>
)
}
export default compose(
withTranslation(),
connect(
null,
{ directorIsInAMinority }
)
)(MinorityDirector)

View File

@ -1,75 +0,0 @@
/* @flow */
import { companyHasMultipleAssociates } from 'Actions/companyStatusActions'
import { React, T } from 'Components'
import { compose } from 'ramda'
import { Helmet } from 'react-helmet'
import { withTranslation } from 'react-i18next'
import { connect } from 'react-redux'
import type { TFunction } from 'react-i18next'
type Props = {
companyHasMultipleAssociates: (?boolean) => void,
t: TFunction
}
const NumberOfAssociates = ({ companyHasMultipleAssociates, t }: Props) => (
<>
<Helmet>
<title>
{t(
'associés.page.titre',
"Nombre d'associés pour créer une entreprise"
)}
</title>
<meta
name="description"
content={t(
'associés.page.description',
"Découvrez quels status choisir en fonction du nombre d'associés participant à la création de l'entreprise."
)}
/>
</Helmet>
<h2>
<T k="associés.titre">Seul ou à plusieurs</T>
</h2>
<T k="associés.description">
<p>
Une entreprise avec un seul associé est plus simple à créer et gérer. Un
associé peut-être une personne physique (un individu) ou une personne
morale (par exemple une société).
</p>
<p>
Note : ce choix n'est pas définitif. Vous pouvez tout à fait commencer
votre société seul, et accueillir de nouveaux associés au cours de votre
développement.
</p>
</T>
<div className="ui__ answer-group">
<button
onClick={() => {
companyHasMultipleAssociates(false)
}}
className="ui__ button">
<T k="associés.choix1">Seul</T>
</button>
<button
onClick={() => {
companyHasMultipleAssociates(true)
}}
className="ui__ button">
<T k="associés.choix2">Plusieurs personnes</T>
</button>
</div>
</>
)
export default compose(
withTranslation(),
connect(
null,
{ companyHasMultipleAssociates }
)
)(NumberOfAssociates)

View File

@ -0,0 +1,66 @@
import { companyHasMultipleAssociates } from 'Actions/companyStatusActions'
import { T } from 'Components'
import React from 'react'
import { Helmet } from 'react-helmet'
import { useTranslation } from 'react-i18next'
import { connect } from 'react-redux'
const NumberOfAssociates = ({ companyHasMultipleAssociates }) => {
const { t } = useTranslation()
return (
<>
<Helmet>
<title>
{t(
'associés.page.titre',
"Nombre d'associés pour créer une entreprise"
)}
</title>
<meta
name="description"
content={t(
'associés.page.description',
"Découvrez quels status choisir en fonction du nombre d'associés participant à la création de l'entreprise."
)}
/>
</Helmet>
<h2>
<T k="associés.titre">Seul ou à plusieurs</T>
</h2>
<T k="associés.description">
<p>
Une entreprise avec un seul associé est plus simple à créer et gérer.
Un associé peut-être une personne physique (un individu) ou une
personne morale (par exemple une société).
</p>
<p>
Note : ce choix n'est pas définitif. Vous pouvez tout à fait commencer
votre société seul, et accueillir de nouveaux associés au cours de
votre développement.
</p>
</T>
<div className="ui__ answer-group">
<button
onClick={() => {
companyHasMultipleAssociates(false)
}}
className="ui__ button">
<T k="associés.choix1">Seul</T>
</button>
<button
onClick={() => {
companyHasMultipleAssociates(true)
}}
className="ui__ button">
<T k="associés.choix2">Plusieurs personnes</T>
</button>
</div>
</>
)
}
export default connect(
null,
{ companyHasMultipleAssociates }
)(NumberOfAssociates)

View File

@ -1,43 +1,34 @@
/* @flow */
import { React, T } from 'Components'
import withSitePaths from 'Components/utils/withSitePaths'
import { compose, filter } from 'ramda'
import { T } from 'Components'
import { SitePathsContext } from 'Components/utils/withSitePaths'
import { filter } from 'ramda'
import React, { useContext } from 'react'
import { Helmet } from 'react-helmet'
import { useTranslation } from 'react-i18next'
import { connect } from 'react-redux'
import { useSelector } from 'react-redux'
import { Link } from 'react-router-dom'
import { possibleStatusSelector } from 'Selectors/companyStatusSelectors'
import {
LegalStatus,
possibleStatusSelector
} from 'Selectors/companyStatusSelectors'
import StatutDescription from '../StatutDescription'
import type { RouterHistory } from 'react-router'
import type { LegalStatus } from 'Selectors/companyStatusSelectors'
type Props = {
history: RouterHistory,
possibleStatus: { [LegalStatus]: boolean },
goBackToPreviousQuestion: () => void,
sitePaths: Object,
setMainStatus: LegalStatus => void,
language: string
const StatutButton = ({ statut }: { statut: LegalStatus }) => {
const sitePaths = useContext(SitePathsContext)
const { t } = useTranslation()
return (
<div className="ui__ answer-group">
<Link to={sitePaths.créer[statut]} className="ui__ button">
{statut.includes('auto-entrepreneur') ? (
<T>Devenir</T>
) : (
<T>Créer une</T>
)}{' '}
{t(statut)}
</Link>
</div>
)
}
const StatutButton = withSitePaths(
({ statut, sitePaths }: { statut: LegalStatus, sitePaths: Object }) => {
const { t } = useTranslation()
return (
<div className="ui__ answer-group">
<Link to={sitePaths.créer[statut]} className="ui__ button">
{statut.includes('auto-entrepreneur') ? (
<T>Devenir</T>
) : (
<T>Créer une</T>
)}{' '}
{t(statut)}
</Link>
</div>
)
}
)
const StatutTitle = ({ statut, language }) =>
statut === 'EI' ? (
<>
@ -85,11 +76,9 @@ const StatutTitle = ({ statut, language }) =>
</>
) : null
const SetMainStatus = ({
history,
possibleStatus,
}: Props) => {
export default function SetMainStatus() {
const { t, i18n } = useTranslation()
const possibleStatus = useSelector(possibleStatusSelector)
return (
<>
<Helmet>
@ -101,30 +90,28 @@ const SetMainStatus = ({
</title>
</Helmet>
<h2>
{Object.keys(possibleStatus).every(Boolean) ? <T> Liste des statuts juridiques </T> : <T>Votre forme juridique</T>}
{Object.keys(possibleStatus).every(Boolean) ? (
<T> Liste des statuts juridiques </T>
) : (
<T>Votre forme juridique</T>
)}
</h2>
<ul>
{Object.keys(filter(Boolean, possibleStatus)).map(statut => (
<li key={statut}>
<strong>
<StatutTitle statut={statut} language={i18n.language} />
</strong>{' '}
<p>
<StatutDescription statut={statut} />
</p>
<StatutButton statut={statut} history={history} />
</li>
))}
{Object.keys(filter(Boolean, possibleStatus as any)).map(
(statut: keyof typeof possibleStatus) => (
<li key={statut}>
<strong>
<StatutTitle statut={statut} language={i18n.language} />
</strong>{' '}
<p>
<StatutDescription statut={statut} />
</p>
<StatutButton statut={statut} />
</li>
)
)}
</ul>
</>
)
}
export default compose(
withSitePaths,
connect(
state => ({ possibleStatus: possibleStatusSelector(state) }),
)
)(SetMainStatus)

View File

@ -1,62 +0,0 @@
/* @flow */
import { React, T } from 'Components'
import withSitePaths from 'Components/utils/withSitePaths'
import { compose, isNil } from 'ramda'
import { connect } from 'react-redux'
import { Link } from 'react-router-dom'
import type { LegalStatusRequirements } from 'Types/companyTypes'
const requirementToText = (key, value) => {
switch (key) {
case 'multipleAssociates':
return value ? <T>Plusieurs associés</T> : <T>Un seul associé</T>
case 'soleProprietorship':
return value ? (
<T T k="responsabilité.bouton2">
Entreprise individuelle
</T>
) : (
<T k="responsabilité.bouton1">Société</T>
)
case 'directorStatus':
return value === 'SELF_EMPLOYED' ? (
<T>Indépendant</T>
) : (
<T>Assimilé salarié</T>
)
case 'autoEntrepreneur':
return value ? <T>Auto-entrepreneur</T> : <T>Pas en auto-entrepreneur</T>
case 'minorityDirector':
return value ? <T>Gérant minoritaire</T> : <T>Gérant majoritaire</T>
}
}
type OwnProps = {}
type Props = LegalStatusRequirements & {
goToCompanyStatusChoice: () => void,
sitePaths: Object
}
const PreviousAnswers = ({
sitePaths,
legalStatus
}: Props) => !!Object.values(legalStatus).length && (
<ul css="margin-bottom: -1rem;">
{Object.entries(legalStatus).map(
([key, value]) =>
!isNil(value) && (
<li key={key}>
<Link to={sitePaths.créer.guideStatut[key]}>
{requirementToText(key, value)}
</Link>
</li>
)
)}
</ul>
)
export default (compose(
connect(
state => ({ legalStatus: state.inFranceApp.companyLegalStatus }),
),
withSitePaths
)(PreviousAnswers): React$ComponentType<OwnProps>)

View File

@ -0,0 +1,58 @@
import { T } from 'Components'
import { SitePathsContext } from 'Components/utils/withSitePaths'
import { isNil } from 'ramda'
import React, { useContext } from 'react'
import { useSelector } from 'react-redux'
import { Link } from 'react-router-dom'
import { LegalStatusRequirements } from 'Types/companyTypes'
const requirementToText = (
key: keyof LegalStatusRequirements,
value: string
) => {
switch (key) {
case 'multipleAssociates':
return value ? <T>Plusieurs associés</T> : <T>Un seul associé</T>
case 'soleProprietorship':
return value ? (
<T T k="responsabilité.bouton2">
Entreprise individuelle
</T>
) : (
<T k="responsabilité.bouton1">Société</T>
)
case 'directorStatus':
return value === 'SELF_EMPLOYED' ? (
<T>Indépendant</T>
) : (
<T>Assimilé salarié</T>
)
case 'autoEntrepreneur':
return value ? <T>Auto-entrepreneur</T> : <T>Pas en auto-entrepreneur</T>
case 'minorityDirector':
return value ? <T>Gérant minoritaire</T> : <T>Gérant majoritaire</T>
}
}
export default function PreviousAnswers() {
const sitePaths = useContext(SitePathsContext)
const legalStatus = useSelector<any, any>(
state => state.inFranceApp.companyLegalStatus
)
return (
!!Object.values(legalStatus).length && (
<ul css="margin-bottom: -1rem;">
{Object.entries(legalStatus).map(
([key, value]) =>
!isNil(value) && (
<li key={key}>
<Link to={sitePaths.créer.guideStatut[key]}>
{requirementToText(key as any, value as any)}
</Link>
</li>
)
)}
</ul>
)
)
}

View File

@ -1,16 +1,12 @@
/* @flow */
import { isSoleProprietorship } from 'Actions/companyStatusActions'
import { React, T } from 'Components'
import { T } from 'Components'
import { compose } from 'ramda'
import React from 'react'
import { Helmet } from 'react-helmet'
import { useTranslation } from 'react-i18next'
import { connect } from 'react-redux'
type Props = {
isSoleProprietorship: (?boolean) => void
}
const SoleProprietorship = ({ isSoleProprietorship }: Props) => {
const SoleProprietorship = ({ isSoleProprietorship }) => {
const { t } = useTranslation()
return (
<>

View File

@ -1,91 +0,0 @@
/* @flow */
import { resetCompanyStatusChoice } from 'Actions/companyStatusActions';
import { T } from 'Components';
import { SitePathsContext } from 'Components/utils/withSitePaths';
import { toPairs } from 'ramda';
import React, { useContext, useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { NavLink, Route, Switch, useLocation } from 'react-router-dom';
import Animate from 'Ui/animate';
import AutoEntrepreneur from './AutoEntrepreneur';
import DirectorStatus from './DirectorStatus';
import MinorityDirector from './MinorityDirector';
import NumberOfAssociate from './NumberOfAssociate';
import PickLegalStatus from './PickLegalStatus';
import PreviousAnswers from './PreviousAnswers';
import SoleProprietorship from './SoleProprietorship';
const useResetFollowingAnswers = () => {
const dispatch = useDispatch();
const sitePaths = useContext(SitePathsContext);
const location = useLocation();
useEffect(() => {
const companyStatusCurrentQuestionName = (toPairs(
sitePaths.créer.guideStatut
).find(([, pathname]) => location.pathname === pathname) || [])[0]
if (!companyStatusCurrentQuestionName) {
return
}
dispatch(resetCompanyStatusChoice(companyStatusCurrentQuestionName))
}, [location.pathname, dispatch, sitePaths.créer.guideStatut])
}
export default function Créer() {
const sitePaths = useContext(SitePathsContext);
const location = useLocation();
useResetFollowingAnswers();
return (
<>
<div css="transform: translateY(2rem)">
<NavLink
to={sitePaths.créer.index}
exact
activeClassName="ui__ hide"
className="ui__ simple push-left small button">
<T>Retour</T>
</NavLink>
</div>
<h1>
<T k="formeJuridique.titre">Choix du statut juridique</T>
</h1>
<PreviousAnswers />
<Animate.fromBottom key={location.pathname}>
<Switch>
<Route
path={sitePaths.créer.guideStatut.soleProprietorship}
>
<SoleProprietorship />
</Route>
<Route
path={sitePaths.créer.guideStatut.directorStatus}
>
<DirectorStatus />
</Route>
<Route
path={sitePaths.créer.guideStatut.autoEntrepreneur}
>
<AutoEntrepreneur />
</Route>
<Route
path={sitePaths.créer.guideStatut.multipleAssociates}
>
<NumberOfAssociate />
</Route>
<Route
path={sitePaths.créer.guideStatut.minorityDirector}
>
<MinorityDirector />
</Route>
<Route
path={sitePaths.créer.guideStatut.liste}
>
<PickLegalStatus />
</Route>
</Switch>
</Animate.fromBottom>
</>
)
}

View File

@ -0,0 +1,75 @@
import { resetCompanyStatusChoice } from 'Actions/companyStatusActions'
import { T } from 'Components'
import Animate from 'Ui/animate'
import { SitePathsContext } from 'Components/utils/withSitePaths'
import { toPairs } from 'ramda'
import React, { useContext, useEffect } from 'react'
import { useDispatch } from 'react-redux'
import { NavLink, Route, Switch, useLocation } from 'react-router-dom'
import AutoEntrepreneur from './AutoEntrepreneur'
import DirectorStatus from './DirectorStatus'
import MinorityDirector from './MinorityDirector'
import NumberOfAssociate from './NumberOfAssociate'
import PickLegalStatus from './PickLegalStatus'
import PreviousAnswers from './PreviousAnswers'
import SoleProprietorship from './SoleProprietorship'
const useResetFollowingAnswers = () => {
const dispatch = useDispatch()
const sitePaths = useContext(SitePathsContext)
const location = useLocation()
useEffect(() => {
const companyStatusCurrentQuestionName = (toPairs(
sitePaths.créer.guideStatut
).find(([, pathname]) => location.pathname === pathname) || [])[0]
if (!companyStatusCurrentQuestionName) {
return
}
dispatch(resetCompanyStatusChoice(companyStatusCurrentQuestionName))
}, [location.pathname, dispatch, sitePaths.créer.guideStatut])
}
export default function Créer() {
const sitePaths = useContext(SitePathsContext)
const location = useLocation()
useResetFollowingAnswers()
return (
<>
<div css="transform: translateY(2rem)">
<NavLink
to={sitePaths.créer.index}
exact
activeClassName="ui__ hide"
className="ui__ simple push-left small button">
<T>Retour</T>
</NavLink>
</div>
<h1>
<T k="formeJuridique.titre">Choix du statut juridique</T>
</h1>
<PreviousAnswers />
<Animate.fromBottom key={location.pathname}>
<Switch>
<Route path={sitePaths.créer.guideStatut.soleProprietorship}>
<SoleProprietorship />
</Route>
<Route path={sitePaths.créer.guideStatut.directorStatus}>
<DirectorStatus />
</Route>
<Route path={sitePaths.créer.guideStatut.autoEntrepreneur}>
<AutoEntrepreneur />
</Route>
<Route path={sitePaths.créer.guideStatut.multipleAssociates}>
<NumberOfAssociate />
</Route>
<Route path={sitePaths.créer.guideStatut.minorityDirector}>
<MinorityDirector />
</Route>
<Route path={sitePaths.créer.guideStatut.liste}>
<PickLegalStatus />
</Route>
</Switch>
</Animate.fromBottom>
</>
)
}

View File

@ -1,6 +1,4 @@
/* @flow */
import { T } from 'Components'
import Animate from 'Components/ui/animate'
import { SitePathsContext } from 'Components/utils/withSitePaths'
import React, { useContext } from 'react'
import { Helmet } from 'react-helmet'
@ -8,21 +6,22 @@ import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import { Link } from 'react-router-dom'
import { nextQuestionUrlSelector } from 'Selectors/companyStatusSelectors'
import Animate from 'Ui/animate'
import créerSvg from './créer.svg'
export default function Créer() {
const { t } = useTranslation()
const sitePaths = useContext(SitePathsContext);
const nextQuestionUrl = useSelector(state => nextQuestionUrlSelector(state, { sitePaths }));
const guideAlreadyStarted = useSelector(state => !!Object.keys(state.inFranceApp.companyLegalStatus)
.length);
const sitePaths = useContext(SitePathsContext)
const nextQuestionUrl = useSelector(state =>
nextQuestionUrlSelector(state, { sitePaths })
)
const guideAlreadyStarted = useSelector<any, any>(
state => !!Object.keys(state.inFranceApp.companyLegalStatus).length
)
return (
<Animate.fromBottom>
<Helmet>
<title>{t(
'créer.titre',
'Créer une entreprise'
)}</title>
<title>{t('créer.titre', 'Créer une entreprise')}</title>
</Helmet>
<h1>
@ -30,18 +29,30 @@ export default function Créer() {
</h1>
<div css="display: flex; align-items: flex-start; justify-content: space-between">
<div>
<p className='ui__ lead'>
<p className="ui__ lead">
<T k="créer.description">
Avant d'entamer les démarches administratives pour créer votre entreprise, vous devez choisir un statut juridique adapté à votre activité
Avant d'entamer les démarches administratives pour créer votre
entreprise, vous devez choisir un statut juridique adapté à votre
activité
</T>
</p>
<Link
className="ui__ button plain cta"
to={guideAlreadyStarted && nextQuestionUrl ? nextQuestionUrl : sitePaths.créer.guideStatut.multipleAssociates}>
{!guideAlreadyStarted ? t('créer.cta.default', 'Trouver le bon statut') : t('créer.cta.continue', 'Continuer le guide')}
to={
guideAlreadyStarted && nextQuestionUrl
? nextQuestionUrl
: sitePaths.créer.guideStatut.multipleAssociates
}>
{!guideAlreadyStarted
? t('créer.cta.default', 'Trouver le bon statut')
: t('créer.cta.continue', 'Continuer le guide')}
</Link>
<p className="ui__ notice"><T k="créer.warningPL">Le cas des professions libérales réglementées n'est pas encore traité</T></p>
<p className="ui__ notice">
<T k="créer.warningPL">
Le cas des professions libérales réglementées n'est pas encore
traité
</T>
</p>
</div>
<img
@ -50,7 +61,9 @@ export default function Créer() {
css="margin-left: 3rem; max-width: 15rem; transform: translateX(2rem) scale(1.4);"
/>
</div>
<h2><T>Ressources utiles</T></h2>
<h2>
<T>Ressources utiles</T>
</h2>
<div
css={`
display: flex;
@ -66,19 +79,23 @@ export default function Créer() {
<T k="créer.ressources.listeStatuts">
<p>Liste des statuts juridiques </p>
<small>
Vous savez déjà quel statut choisir ? Accédez directement à la liste des démarches associées
</small>
Vous savez déjà quel statut choisir ? Accédez directement à la
liste des démarches associées
</small>
</T>
</Link>
<Link
className="ui__ interactive card button-choice lighter-bg"
to={{ pathname: sitePaths.simulateurs.comparaison, state: { fromCréer: true } }}>
to={{
pathname: sitePaths.simulateurs.comparaison,
state: { fromCréer: true }
}}>
<T k="créer.ressources.comparaison">
<p>Comparateur de régimes</p>
<small>
Indépendant, assimilé-salarié ou auto-entrepreneur ? Calculez les différences en terme de revenus, cotisations, retraite, etc
</small>
Indépendant, assimilé-salarié ou auto-entrepreneur ? Calculez les
différences en terme de revenus, cotisations, retraite, etc
</small>
</T>
</Link>
@ -86,15 +103,14 @@ export default function Créer() {
className="ui__ interactive card button-choice lighter-bg"
to={sitePaths.créer['auto-entrepreneur']}>
<T k="créer.ressources.autoEntrepreneur">
<p>Démarche auto-entrepreneur</p>
<small>
Vous souhaitez devenir auto-entrepreneur ? Découvrez les étapes pour bien démarrer votre activité
</small>
Vous souhaitez devenir auto-entrepreneur ? Découvrez les étapes
pour bien démarrer votre activité
</small>
</T>
</Link>
</div>
</Animate.fromBottom >
</Animate.fromBottom>
)
}

View File

@ -1,9 +1,8 @@
/* @flow */
// eslint-disable-next-line no-unused-vars
import { React, T } from 'Components'
import type { LegalStatus } from 'Selectors/companyStatusSelectors'
import { T } from 'Components'
import React from 'react'
import { LegalStatus } from 'Selectors/companyStatusSelectors'
type Props = {
StatutDescription: LegalStatus
statut: LegalStatus
}
const StatutDescription = ({ statut }: Props) =>

View File

@ -1,43 +0,0 @@
import { ScrollToTop } from 'Components/utils/Scroll';
import { SitePathsContext } from 'Components/utils/withSitePaths';
import React, { useContext } from 'react';
import { Route, Switch } from 'react-router';
import { useLocation } from 'react-router-dom';
import { LANDING_LEGAL_STATUS_LIST } from '../../sitePaths';
import AfterRegistration from './AfterRegistration';
import CreationChecklist from './CreationChecklist';
import GuideStatut from './GuideStatut';
import Home from './Home';
export default function CreateMyCompany() {
const sitePaths = useContext(SitePathsContext);
const location = useLocation()
return (
<>
<ScrollToTop key={location.pathname} />
<Switch>
<Route
exact
path={sitePaths.créer.index}
component={Home}
/>
{LANDING_LEGAL_STATUS_LIST.map(statut => (
<Route path={sitePaths.créer[statut]} key={statut} >
<CreationChecklist statut={statut} />
</Route>
))
}
<Route
path={sitePaths.créer.après}
component={AfterRegistration}
/>
<Route
path={sitePaths.créer.guideStatut.index}
component={GuideStatut}
/>
</Switch>
</>
)
}

View File

@ -0,0 +1,33 @@
import { ScrollToTop } from 'Components/utils/Scroll'
import { SitePathsContext } from 'Components/utils/withSitePaths'
import React, { useContext } from 'react'
import { Route, Switch } from 'react-router'
import { useLocation } from 'react-router-dom'
import { LANDING_LEGAL_STATUS_LIST } from '../../sitePaths'
import AfterRegistration from './AfterRegistration'
import CreationChecklist from './CreationChecklist'
import GuideStatut from './GuideStatut'
import Home from './Home'
export default function CreateMyCompany() {
const sitePaths = useContext(SitePathsContext)
const location = useLocation()
return (
<>
<ScrollToTop key={location.pathname} />
<Switch>
<Route exact path={sitePaths.créer.index} component={Home} />
{LANDING_LEGAL_STATUS_LIST.map(statut => (
<Route path={sitePaths.créer[statut]} key={statut}>
<CreationChecklist statut={statut} />
</Route>
))}
<Route path={sitePaths.créer.après} component={AfterRegistration} />
<Route
path={sitePaths.créer.guideStatut.index}
component={GuideStatut}
/>
</Switch>
</>
)
}

View File

@ -1,4 +1,3 @@
/* @flow */
import withColours, { ThemeColoursProvider } from 'Components/utils/withColours'
import React, { Suspense, useState } from 'react'

View File

@ -1,10 +1,10 @@
// Page listing the engine's currently implemented mecanisms and their tests
import { React, T } from 'Components'
import withColours from 'Components/utils/withColours'
import { T } from 'Components'
import { ThemeColoursContext } from 'Components/utils/withColours'
import { analyseMany } from 'Engine/traverse'
import { compose } from 'ramda'
import React, { useContext } from 'react'
import emoji from 'react-easy-emoji'
import { connect } from 'react-redux'
import { useSelector } from 'react-redux'
import examples from 'Règles/cas-types.yaml'
import {
parsedRulesSelector,
@ -28,13 +28,10 @@ export default function ExampleSituations() {
)
}
const Example = compose(
connect(state => ({
defaults: ruleDefaultsSelector(state),
parsedRules: parsedRulesSelector(state)
})),
withColours
)(function Example({ ex: { nom, situation }, parsedRules, defaults, colours }) {
const Example = function Example({ ex: { nom, situation } }) {
const defaults = useSelector(ruleDefaultsSelector) as object
const parsedRules = useSelector(parsedRulesSelector)
const colours = useContext(ThemeColoursContext)
let [total, net, netAprèsImpôts] = analyseMany(parsedRules, [
'total',
'net',
@ -77,4 +74,4 @@ const Example = compose(
</ul>
</li>
)
})
}

Some files were not shown because too many files have changed in this diff Show More