Ajoute la gestion €/an et €/mois dans le simulateur auto-entrepreneur
parent
1cebad45e4
commit
31f5f1889f
|
@ -10,7 +10,7 @@ export default function PeriodSwitch() {
|
|||
|
||||
const units = ['€/mois', '€/an']
|
||||
return (
|
||||
<span id="PeriodSwitch">
|
||||
<div id="PeriodSwitch">
|
||||
<span className="base ui__ small radio toggle">
|
||||
{units.map((unit) => (
|
||||
<label key={unit}>
|
||||
|
@ -27,6 +27,6 @@ export default function PeriodSwitch() {
|
|||
</label>
|
||||
))}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,10 @@ import { DottedName } from 'modele-social'
|
|||
import { formatValue, UNSAFE_isNotApplicable } from 'publicodes'
|
||||
import { createContext, useContext, useEffect, useState } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { situationSelector } from 'Selectors/simulationSelectors'
|
||||
import {
|
||||
situationSelector,
|
||||
targetUnitSelector,
|
||||
} from 'Selectors/simulationSelectors'
|
||||
import RuleInput from './conversation/RuleInput'
|
||||
import RuleLink from './RuleLink'
|
||||
import AnimatedTargetValue from './ui/AnimatedTargetValue'
|
||||
|
@ -26,7 +29,10 @@ export function SimulationGoals({
|
|||
|
||||
return (
|
||||
<InitialRenderContext.Provider value={initialRender}>
|
||||
<section className={`ui__ card ${className}`}>
|
||||
<section
|
||||
className={`ui__ card ${className}`}
|
||||
style={{ marginTop: '0.6rem' }}
|
||||
>
|
||||
<div id="targetSelection">
|
||||
<ul className="targets">{children}</ul>
|
||||
</div>
|
||||
|
@ -45,7 +51,7 @@ function useInitialRender() {
|
|||
|
||||
type SimulationGoalProps = {
|
||||
dottedName: DottedName
|
||||
labelWithTitle?: boolean
|
||||
labelWithQuestion?: boolean
|
||||
small?: boolean
|
||||
appear?: boolean
|
||||
editable?: boolean
|
||||
|
@ -53,16 +59,21 @@ type SimulationGoalProps = {
|
|||
|
||||
export function SimulationGoal({
|
||||
dottedName,
|
||||
labelWithTitle = false,
|
||||
labelWithQuestion = false,
|
||||
small = false,
|
||||
appear = true,
|
||||
editable = true,
|
||||
}: SimulationGoalProps) {
|
||||
const dispatch = useDispatch()
|
||||
const engine = useEngine()
|
||||
const currentUnit = useSelector(targetUnitSelector)
|
||||
const situation = useSelector(situationSelector)
|
||||
const isNotApplicable = UNSAFE_isNotApplicable(engine, dottedName)
|
||||
const evaluation = engine.evaluate(dottedName)
|
||||
const evaluation = engine.evaluate({
|
||||
valeur: dottedName,
|
||||
unité: currentUnit,
|
||||
arrondi: 'oui',
|
||||
})
|
||||
const rule = engine.getRule(dottedName)
|
||||
const initialRender = useContext(InitialRenderContext)
|
||||
const [isFocused, setFocused] = useState(false)
|
||||
|
@ -79,6 +90,12 @@ export function SimulationGoal({
|
|||
isFocused ||
|
||||
initialRender ||
|
||||
Object.keys(situation).length === 0
|
||||
if (
|
||||
!editable &&
|
||||
(evaluation.nodeValue === false || evaluation.nodeValue === null)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<li className={small ? 'small-target' : ''}>
|
||||
<Animate.appear unless={!appear || initialRender}>
|
||||
|
@ -86,7 +103,9 @@ export function SimulationGoal({
|
|||
<div className="header">
|
||||
<label htmlFor={dottedName}>
|
||||
<span className="optionTitle">
|
||||
{(!labelWithTitle && rule.rawNode.question) || rule.title}
|
||||
{(labelWithQuestion && rule.rawNode.question) || (
|
||||
<RuleLink dottedName={dottedName} />
|
||||
)}
|
||||
</span>
|
||||
<p className="ui__ notice">{rule.rawNode.résumé}</p>
|
||||
</label>
|
||||
|
@ -101,6 +120,10 @@ export function SimulationGoal({
|
|||
{ focused: isFocused }
|
||||
)}
|
||||
isTarget
|
||||
modifiers={{
|
||||
unité: currentUnit,
|
||||
arrondi: 'oui',
|
||||
}}
|
||||
dottedName={dottedName}
|
||||
onFocus={() => setFocused(true)}
|
||||
onBlur={() => setFocused(false)}
|
||||
|
|
|
@ -27,6 +27,7 @@ export type Props<Name extends string = DottedName> = Omit<
|
|||
useSwitch?: boolean
|
||||
isTarget?: boolean
|
||||
onSubmit?: (source: string) => void
|
||||
modifiers?: Record<string, string>
|
||||
}
|
||||
|
||||
export type InputProps<Name extends string = string> = Props<Name> &
|
||||
|
@ -51,11 +52,12 @@ export default function RuleInput({
|
|||
useSwitch = false,
|
||||
isTarget = false,
|
||||
onSubmit = () => null,
|
||||
modifiers = {},
|
||||
...props
|
||||
}: Props<DottedName>) {
|
||||
const engine = useContext(EngineContext)
|
||||
const rule = engine.getRule(dottedName)
|
||||
const evaluation = engine.evaluate(dottedName)
|
||||
const evaluation = engine.evaluate({ valeur: dottedName, ...modifiers })
|
||||
const language = useTranslation().i18n.language
|
||||
const value = evaluation.nodeValue
|
||||
const commonProps: InputProps<DottedName> = {
|
||||
|
|
|
@ -22,14 +22,29 @@ export default function ArtisteAuteur() {
|
|||
<>
|
||||
<SimulateurWarning simulateur="artiste-auteur" />
|
||||
<SimulationGoals className="light">
|
||||
<SimulationGoal dottedName="artiste-auteur . revenus . traitements et salaires" />
|
||||
<SimulationGoal dottedName="artiste-auteur . revenus . BNC . recettes" />
|
||||
<SimulationGoal dottedName="artiste-auteur . revenus . BNC . micro-bnc" />
|
||||
<SimulationGoal
|
||||
labelWithQuestion
|
||||
dottedName="artiste-auteur . revenus . traitements et salaires"
|
||||
/>
|
||||
<SimulationGoal
|
||||
labelWithQuestion
|
||||
dottedName="artiste-auteur . revenus . BNC . recettes"
|
||||
/>
|
||||
<SimulationGoal
|
||||
labelWithQuestion
|
||||
dottedName="artiste-auteur . revenus . BNC . micro-bnc"
|
||||
/>
|
||||
<Warning dottedName="artiste-auteur . revenus . BNC . contrôle micro-bnc" />
|
||||
<Condition expression="artiste-auteur . revenus . BNC . micro-bnc = non">
|
||||
<SimulationGoal dottedName="artiste-auteur . revenus . BNC . frais réels" />
|
||||
<SimulationGoal
|
||||
labelWithQuestion
|
||||
dottedName="artiste-auteur . revenus . BNC . frais réels"
|
||||
/>
|
||||
</Condition>
|
||||
<SimulationGoal dottedName="artiste-auteur . cotisations . option surcotisation" />
|
||||
<SimulationGoal
|
||||
labelWithQuestion
|
||||
dottedName="artiste-auteur . cotisations . option surcotisation"
|
||||
/>
|
||||
</SimulationGoals>
|
||||
<CotisationsResult />
|
||||
</>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { updateSituation } from 'Actions/actions'
|
||||
import Conversation from 'Components/conversation/Conversation'
|
||||
import { Condition } from 'Components/EngineValue'
|
||||
import PeriodSwitch from 'Components/PeriodSwitch'
|
||||
import SearchButton from 'Components/SearchButton'
|
||||
import SimulateurWarning from 'Components/SimulateurWarning'
|
||||
import { SimulationGoal, SimulationGoals } from 'Components/SimulationGoals'
|
||||
|
@ -18,14 +19,14 @@ export default function AutoEntrepreneur() {
|
|||
<>
|
||||
<SimulateurWarning simulateur="auto-entrepreneur" />
|
||||
<SearchButton invisibleButton />
|
||||
|
||||
<PeriodSwitch />
|
||||
<br />
|
||||
<SimulationGoals className="plain">
|
||||
<SimulationGoal
|
||||
appear={false}
|
||||
editable={
|
||||
engine.evaluate('entreprise . activité . mixte').nodeValue !== true
|
||||
}
|
||||
labelWithTitle
|
||||
dottedName="entreprise . chiffre d'affaires"
|
||||
/>
|
||||
<ActivitéMixte />
|
||||
|
@ -34,31 +35,29 @@ export default function AutoEntrepreneur() {
|
|||
<ul>
|
||||
<SimulationGoal
|
||||
small
|
||||
labelWithTitle
|
||||
dottedName="entreprise . chiffre d'affaires . vente restauration hébergement"
|
||||
/>
|
||||
<SimulationGoal
|
||||
labelWithTitle
|
||||
small
|
||||
dottedName="entreprise . chiffre d'affaires . prestations de service . BIC"
|
||||
/>
|
||||
<SimulationGoal
|
||||
labelWithTitle
|
||||
small
|
||||
dottedName="entreprise . chiffre d'affaires . prestations de service . BNC"
|
||||
/>
|
||||
</ul>
|
||||
</li>
|
||||
</Condition>
|
||||
|
||||
<SimulationGoal
|
||||
labelWithTitle
|
||||
dottedName="dirigeant . auto-entrepreneur . net de cotisations"
|
||||
/>
|
||||
<SimulationGoal
|
||||
labelWithTitle
|
||||
dottedName="dirigeant . auto-entrepreneur . net après impôt"
|
||||
small
|
||||
editable={false}
|
||||
dottedName="dirigeant . auto-entrepreneur . cotisations et contributions"
|
||||
/>
|
||||
<SimulationGoal dottedName="dirigeant . auto-entrepreneur . net de cotisations" />
|
||||
<Condition expression="impôt > 0">
|
||||
<SimulationGoal small editable={false} dottedName="impôt" />
|
||||
</Condition>
|
||||
<SimulationGoal dottedName="dirigeant . auto-entrepreneur . net après impôt" />
|
||||
</SimulationGoals>
|
||||
<Conversation />
|
||||
<Explanation />
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Evaluation, Unit } from './AST/types'
|
||||
import { simplifyNodeUnit } from './nodeUnits'
|
||||
import { formatUnit, serializeUnit } from './units'
|
||||
|
||||
export const numberFormatter = ({
|
||||
|
@ -29,27 +30,27 @@ export const numberFormatter = ({
|
|||
}).format(value)
|
||||
}
|
||||
|
||||
export const formatCurrency = (value: number | undefined, language: string) => {
|
||||
return value == null
|
||||
export const formatCurrency = (nodeValue: number | undefined, language: string) => {
|
||||
return nodeValue == null
|
||||
? ''
|
||||
: (formatNumber({ unit: '€', language, value }) ?? '').replace(
|
||||
: (formatNumber({ unit: '€', language, nodeValue }) ?? '').replace(
|
||||
/^(-)?€/,
|
||||
'$1€\u00A0'
|
||||
)
|
||||
}
|
||||
|
||||
export const formatPercentage = (value: number | undefined) =>
|
||||
value == null
|
||||
export const formatPercentage = (nodeValue: number | undefined) =>
|
||||
nodeValue == null
|
||||
? ''
|
||||
: formatNumber({ unit: '%', value, maximumFractionDigits: 2 })
|
||||
: formatNumber({ unit: '%', nodeValue, maximumFractionDigits: 2 })
|
||||
|
||||
export type formatValueOptions = {
|
||||
type formatValueOptions = {
|
||||
maximumFractionDigits?: number
|
||||
minimumFractionDigits?: number
|
||||
language?: string
|
||||
unit?: Unit | string
|
||||
formatUnit?: formatUnit
|
||||
value: number
|
||||
nodeValue: number
|
||||
}
|
||||
|
||||
function formatNumber({
|
||||
|
@ -58,13 +59,13 @@ function formatNumber({
|
|||
language,
|
||||
formatUnit,
|
||||
unit,
|
||||
value,
|
||||
nodeValue,
|
||||
}: formatValueOptions) {
|
||||
if (typeof value !== 'number') {
|
||||
return value
|
||||
if (typeof nodeValue !== 'number') {
|
||||
return nodeValue
|
||||
}
|
||||
const serializedUnit = unit
|
||||
? serializeUnit(unit, value, formatUnit)
|
||||
? serializeUnit(unit, nodeValue, formatUnit)
|
||||
: undefined
|
||||
switch (serializedUnit) {
|
||||
case '€':
|
||||
|
@ -73,13 +74,13 @@ function formatNumber({
|
|||
maximumFractionDigits,
|
||||
minimumFractionDigits,
|
||||
language,
|
||||
})(value)
|
||||
})(nodeValue)
|
||||
case '%':
|
||||
return numberFormatter({
|
||||
style: 'percent',
|
||||
maximumFractionDigits,
|
||||
language,
|
||||
})(value / 100)
|
||||
})(nodeValue / 100)
|
||||
default:
|
||||
return (
|
||||
numberFormatter({
|
||||
|
@ -87,7 +88,7 @@ function formatNumber({
|
|||
minimumFractionDigits,
|
||||
maximumFractionDigits,
|
||||
language,
|
||||
})(value) +
|
||||
})(nodeValue) +
|
||||
(typeof serializedUnit === 'string' ? `\u00A0${serializedUnit}` : '')
|
||||
)
|
||||
}
|
||||
|
@ -116,18 +117,11 @@ export function formatValue(
|
|||
|
||||
{ language = 'fr', displayedUnit, formatUnit, precision = 2 }: Options = {}
|
||||
) {
|
||||
const nodeValue =
|
||||
let nodeValue =
|
||||
typeof value === 'number' || typeof value === 'undefined'
|
||||
? value
|
||||
: value.nodeValue
|
||||
|
||||
const unit =
|
||||
displayedUnit ??
|
||||
(typeof value === 'number' ||
|
||||
typeof value === 'undefined' ||
|
||||
!('unit' in value)
|
||||
? undefined
|
||||
: value.unit)
|
||||
|
||||
if (
|
||||
(typeof nodeValue === 'number' && Number.isNaN(nodeValue)) ||
|
||||
|
@ -135,22 +129,38 @@ export function formatValue(
|
|||
) {
|
||||
return '-'
|
||||
}
|
||||
return typeof nodeValue === 'string'
|
||||
? capitalise0(nodeValue.replace('\\n', '\n'))
|
||||
: typeof nodeValue === 'object'
|
||||
? (nodeValue as any).nom
|
||||
: typeof nodeValue === 'boolean'
|
||||
? booleanTranslations[language][nodeValue]
|
||||
: typeof nodeValue === 'number'
|
||||
? formatNumber({
|
||||
if (typeof nodeValue === 'string'){
|
||||
return capitalise0(nodeValue.replace('\\n', '\n'))
|
||||
}
|
||||
if (typeof nodeValue === 'object')
|
||||
return (nodeValue as any).nom
|
||||
if (typeof nodeValue === 'boolean')
|
||||
return booleanTranslations[language][nodeValue]
|
||||
if (typeof nodeValue === 'number'){
|
||||
let unit =
|
||||
(typeof value === 'number' ||
|
||||
typeof value === 'undefined' ||
|
||||
!('unit' in value)
|
||||
? undefined
|
||||
: value.unit)
|
||||
if (unit) {
|
||||
const simplifiedNode = simplifyNodeUnit({
|
||||
unit,
|
||||
nodeValue
|
||||
})
|
||||
unit = simplifiedNode.unit
|
||||
nodeValue = simplifiedNode.nodeValue as number
|
||||
}
|
||||
return formatNumber({
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: precision,
|
||||
language,
|
||||
formatUnit,
|
||||
unit,
|
||||
value: nodeValue,
|
||||
nodeValue,
|
||||
unit: displayedUnit ?? unit
|
||||
})
|
||||
: null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export function serializeValue(
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { EvaluatedNode, Unit } from './AST/types'
|
||||
import { mapTemporal } from './temporal'
|
||||
import { convertUnit, simplifyUnit } from './units'
|
||||
import { EvaluatedNode, Unit } from './AST/types'
|
||||
|
||||
export function simplifyNodeUnit(node) {
|
||||
console.log('jhjoiijoijo', node)
|
||||
if (!node.unit) {
|
||||
return node
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue