Merge pull request #1014 from betagouv/quickfix

Quickfix
pull/1019/head
Maxime Quandalle 2020-05-03 21:33:54 +02:00 committed by GitHub
commit a204b57498
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 201 additions and 183 deletions

View File

@ -38,9 +38,9 @@
"react-addons-css-transition-group": "^15.6.2",
"react-color": "^2.14.0",
"react-dom": "npm:@hot-loader/react-dom",
"react-easy-emoji": "^1.2.0",
"react-easy-emoji": "^1.4.0",
"react-fuzzy-highlighter": "^0.3.1",
"react-helmet": "6.0.0-beta",
"react-helmet": "^6.0.0",
"react-i18next": "^11.0.0",
"react-loading-skeleton": "^2.0.1",
"react-markdown": "^4.1.0",

View File

@ -8,17 +8,17 @@ import { useTranslation } from 'react-i18next'
const ANIMATION_SPRING = config.gentle
let ChartItemBar = ({ styles, color, numberToPlot, unit }) => {
type ChartItemBarProps = {
numberToPlot: number
unit?: string
style: React.CSSProperties
}
function ChartItemBar({ style, numberToPlot, unit }: ChartItemBarProps) {
const language = useTranslation().i18n.language
return (
<div className="distribution-chart__bar-container">
<animated.div
className="distribution-chart__bar"
style={{
backgroundColor: color,
flex: styles.flex
}}
/>
<animated.div className="distribution-chart__bar" style={style} />
<div
css={`
font-weight: bold;
@ -31,11 +31,14 @@ let ChartItemBar = ({ styles, color, numberToPlot, unit }) => {
</div>
)
}
let BranchIcône = ({ icône }) => (
<div className="distribution-chart__legend">
<span className="distribution-chart__icon">{emoji(icône)}</span>
</div>
)
function BranchIcon({ icon }: { icon: string }) {
return (
<div className="distribution-chart__legend">
<span className="distribution-chart__icon">{emoji(icon)}</span>
</div>
)
}
type BarChartBranchProps = {
value: number
@ -73,7 +76,7 @@ export default function BarChartBranch({
className="distribution-chart__item"
style={{ opacity: styles.opacity }}
>
{icon && <BranchIcône icône={icon} />}
{icon && <BranchIcon icon={icon} />}
<div className="distribution-chart__item-content">
<p className="distribution-chart__counterparts">
<span className="distribution-chart__branche-name">{title}</span>
@ -81,12 +84,12 @@ export default function BarChartBranch({
{description && <small>{description}</small>}
</p>
<ChartItemBar
{...{
styles,
color,
numberToPlot,
unit
style={{
backgroundColor: color,
flex: styles.flex
}}
numberToPlot={numberToPlot}
unit={unit}
/>
</div>
</animated.div>

View File

@ -3,13 +3,14 @@ import { any, identity, path } from 'ramda'
import React from 'react'
import { Trans } from 'react-i18next'
import './Algorithm.css'
import { EvaluatedRule, ParsedRule } from 'Rules'
let Conditions = ({
'rendu non applicable': disabledBy,
parentDependencies,
'applicable si': applicable,
'non applicable si': notApplicable
}) => {
}: EvaluatedRule) => {
let listElements = [
...parentDependencies.map(
parentDependency =>
@ -21,7 +22,7 @@ let Conditions = ({
)
),
...disabledBy?.explanation?.isDisabledBy?.map(
(dependency, i) =>
(dependency: EvaluatedRule, i: number) =>
dependency?.nodeValue === true && (
<ShowIfDisabled dependency={dependency} key={`dependency ${i}`} />
)
@ -40,7 +41,7 @@ let Conditions = ({
) : null
}
function ShowIfDisabled({ dependency }) {
function ShowIfDisabled({ dependency }: { dependency: EvaluatedRule }) {
return (
<li>
<span css="background: yellow">
@ -51,7 +52,7 @@ function ShowIfDisabled({ dependency }) {
)
}
export default function Algorithm({ rule }) {
export default function Algorithm({ rule }: { rule: EvaluatedRule }) {
let formula =
rule.formule ||
(rule.category === 'variable' && rule.explanation.formule),

View File

@ -1,7 +1,6 @@
import { ParsedRule } from 'Engine/types'
import yaml from 'yaml'
import React from 'react'
import rules from 'Rules'
import rules, { ParsedRule } from 'Rules'
import PublicodeHighlighter from '../ui/PublicodeHighlighter'
type RuleSourceProps = Pick<ParsedRule, 'dottedName'>

View File

@ -7,6 +7,7 @@ import { DottedName } from 'Rules'
import './PaySlip.css'
import { Line, SalaireBrutSection, SalaireNetSection } from './PaySlipSections'
import RuleLink from './RuleLink'
import { ParsedRules, ParsedRule } from 'Rules'
export const SECTION_ORDER = [
'protection sociale . santé',
@ -21,9 +22,9 @@ export const SECTION_ORDER = [
type Section = typeof SECTION_ORDER[number]
function getSection(rule): Section {
function getSection(rule: ParsedRule): Section {
const section = ('protection sociale . ' +
(rule.cotisation?.branche ?? rule.taxe?.branche)) as Section
rule.cotisation?.branche) as Section
if (SECTION_ORDER.includes(section)) {
return section
}
@ -31,7 +32,7 @@ function getSection(rule): Section {
}
export function getCotisationsBySection(
parsedRules
parsedRules: ParsedRules
): Array<[Section, DottedName[]]> {
const cotisations = [
...parsedRules['contrat salarié . cotisations . patronales'].formule
@ -41,7 +42,7 @@ export function getCotisationsBySection(
]
.map(cotisation => cotisation.dottedName)
.filter(Boolean)
.reduce((acc, cotisation) => {
.reduce((acc, cotisation: DottedName) => {
const sectionName = getSection(parsedRules[cotisation])
return {
...acc,
@ -154,12 +155,12 @@ function Cotisation({ dottedName }: { dottedName: DottedName }) {
const partSalariale = useEvaluation(
'contrat salarié . cotisations . salariales'
)?.formule.explanation.explanation.find(
cotisation => cotisation.dottedName === dottedName
(cotisation: ParsedRule) => cotisation.dottedName === dottedName
)
const partPatronale = useEvaluation(
'contrat salarié . cotisations . patronales'
)?.formule.explanation.explanation.find(
cotisation => cotisation.dottedName === dottedName
(cotisation: ParsedRule) => cotisation.dottedName === dottedName
)
if (!partPatronale?.nodeValue && !partSalariale?.nodeValue) {
return null

View File

@ -36,7 +36,7 @@ export default function SearchBar({
const { i18n } = useTranslation()
const history = useHistory()
const useDefaultValues = useContext(UseDefaultValuesContext)
const handleKeyDown = e => {
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter' && results.length > 0) {
finallyCallback && finallyCallback()
history.push({
@ -257,7 +257,7 @@ export default function SearchBar({
`}
value={input}
placeholder={i18n.t('Entrez des mots clefs ici')}
onKeyDown={e => handleKeyDown(e)}
onKeyDown={handleKeyDown}
onChange={e => {
let input = e.target.value
setInput(input)

View File

@ -6,15 +6,19 @@ import SeeAnswersButton from 'Components/conversation/SeeAnswersButton'
import PageFeedback from 'Components/Feedback/PageFeedback'
import SearchButton from 'Components/SearchButton'
import TargetSelection from 'Components/TargetSelection'
import React from 'react'
import React, { useEffect } from 'react'
import { Trans } from 'react-i18next'
import { useSelector } from 'react-redux'
import { useSelector, useDispatch } from 'react-redux'
import { firstStepCompletedSelector } from 'Selectors/simulationSelectors'
import { useSimulationProgress } from 'Components/utils/useNextQuestion'
import * as Animate from 'Ui/animate'
import Progress from 'Ui/Progress'
import { setSimulationConfig } from 'Actions/actions'
import { useLocation } from 'react-router'
import { SimulationConfig } from 'Reducers/rootReducer'
type SimulationProps = {
config: SimulationConfig
explanations?: React.ReactNode
results?: React.ReactNode
customEndMessages?: ConversationProps['customEndMessages']
@ -22,11 +26,17 @@ type SimulationProps = {
}
export default function Simulation({
config,
explanations,
results,
customEndMessages,
showPeriodSwitch
}: SimulationProps) {
const dispatch = useDispatch()
const location = useLocation<{ fromGérer?: boolean }>()
useEffect(() => {
dispatch(setSimulationConfig(config, location.state?.fromGérer))
}, [config])
const firstStepCompleted = useSelector(firstStepCompletedSelector)
const progress = useSimulationProgress()
return (

View File

@ -10,15 +10,15 @@ import {
} from 'Components/utils/EngineContext'
import { SitePathsContext } from 'Components/utils/SitePathsContext'
import { formatCurrency, formatValue } from 'Engine/format'
import { EvaluatedRule } from 'Engine/types'
import { EvaluatedRule, EvaluatedNode } from 'Engine/types'
import { isNil } from 'ramda'
import React, { useCallback, useContext, useEffect, useState } from 'react'
import emoji from 'react-easy-emoji'
import { Trans, useTranslation } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
import { Link, useLocation } from 'react-router-dom'
import { RootState } from 'Reducers/rootReducer'
import { DottedName } from 'Rules'
import { RootState, SimulationConfig } from 'Reducers/rootReducer'
import { DottedName, ParsedRule } from 'Rules'
import {
situationSelector,
targetUnitSelector
@ -29,7 +29,6 @@ import CurrencyInput from './CurrencyInput/CurrencyInput'
import './TargetSelection.css'
export default function TargetSelection({ showPeriodSwitch = true }) {
const [initialRender, setInitialRender] = useState(true)
const objectifs = useSelector(
(state: RootState) => state.simulation?.config.objectifs || []
)
@ -40,7 +39,7 @@ export default function TargetSelection({ showPeriodSwitch = true }) {
{((typeof objectifs[0] === 'string'
? [{ objectifs }]
: objectifs) as any).map(
({ icône, objectifs: targets, nom }, index) => (
({ icône, objectifs: targets, nom }, index: number) => (
<React.Fragment key={nom || '0'}>
<div style={{ display: 'flex', alignItems: 'end' }}>
<div style={{ flex: 1 }}>
@ -151,7 +150,7 @@ const Target = ({ dottedName }: TargetProps) => {
)
}
let Header = ({ target }) => {
let Header = ({ target }: { target: ParsedRule }) => {
const sitePaths = useContext(SitePathsContext)
const { t } = useTranslation()
const { pathname } = useLocation()

View File

@ -154,7 +154,12 @@ function HeadingWithAnchorLink({
)
}
function Heading({ level, children, ...otherProps }) {
type HeadingProps = {
level: number
children: React.ReactNode
} & React.ComponentProps<'h1'>
function Heading({ level, children, ...otherProps }: HeadingProps) {
return React.createElement(`h${level}`, otherProps, children)
}

View File

@ -22,7 +22,6 @@ import {
sortWith,
takeWhile,
toPairs,
values,
zipWith
} from 'ramda'
import { useSelector } from 'react-redux'
@ -71,7 +70,7 @@ const similarity = (rule1: string = '', rule2: string = '') =>
export function getNextQuestions(
missingVariables: MissingVariables,
questionConfig: SimulationConfig['questions'] = {},
answeredQuestions = [],
answeredQuestions: Array<DottedName> = [],
situation: Simulation['situation'] = {}
): Array<DottedName> {
const {

View File

@ -7,7 +7,7 @@ import CurrencyInput from 'Components/CurrencyInput/CurrencyInput'
import PercentageField from 'Components/PercentageField'
import ToggleSwitch from 'Components/ui/ToggleSwitch'
import { EngineContext } from 'Components/utils/EngineContext'
import { ParsedRules } from 'Engine/types'
import { ParsedRules, ParsedRule } from 'Engine/types'
import React, { useContext } from 'react'
import { useTranslation } from 'react-i18next'
import { DottedName } from 'Rules'
@ -130,10 +130,14 @@ export default function RuleInput({
return <Input {...commonProps} unit={unit} />
}
let getVariant = rule => rule?.formule?.explanation['possibilités']
let getVariant = (rule: ParsedRule) =>
rule?.formule?.explanation['possibilités']
export let buildVariantTree = (allRules, path) => {
let rec = path => {
export let buildVariantTree = <Name extends string>(
allRules: ParsedRules<Name>,
path: Name
) => {
let rec = (path: Name) => {
let node = allRules[path]
if (!node) throw new Error(`La règle ${path} est introuvable`)
let variant = getVariant(node)
@ -144,7 +148,7 @@ export let buildVariantTree = (allRules, path) => {
!!variant
? {
canGiveUp,
children: variants.map(v => rec(path + ' . ' + v))
children: variants.map((v: string) => rec(`${path} . ${v}` as Name))
}
: null
)

View File

@ -23,8 +23,10 @@ import {
} from './temporal'
import { ParsedRule, ParsedRules } from './types'
export let makeJsx = (node: EvaluatedNode): JSX.Element =>
typeof node.jsx == 'function' ? node.jsx(node) : <></>
export let makeJsx = (node: EvaluatedNode): JSX.Element => {
const Component = node.jsx
return <Component {...node} />
}
export let collectNodeMissing = node => node.missingVariables || {}
@ -119,7 +121,7 @@ export const evaluateArrayWithFilter = (evaluationFilter, reducer, start) => (
})
}
export let defaultNode = nodeValue => ({
export let defaultNode = (nodeValue: EvaluatedNode['nodeValue']) => ({
nodeValue,
// eslint-disable-next-line
jsx: ({ nodeValue }: EvaluatedNode) => (
@ -211,7 +213,6 @@ type DefaultValues<Names extends string> = { [name in Names]: any } | {}
export function collectDefaults<Names extends string>(
parsedRules: ParsedRules<Names>
): DefaultValues<Names> {
const cache = { _meta: { contextRule: [] as string[] } }
return (Object.values(parsedRules) as Array<ParsedRule<Names>>).reduce(
(acc, parsedRule) => {
if (parsedRule?.['par défaut'] == null) {

View File

@ -84,7 +84,7 @@ export default class Engine<Names extends string> {
)
if (Object.keys(result.defaultValue?.missingVariable ?? {}).length) {
throw new evaluationError(
throw evaluationError(
context,
"Impossible d'évaluer l'expression car celle ci fait appel à des variables manquantes"
)

View File

@ -3,10 +3,7 @@ import React from 'react'
import { makeJsx } from '../evaluation'
import { Node } from './common'
export default function Allègement({
nodeValue,
explanations: rawExplanation
}) {
export default function Allègement({ nodeValue, explanation: rawExplanation }) {
// Don't display attributes with default values
let explanation = map(k => (k && !k.isDefault ? k : null), rawExplanation)
return (

View File

@ -316,10 +316,7 @@ export let mecanismSum = (recurse, k, v) => {
return {
evaluate,
// eslint-disable-next-line
jsx: ({ nodeValue, explanation, unit }: EvaluatedRule) => (
<Somme nodeValue={nodeValue} explanation={explanation} unit={unit} />
),
jsx: Somme,
explanation,
category: 'mecanism',
name: 'somme',

View File

@ -2,7 +2,7 @@ import { dropLast, last, pipe, propEq, range, take } from 'ramda'
import { coerceArray } from '../utils'
import { EvaluatedRule, Rule, Rules } from './types'
export const splitName = str => str.split(' . ')
export const splitName = (str: string) => str.split(' . ')
export const joinName = strs => strs.join(' . ')
export const parentName = pipe(
splitName,

View File

@ -1,11 +1,19 @@
import { assoc, mapObjIndexed } from 'ramda'
import { Rule, Rules } from './types'
type Translation = Record<string, string>
type translateAttribute = (
prop: string,
rule: Rule,
translation: Translation,
lang: string
) => Rule
/* Traduction */
const translateContrôle = (prop, rule, translation, lang) =>
const translateContrôle: translateAttribute = (prop, rule, translation, lang) =>
assoc(
'contrôles',
rule.contrôles.map((control, i) => ({
rule.contrôles!.map((control, i) => ({
...control,
message: translation[`${prop}.${i}.${lang}`]?.replace(
/^\[automatic\] /,
@ -15,10 +23,15 @@ const translateContrôle = (prop, rule, translation, lang) =>
rule
)
const translateSuggestion = (prop, rule, translation, lang) =>
const translateSuggestion: translateAttribute = (
prop,
rule,
translation,
lang
) =>
assoc(
'suggestions',
Object.entries(rule.suggestions).reduce(
Object.entries(rule.suggestions!).reduce(
(acc, [name, value]) => ({
...acc,
[translation[`${prop}.${name}.${lang}`]?.replace(
@ -41,9 +54,9 @@ export const attributesToTranslate = [
'note'
]
const translateProp = (lang: string, translation: Object) => (
const translateProp = (lang: string, translation: Translation) => (
rule: Rule,
prop
prop: string
) => {
if (prop === 'contrôles' && rule?.contrôles) {
return translateContrôle(prop, rule, translation, lang)
@ -58,7 +71,7 @@ const translateProp = (lang: string, translation: Object) => (
function translateRule<Names extends string>(
lang: string,
translations: { [Name in Names]: Object },
translations: { [Name in Names]: Translation },
name: Names,
rule: Rule
): Rule {
@ -74,7 +87,7 @@ function translateRule<Names extends string>(
export default function translateRules<Names extends string>(
lang: string,
translations: { [Name in Names]: Object },
translations: { [Name in Names]: Translation },
rules: Rules<Names>
): Rules<Names> {
const translatedRules = mapObjIndexed(

View File

@ -48,6 +48,15 @@ export type ParsedRule<Name extends string = string> = Rule & {
category?: string
rulePropType?: string
jsx?: Function
cotisation?: Partial<{
'dû par': string
branche: string
destinataire: string
responsable: string
}>
taxe?: {
'dû par': string
}
}
export type ParsedRules<Names extends string = string> = {
@ -67,7 +76,7 @@ export type EvaluatedNode<
nodeValue: Evaluation<T>
explanation?: Object
isDefault?: boolean
jsx?: (node: EvaluatedNode) => JSX.Element
jsx: React.FunctionComponent<EvaluatedNode>
missingVariables: Partial<Record<Names, number>>
} & (T extends number
? {
@ -87,4 +96,7 @@ export type EvaluatedRule<
EvaluatedNode<Names, Type> & {
isApplicable: boolean
explanation: Explanation
'rendu non applicable': EvaluatedRule<Names, {}, Type>
'applicable si': EvaluatedNode<Names, Type>
'non applicable si': EvaluatedNode<Names, Type>
}

View File

@ -227,6 +227,11 @@ artiste-auteur . revenus . BNC . micro-bnc:
résumé.fr: Avec abattement forfaitaire fiscal de 34 % au titre des frais professionnels
titre.en: Would you like to opt-in for the micro-BNC regime?
titre.fr: Souhaitez-vous opter pour le régime micro-BNC ?
artiste-auteur . revenus . BNC . micro-bnc . contrôle micro-bnc:
description.en: "[automatic] Your income does not allow you to opt for the micro-BNC plan."
description.fr: Vos revenus ne vous permettent pas d'opter pour le régime micro-BNC.
titre.en: "[automatic] micro-bnc control"
titre.fr: contrôle micro-bnc
artiste-auteur . revenus . BNC . recettes:
résumé.en: The amount of your gross revenue excluding VAT
résumé.fr: Le montant de vos recettes brutes hors TVA

View File

@ -66,6 +66,7 @@ export type SimulationConfig = {
objectifs:
| Array<DottedName>
| Array<{ icône: string; nom: string; objectifs: Array<DottedName> }>
'objectifs cachés': Array<DottedName>
situation: Simulation['situation']
bloquant?: Array<DottedName>
questions?: Partial<Record<QuestionsKind, Array<DottedName>>>
@ -140,7 +141,7 @@ function simulation(
case 'UPDATE_SITUATION':
const targets = without(
['entreprise . charges'],
objectifsSelector({ simulation: state })
objectifsSelector({ simulation: state } as RootState)
)
const situation = state.situation
const { fieldName: dottedName, value } = action

View File

@ -20,14 +20,18 @@ artiste-auteur . revenus . BNC:
- sinon: frais réels
artiste-auteur . revenus . BNC . micro-bnc:
applicable si:
toutes ces conditions:
- recettes > 0
- recettes < 72500 €/an
non applicable si: contrôle micro-bnc
par défaut: oui
titre: Souhaitez-vous opter pour le régime micro-BNC ?
résumé: Avec abattement forfaitaire fiscal de 34 % au titre des frais professionnels
artiste-auteur . revenus . BNC . micro-bnc . contrôle micro-bnc:
description: Vos revenus ne vous permettent pas d'opter pour le régime micro-BNC.
formule:
toutes ces conditions:
- recettes != 0
- recettes > 72500 €/an
artiste-auteur . revenus . BNC . recettes:
titre: Revenu en BNC
par défaut: 0 €/an

View File

@ -3,6 +3,8 @@
// future.
import {
EvaluatedRule as GenericEvaluatedRule,
ParsedRule as GenericParsedRule,
ParsedRules as GenericParsedRules,
Rules as GenericRules
} from 'Engine/types'
import artisteAuteur from './artiste-auteur.yaml'
@ -25,6 +27,8 @@ import situationPersonnelle from './situation-personnelle.yaml'
export type DottedName = keyof typeof jsonRules
export type Rules = GenericRules<DottedName>
export type ParsedRules = GenericParsedRules<DottedName>
export type ParsedRule = GenericParsedRule<DottedName>
export type EvaluatedRule = GenericEvaluatedRule<DottedName>
export type Situation = Partial<Record<DottedName, string>>

View File

@ -1,9 +1,12 @@
import { DottedName } from './../rules/index'
import { createSelector } from 'reselect'
import { RootState, SimulationConfig } from 'Reducers/rootReducer'
export const configSelector = (state: RootState): Partial<SimulationConfig> =>
state.simulation?.config ?? {}
export const configSelector = state => state.simulation?.config ?? {}
export const objectifsSelector = createSelector([configSelector], config => {
const primaryObjectifs = ((config.objectifs ?? []) as any)
const primaryObjectifs = (config.objectifs ?? ([] as any))
.map((obj: DottedName | { objectifs: Array<DottedName> }) =>
typeof obj === 'string' ? [obj] : obj.objectifs
)
@ -12,10 +15,13 @@ export const objectifsSelector = createSelector([configSelector], config => {
const objectifs = [...primaryObjectifs, ...(config['objectifs cachés'] ?? [])]
return objectifs
})
const emptySituation = {}
export const situationSelector = state =>
export const situationSelector = (state: RootState) =>
state.simulation?.situation ?? emptySituation
export const configSituationSelector = state =>
export const configSituationSelector = (state: RootState) =>
configSelector(state).situation ?? emptySituation
export const firstStepCompletedSelector = createSelector(
@ -30,11 +36,11 @@ export const firstStepCompletedSelector = createSelector(
}
)
export const targetUnitSelector = state =>
export const targetUnitSelector = (state: RootState) =>
state.simulation?.targetUnit ?? '€/mois'
export const currentQuestionSelector = state =>
export const currentQuestionSelector = (state: RootState) =>
state.simulation?.unfoldedStep ?? null
export const answeredQuestionsSelector = state =>
export const answeredQuestionsSelector = (state: RootState) =>
state.simulation?.foldedSteps ?? []

View File

@ -25,9 +25,9 @@ const ressources = {
}
export default function Budget() {
const [selectedYear, setSelectedYear] = useState('2020')
const years = ['2019', '2020']
const years = ['2019', '2020'] as const
const quarters = ['T1', 'T2', 'T3', 'T4']
const [selectedYear, setSelectedYear] = useState<typeof years[number]>('2020')
const categories = uniq(
quarters
.map(q => Object.keys(budget[2020][q] ?? {}))
@ -44,7 +44,9 @@ export default function Budget() {
{emoji('📅')} Année{' '}
<select
value={selectedYear}
onChange={event => setSelectedYear(event.target.value)}
onChange={event =>
setSelectedYear(event.target.value as typeof years[number])
}
>
{years.map(year => (
<option key={year}>{year}</option>

View File

@ -1,4 +1,3 @@
import { setSimulationConfig } from 'Actions/actions'
import RuleLink from 'Components/RuleLink'
import Simulation from 'Components/Simulation'
import chomagePartielConfig from 'Components/simulationConfigs/chômage-partiel.yaml'
@ -12,8 +11,6 @@ import { EvaluatedRule } from 'Engine/types'
import React, { useContext, useEffect, useState } from 'react'
import { Helmet } from 'react-helmet'
import { Trans, useTranslation } from 'react-i18next'
import { useDispatch } from 'react-redux'
import { useLocation } from 'react-router'
import { DottedName } from 'Rules'
import styled from 'styled-components'
import Animate from 'Ui/animate'
@ -26,14 +23,7 @@ declare global {
}
export default function ChômagePartiel() {
const dispatch = useDispatch()
const location = useLocation<{ fromGérer?: boolean }>()
const inIframe = useContext(IsEmbeddedContext)
useEffect(() => {
dispatch(
setSimulationConfig(chomagePartielConfig, location.state?.fromGérer)
)
}, [])
useEffect(() => {
if (inIframe || !productionMode) {
return
@ -100,6 +90,7 @@ export default function ChômagePartiel() {
</ul>
</Warning>
<Simulation
config={chomagePartielConfig}
results={<ExplanationSection />}
customEndMessages={
<span className="ui__ notice">Voir les résultats au-dessus</span>

View File

@ -50,7 +50,7 @@ export default function ArtisteAuteur() {
<SimpleField dottedName="artiste-auteur . revenus . traitements et salaires" />
<SimpleField dottedName="artiste-auteur . revenus . BNC . recettes" />
<SimpleField dottedName="artiste-auteur . revenus . BNC . micro-bnc" />
<WarningRegimeSpecial />
<Warning dottedName="artiste-auteur . revenus . BNC . micro-bnc . contrôle micro-bnc" />
<SimpleField dottedName="artiste-auteur . revenus . BNC . frais réels" />
<SimpleField dottedName="artiste-auteur . cotisations . option surcotisation" />
</InitialRenderContext.Provider>
@ -103,18 +103,16 @@ function SimpleField({ dottedName }: SimpleFieldProps) {
)
}
function WarningRegimeSpecial() {
const situation = useSelector(situationSelector)
const recettes = situation['artiste-auteur . revenus . BNC . recettes']
const showWarning = recettes !== 0 && recettes >= 70000
if (!showWarning) {
type WarningProps = {
dottedName: DottedName
}
function Warning({ dottedName }: WarningProps) {
const warning = useEvaluation(dottedName)
if (!warning.nodeValue) {
return null
}
return (
<li>
Vos revenus ne vous permettent pas d'opter pour le régime micro-BNC.
</li>
)
return <li>{warning.description}</li>
}
const ResultBlock = styled.div`
@ -168,11 +166,7 @@ const branches = [
icon: '👵'
},
{
dottedName: 'artiste-auteur . cotisations . CSG-CRDS . CSG',
icon: '🏛'
},
{
dottedName: 'artiste-auteur . cotisations . CSG-CRDS . CRDS',
dottedName: 'artiste-auteur . cotisations . CSG-CRDS',
icon: '🏛'
},
{

View File

@ -1,22 +1,13 @@
import { setSimulationConfig } from 'Actions/actions'
import SalaryExplanation from 'Components/SalaryExplanation'
import Warning from 'Components/SimulateurWarning'
import Simulation from 'Components/Simulation'
import assimiléConfig from 'Components/simulationConfigs/assimilé.yaml'
import { IsEmbeddedContext } from 'Components/utils/embeddedContext'
import React, { useContext, useEffect } from 'react'
import React, { useContext } from 'react'
import { Helmet } from 'react-helmet'
import { Trans, useTranslation } from 'react-i18next'
import { useDispatch } from 'react-redux'
import { useLocation } from 'react-router'
export default function AssimiléSalarié() {
const dispatch = useDispatch()
const location = useLocation<{ fromGérer?: boolean }>()
useEffect(() => {
dispatch(setSimulationConfig(assimiléConfig, location.state?.fromGérer))
}, [])
const { t } = useTranslation()
const inIframe = useContext(IsEmbeddedContext)
@ -45,7 +36,10 @@ export default function AssimiléSalarié() {
</h1>
)}
<Warning simulateur="assimilé-salarié" />
<Simulation explanations={<SalaryExplanation />} />
<Simulation
config={assimiléConfig}
explanations={<SalaryExplanation />}
/>
</>
)
}

View File

@ -1,4 +1,3 @@
import { setSimulationConfig } from 'Actions/actions'
import Warning from 'Components/SimulateurWarning'
import Simulation from 'Components/Simulation'
import autoEntrepreneurConfig from 'Components/simulationConfigs/auto-entrepreneur.yaml'
@ -6,22 +5,14 @@ import StackedBarChart from 'Components/StackedBarChart'
import { ThemeColorsContext } from 'Components/utils/colors'
import { IsEmbeddedContext } from 'Components/utils/embeddedContext'
import { EngineContext } from 'Components/utils/EngineContext'
import { default as React, useContext, useEffect } from 'react'
import { default as React, useContext } from 'react'
import { Helmet } from 'react-helmet'
import { Trans, useTranslation } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
import { useLocation } from 'react-router'
import { useSelector } from 'react-redux'
import { targetUnitSelector } from 'Selectors/simulationSelectors'
export default function AutoEntrepreneur() {
const dispatch = useDispatch()
const location = useLocation<{ fromGérer?: boolean }>()
const inIframe = useContext(IsEmbeddedContext)
useEffect(() => {
dispatch(
setSimulationConfig(autoEntrepreneurConfig, location.state?.fromGérer)
)
}, [])
const { t } = useTranslation()
return (
@ -49,7 +40,10 @@ export default function AutoEntrepreneur() {
</h1>
)}
<Warning simulateur="auto-entrepreneur" />
<Simulation explanations={<ExplanationSection />} />
<Simulation
config={autoEntrepreneurConfig}
explanations={<ExplanationSection />}
/>
</>
)
}

View File

@ -1,4 +1,3 @@
import { setSimulationConfig } from 'Actions/actions'
import Warning from 'Components/SimulateurWarning'
import Simulation from 'Components/Simulation'
import indépendantConfig from 'Components/simulationConfigs/indépendant.yaml'
@ -6,18 +5,11 @@ import StackedBarChart from 'Components/StackedBarChart'
import { ThemeColorsContext } from 'Components/utils/colors'
import { IsEmbeddedContext } from 'Components/utils/embeddedContext'
import { EngineContext } from 'Components/utils/EngineContext'
import { default as React, useContext, useEffect } from 'react'
import { default as React, useContext } from 'react'
import { Helmet } from 'react-helmet'
import { Trans, useTranslation } from 'react-i18next'
import { useDispatch } from 'react-redux'
import { useLocation } from 'react-router'
export default function Indépendant() {
const dispatch = useDispatch()
const location = useLocation<{ fromGérer?: boolean }>()
useEffect(() => {
dispatch(setSimulationConfig(indépendantConfig, location.state?.fromGérer))
}, [])
const { t } = useTranslation()
const inIframe = useContext(IsEmbeddedContext)
@ -46,7 +38,10 @@ export default function Indépendant() {
</h1>
)}
<Warning simulateur="indépendant" />
<Simulation explanations={<ExplanationSection />} />
<Simulation
config={indépendantConfig}
explanations={<ExplanationSection />}
/>
</>
)
}

View File

@ -1,4 +1,3 @@
import { setSimulationConfig } from 'Actions/actions'
import Banner from 'Components/Banner'
import PreviousSimulationBanner from 'Components/PreviousSimulationBanner'
import SalaryExplanation from 'Components/SalaryExplanation'
@ -8,11 +7,10 @@ import { IsEmbeddedContext } from 'Components/utils/embeddedContext'
import { SitePathsContext } from 'Components/utils/SitePathsContext'
import urlIllustrationNetBrutEn from 'Images/illustration-net-brut-en.png'
import urlIllustrationNetBrut from 'Images/illustration-net-brut.png'
import { default as React, useContext, useEffect } from 'react'
import { default as React, useContext } from 'react'
import { Helmet } from 'react-helmet'
import { Trans, useTranslation } from 'react-i18next'
import { useDispatch } from 'react-redux'
import { Link, useLocation } from 'react-router-dom'
import { Link } from 'react-router-dom'
export default function Salarié() {
const { t } = useTranslation()
@ -134,15 +132,11 @@ function SeoExplanations() {
}
export let SalarySimulation = () => {
const dispatch = useDispatch()
const location = useLocation<{ fromGérer?: boolean }>()
const sitePaths = useContext(SitePathsContext)
useEffect(() => {
dispatch(setSimulationConfig(salariéConfig, location.state?.fromGérer))
}, [])
return (
<>
<Simulation
config={salariéConfig}
explanations={<SalaryExplanation />}
customEndMessages={
<>

View File

@ -123,7 +123,7 @@ export const Results = ({ targets, onClickShare, rules }: ResultsProps) => {
// console.warn
const warnings: string[] = []
const originalWarn = console.warn
console.warn = warning => warnings.push(warning)
console.warn = (warning: string) => warnings.push(warning)
const evaluation = engine.evaluate(currentTarget)
console.warn = originalWarn

View File

@ -1,5 +0,0 @@
declare module 'react-easy-emoji' {
function emoji(input: string): string
export default emoji
}

View File

@ -9338,7 +9338,7 @@ prop-types-exact@^1.2.0:
object.assign "^4.1.0"
reflect.ownkeys "^0.2.0"
prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
prop-types@^15.5.10, prop-types@^15.5.6, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@ -9642,15 +9642,15 @@ react-color@^2.14.0:
prop-types "^15.6.2"
scheduler "^0.19.0"
react-easy-emoji@^1.2.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/react-easy-emoji/-/react-easy-emoji-1.3.0.tgz#c68ac1512985c08429928ab19d83824c272379c2"
integrity sha512-OBv+Yue3ubZ+Qe2DMNZAXReluJFy7eG1fFNG2+AfzIYN7dhFCFynpt+mX93QuOa15GJR6u5LzwnrnTWsDSIxrg==
react-easy-emoji@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/react-easy-emoji/-/react-easy-emoji-1.4.0.tgz#dfcbb743bf8439af265aa25a1e72998c6d2225ff"
integrity sha512-TcufijpuWKgYgzbySEBukNef+y0HI/4PAJ4gc9vb1CF7Q4CcAS2ZV8VMZk0ObtKKwJJfVgAHVt86nXWOed8QXg==
dependencies:
lodash.assign "^4.0.8"
string-replace-to-array "^1.0.1"
react-fast-compare@^2.0.2:
react-fast-compare@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
@ -9664,15 +9664,15 @@ react-fuzzy-highlighter@^0.3.1:
set-value "^3.0.1"
strind "^0.3.0"
react-helmet@6.0.0-beta:
version "6.0.0-beta"
resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.0.0-beta.tgz#1f2ac04521951486e4fce3296d0c88aae8cabd5c"
integrity sha512-GnNWsokebTe7fe8MH2I/a2dl4THYWhthLBoMaQSRYqW5XbPo881WAJGi+lqRBjyOFryW6zpQluEkBy70zh+h9w==
react-helmet@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.0.0.tgz#fcb93ebaca3ba562a686eb2f1f9d46093d83b5f8"
integrity sha512-My6S4sa0uHN/IuVUn0HFmasW5xj9clTkB9qmMngscVycQ5vVG51Qp44BEvLJ4lixupTwDlU9qX1/sCrMN4AEPg==
dependencies:
object-assign "^4.1.1"
prop-types "^15.5.4"
react-fast-compare "^2.0.2"
react-side-effect "^1.1.0"
prop-types "^15.7.2"
react-fast-compare "^2.0.4"
react-side-effect "^2.1.0"
react-hot-loader@^4.12.15:
version "4.12.20"
@ -9800,12 +9800,10 @@ react-router@5.1.2, react-router@^5.1.1:
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
react-side-effect@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-1.2.0.tgz#0e940c78faba0c73b9b0eba9cd3dda8dfb7e7dae"
integrity sha512-v1ht1aHg5k/thv56DRcjw+WtojuuDHFUgGfc+bFHOWsF4ZK6C2V57DO0Or0GPsg6+LSTE0M6Ry/gfzhzSwbc5w==
dependencies:
shallowequal "^1.0.1"
react-side-effect@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.0.tgz#1ce4a8b4445168c487ed24dab886421f74d380d3"
integrity sha512-IgmcegOSi5SNX+2Snh1vqmF0Vg/CbkycU9XZbOHJlZ6kMzTmi3yc254oB1WCkgA7OQtIAoLmcSFuHTc/tlcqXg==
react-smooth@^1.0.5:
version "1.0.5"
@ -10678,7 +10676,7 @@ sha.js@^2.4.0, sha.js@^2.4.8:
inherits "^2.0.1"
safe-buffer "^5.0.1"
shallowequal@^1.0.1, shallowequal@^1.1.0:
shallowequal@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8"
integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==