🎨 Améliore le style des champs de saisi principaux dex simulateurs
parent
f49fc6aaa6
commit
9714525a9c
|
@ -128,6 +128,7 @@ dirigeant . auto-entrepreneur . seuils dépassés . notification:
|
|||
dirigeant . auto-entrepreneur . net de cotisations:
|
||||
titre: Revenu net de cotisations
|
||||
arrondi: oui
|
||||
unité: €/an
|
||||
identifiant court: auto-entrepreneur-net
|
||||
résumé: Avant impôt
|
||||
question: Quel revenu avant impôt voulez-vous toucher ?
|
||||
|
@ -234,10 +235,9 @@ dirigeant . auto-entrepreneur . cotisations et contributions . contribution form
|
|||
- sinon: 0.1%
|
||||
dirigeant . auto-entrepreneur . chiffre d'affaires:
|
||||
question: Quel est votre chiffre d'affaires ?
|
||||
résumé: Montant total des recettes brutes
|
||||
résumé: Montant total des recettes brutes (hors taxe)
|
||||
unité: €/an
|
||||
arrondi: oui
|
||||
non applicable si: entreprise . activité . mixte
|
||||
inversion numérique:
|
||||
avec:
|
||||
- net de cotisations
|
||||
|
|
|
@ -72,13 +72,8 @@ aide déclaration revenu indépendant 2020 . nature de l'activité . libérale:
|
|||
références:
|
||||
fiche Wikipedia: https://fr.m.wikipedia.org/wiki/Profession_libérale
|
||||
|
||||
<<<<<<< HEAD
|
||||
aide déclaration revenu indépendant 2020 . nature de l'activité . commerciale ou industrielle:
|
||||
remplace: entreprise . catégorie d'activité . commerciale ou industrielle
|
||||
=======
|
||||
aide déclaration revenu indépendant 2019 . nature de l'activité . commerciale ou industrielle:
|
||||
remplace: entreprise . activité . commerciale ou industrielle
|
||||
>>>>>>> 76a45672 ((auto-entrepreneur) Ajoute les revenus mixtes)
|
||||
formule: nature de l'activité = 'commerciale ou industrielle'
|
||||
description: |
|
||||
### Activité commerciale
|
||||
|
|
|
@ -68,22 +68,19 @@ entreprise . chiffre d'affaires . vente restauration hébergement:
|
|||
- activité . service ou vente = 'vente'
|
||||
- activité . mixte
|
||||
titre: Vente de biens, restauration, hebergement
|
||||
par défaut:
|
||||
le maximum de:
|
||||
- dirigeant . auto-entrepreneur . chiffre d'affaires
|
||||
- 0 €/an
|
||||
résumé: Chiffre d'affaires hors taxe
|
||||
question: Quel est le chiffre d'affaires issus de la vente de bien, restauration ou hébergement ?
|
||||
unité: €/an
|
||||
par défaut:
|
||||
produit:
|
||||
assiette: dirigeant . auto-entrepreneur . chiffre d'affaires
|
||||
facteur:
|
||||
variations:
|
||||
- si: activité = 'libérale'
|
||||
alors: 30%
|
||||
- sinon: 70%
|
||||
arrondi: oui
|
||||
plancher: 0€/an
|
||||
inversion numérique:
|
||||
avec:
|
||||
- dirigeant . auto-entrepreneur . net de cotisations
|
||||
- dirigeant . auto-entrepreneur . net après impôt
|
||||
abattement:
|
||||
applicable si: activité . mixte
|
||||
valeur: 2 / 3
|
||||
unité: '%'
|
||||
description: |
|
||||
### Vente de biens
|
||||
Il s’agit du chiffre d'affaires de toutes les opérations comportant
|
||||
|
@ -114,19 +111,16 @@ entreprise . chiffre d'affaires . prestations de service . BIC:
|
|||
- activité . service ou vente = 'service'
|
||||
- activité . mixte
|
||||
unité: €/an
|
||||
par défaut:
|
||||
le maximum de:
|
||||
- dirigeant . auto-entrepreneur . chiffre d'affaires
|
||||
- 0 €/an
|
||||
par défaut:
|
||||
produit:
|
||||
assiette: dirigeant . auto-entrepreneur . chiffre d'affaires
|
||||
facteur:
|
||||
variations:
|
||||
- si: activité = 'libérale'
|
||||
alors: 0
|
||||
- sinon: 30%
|
||||
plancher: 0€/an
|
||||
inversion numérique:
|
||||
avec:
|
||||
- dirigeant . auto-entrepreneur . net de cotisations
|
||||
- dirigeant . auto-entrepreneur . net après impôt
|
||||
abattement:
|
||||
applicable si: activité . mixte
|
||||
valeur: 2 / 3
|
||||
unité: '%'
|
||||
arrondi: oui
|
||||
résumé: Chiffre d'affaires hors taxe
|
||||
titre: Prestations de service commerciales ou artisanales
|
||||
question: Quel est le chiffre d'affaires issus de prestations de service commerciales ou artisanales ?
|
||||
|
@ -146,23 +140,21 @@ entreprise . chiffre d'affaires . prestations de service . BNC:
|
|||
titre: Prestations de service libérale
|
||||
résumé: Chiffre d'affaires hors taxe
|
||||
question: Quel est le chiffre d'affaires issus de prestations de service libérale ?
|
||||
par défaut:
|
||||
le maximum de:
|
||||
- dirigeant . auto-entrepreneur . chiffre d'affaires
|
||||
- 0 €/an
|
||||
par défaut:
|
||||
produit:
|
||||
assiette: dirigeant . auto-entrepreneur . chiffre d'affaires
|
||||
facteur:
|
||||
variations:
|
||||
- si: activité = 'libérale'
|
||||
alors: 70%
|
||||
- sinon: 0.0000001% # On veut qu'elle reste manquante en fonction du CA
|
||||
arrondi: oui
|
||||
applicable si:
|
||||
une de ces conditions:
|
||||
- activité = 'libérale'
|
||||
- activité . mixte
|
||||
plancher: 0€/an
|
||||
inversion numérique:
|
||||
avec:
|
||||
- dirigeant . auto-entrepreneur . net de cotisations
|
||||
- dirigeant . auto-entrepreneur . net après impôt
|
||||
abattement:
|
||||
applicable si: activité . mixte
|
||||
valeur: 2 / 3
|
||||
unité: '%'
|
||||
|
||||
description: |
|
||||
Ce sont toutes les opérations dont l'activité intellectuelle tient
|
||||
un rôle essentiel.
|
||||
|
|
|
@ -18,12 +18,16 @@ export default function CurrencyInput({
|
|||
currencySymbol = '€',
|
||||
onChange,
|
||||
language,
|
||||
missing,
|
||||
className,
|
||||
style,
|
||||
dottedName,
|
||||
...forwardedProps
|
||||
}: CurrencyInputProps) {
|
||||
const valueProp = value ?? ''
|
||||
const [initialValue, setInitialValue] = useState(valueProp)
|
||||
const [currentValue, setCurrentValue] = useState(valueProp)
|
||||
|
||||
const onChangeDebounced = useMemo(
|
||||
() =>
|
||||
debounceTimeout && onChange
|
||||
|
@ -48,7 +52,10 @@ export default function CurrencyInput({
|
|||
// Only trigger the `onChange` event if the value has changed -- and not
|
||||
// only its formating, we don't want to call it when a dot is added in `12.`
|
||||
// for instance
|
||||
if (!nextValue.current || /(\.$)|(^\.)|(-$)/.exec(nextValue.current)) {
|
||||
if (
|
||||
!nextValue.current ||
|
||||
/(\.$)|(^\.)|(-$)|(^0+$)/.exec(nextValue.current)
|
||||
) {
|
||||
return
|
||||
}
|
||||
event.persist()
|
||||
|
@ -68,14 +75,13 @@ export default function CurrencyInput({
|
|||
// Autogrow the input
|
||||
const valueLength = currentValue.toString().length
|
||||
const width = `${5 + (valueLength - 5) * 0.75}em`
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames(className, 'currencyInput__container')}
|
||||
{...(valueLength > 5 ? { style: { width } } : {})}
|
||||
style={{ ...(valueLength > 5 ? { width } : {}), ...style }}
|
||||
onFocus={() => inputRef.current?.select()}
|
||||
onClick={() => inputRef.current?.focus()}
|
||||
>
|
||||
{!currentValue && isCurrencyPrefixed && currencySymbol}
|
||||
<NumberFormat
|
||||
{...forwardedProps}
|
||||
thousandSeparator={thousandSeparator}
|
||||
|
@ -93,7 +99,7 @@ export default function CurrencyInput({
|
|||
//.replace(/^0+/, '')
|
||||
}}
|
||||
onChange={handleChange}
|
||||
value={currentValue ? +currentValue : ''}
|
||||
value={currentValue != null ? currentValue : ''}
|
||||
autoComplete="off"
|
||||
/>
|
||||
{!isCurrencyPrefixed && <> €</>}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import { updateSituation } from 'Actions/actions'
|
||||
import classnames from 'classnames'
|
||||
import Animate from 'Components/ui/animate'
|
||||
import { DottedName } from 'modele-social'
|
||||
import { UNSAFE_isNotApplicable } from 'publicodes'
|
||||
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 RuleInput from './conversation/RuleInput'
|
||||
import RuleLink from './RuleLink'
|
||||
import AnimatedTargetValue from './ui/AnimatedTargetValue'
|
||||
import { useEngine } from './utils/EngineContext'
|
||||
|
||||
type SimulationGoalsProps = {
|
||||
|
@ -13,7 +16,7 @@ type SimulationGoalsProps = {
|
|||
children: React.ReactNode
|
||||
}
|
||||
|
||||
const InitialRenderContext = createContext(false)
|
||||
const InitialRenderContext = createContext(true)
|
||||
|
||||
export function SimulationGoals({
|
||||
className = '',
|
||||
|
@ -44,14 +47,16 @@ type SimulationGoalProps = {
|
|||
dottedName: DottedName
|
||||
labelWithTitle?: boolean
|
||||
small?: boolean
|
||||
titleLevel?: number
|
||||
appear?: boolean
|
||||
editable?: boolean
|
||||
}
|
||||
|
||||
export function SimulationGoal({
|
||||
dottedName,
|
||||
labelWithTitle = false,
|
||||
small = false,
|
||||
titleLevel = 2,
|
||||
appear = true,
|
||||
editable = true,
|
||||
}: SimulationGoalProps) {
|
||||
const dispatch = useDispatch()
|
||||
const engine = useEngine()
|
||||
|
@ -60,6 +65,7 @@ export function SimulationGoal({
|
|||
const evaluation = engine.evaluate(dottedName)
|
||||
const rule = engine.getRule(dottedName)
|
||||
const initialRender = useContext(InitialRenderContext)
|
||||
const [isFocused, setFocused] = useState(false)
|
||||
if (
|
||||
isNotApplicable === true ||
|
||||
(!(dottedName in situation) &&
|
||||
|
@ -68,11 +74,15 @@ export function SimulationGoal({
|
|||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
const displayAsInput =
|
||||
dottedName in situation ||
|
||||
isFocused ||
|
||||
initialRender ||
|
||||
Object.keys(situation).length === 0
|
||||
return (
|
||||
<li className={small ? 'small-target' : ''}>
|
||||
<Animate.appear unless={initialRender}>
|
||||
<div className="main">
|
||||
<Animate.appear unless={!appear || initialRender}>
|
||||
<div className="main" style={small ? { alignItems: 'baseline' } : {}}>
|
||||
<div className="header">
|
||||
<label htmlFor={dottedName}>
|
||||
<span className="optionTitle">
|
||||
|
@ -81,14 +91,39 @@ export function SimulationGoal({
|
|||
<p className="ui__ notice">{rule.rawNode.résumé}</p>
|
||||
</label>
|
||||
</div>
|
||||
{small && <span className="guide-lecture" />}
|
||||
<div className="targetInputOrValue">
|
||||
<RuleInput
|
||||
className="targetInput"
|
||||
isTarget
|
||||
dottedName={dottedName}
|
||||
onChange={(x) => dispatch(updateSituation(dottedName, x))}
|
||||
useSwitch
|
||||
/>
|
||||
{editable ? (
|
||||
<>
|
||||
<RuleInput
|
||||
className={classnames(
|
||||
displayAsInput ? 'targetInput' : 'editableTarget',
|
||||
{ focused: isFocused }
|
||||
)}
|
||||
isTarget
|
||||
dottedName={dottedName}
|
||||
onFocus={() => setFocused(true)}
|
||||
onBlur={() => setFocused(false)}
|
||||
onChange={(x) => dispatch(updateSituation(dottedName, x))}
|
||||
useSwitch
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<AnimatedTargetValue value={evaluation.nodeValue} />
|
||||
<RuleLink
|
||||
dottedName={dottedName}
|
||||
css={`
|
||||
padding-right: 0.6rem
|
||||
&:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
`}
|
||||
>
|
||||
{formatValue(evaluation, { displayedUnit: '€' })}
|
||||
</RuleLink>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Animate.appear>
|
||||
|
|
|
@ -24,29 +24,42 @@
|
|||
|
||||
#targetSelection .targets > li.small-target {
|
||||
border-top: none;
|
||||
padding: 0.4rem 1rem;
|
||||
}
|
||||
#targetSelection .targets > li.small-target .targetInput {
|
||||
|
||||
#targetSelection .targets > li.small-target .targetInput,
|
||||
#targetSelection .targets > li.small-target .editableTarget {
|
||||
border-width: 1px;
|
||||
border-radius: 0.2rem;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
#targetSelection .targets > li {
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.5);
|
||||
border-top: 1px dashed rgba(255, 255, 255, 0.6);
|
||||
padding: 0.8rem 1rem;
|
||||
margin-left: -1rem;
|
||||
margin-right: -1rem;
|
||||
}
|
||||
|
||||
.light #targetSelection .targets > li:not(:first-child) {
|
||||
.light #targetSelection .targets > li {
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.light #targetSelection .targets > li.small-target,
|
||||
.light #targetSelection .targets > li:not(.small-target):first-of-type {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
#targetSelection .targets > li .main {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
#targetSelection .targets > li .guide-lecture {
|
||||
flex: 1;
|
||||
border-bottom: 1px dashed rgba(255, 255, 255, 0.25);
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
#targetSelection li .header {
|
||||
display: flex;
|
||||
|
@ -94,7 +107,7 @@
|
|||
|
||||
#targetSelection .targetInputOrValue {
|
||||
font-size: 115%;
|
||||
margin-left: 0.6rem;
|
||||
min-width: 5.5rem;
|
||||
text-align: right;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -110,26 +123,26 @@
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
#targetSelection .targetInputOrValue > :not(.targetInput) {
|
||||
margin: 0 0.6rem;
|
||||
}
|
||||
|
||||
#targetSelection input {
|
||||
margin: 2.7px 0;
|
||||
}
|
||||
|
||||
#targetSelection .targetInput {
|
||||
width: 5.5em;
|
||||
margin-left: 1rem;
|
||||
max-width: 7.5em;
|
||||
text-align: right;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
padding: 0;
|
||||
padding: 0.2rem 0.6rem;
|
||||
border-radius: 0.3rem;
|
||||
border: 2px solid;
|
||||
border: 1px solid;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
#targetSelection .targetInput.focused {
|
||||
box-shadow: 0 0 0px 1px white;
|
||||
}
|
||||
|
||||
.light #targetSelection .targetInput {
|
||||
color: var(--darkColor);
|
||||
border-color: var(--darkColor);
|
||||
|
@ -142,23 +155,19 @@
|
|||
|
||||
#targetSelection .editableTarget {
|
||||
max-width: 7.5em;
|
||||
width: 5.5em;
|
||||
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
padding: 0 2px;
|
||||
padding: 0.2rem 0.6rem;
|
||||
|
||||
background: transparent;
|
||||
border: 1px dashed rgba(255, 255, 255, 0.5);
|
||||
border-radius: 0.3rem;
|
||||
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
#targetSelection .targetInputBottomBorder {
|
||||
margin: 0;
|
||||
padding: 0 2px;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#targetSelection .editableTarget + .targetInputBottomBorder {
|
||||
border-bottom: 1px dashed #ffffff91;
|
||||
}
|
||||
|
||||
#targetSelection .unit {
|
||||
margin-left: 0.4em;
|
||||
font-size: 110%;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { setActiveTarget, updateSituation } from 'Actions/actions'
|
||||
import InputSuggestions from 'Components/conversation/InputSuggestions'
|
||||
import { updateSituation } from 'Actions/actions'
|
||||
import classnames from 'classnames'
|
||||
import Value, { Condition } from 'Components/EngineValue'
|
||||
import PeriodSwitch from 'Components/PeriodSwitch'
|
||||
import RuleLink from 'Components/RuleLink'
|
||||
|
@ -21,18 +21,17 @@ import {
|
|||
reduceAST,
|
||||
RuleNode,
|
||||
} from 'publicodes'
|
||||
import { isNil } from 'ramda'
|
||||
import { Fragment, useCallback, useContext, useEffect, useState } from 'react'
|
||||
import { Fragment, useCallback, useContext, useState } from 'react'
|
||||
import emoji from 'react-easy-emoji'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { RootState } from 'Reducers/rootReducer'
|
||||
import {
|
||||
firstStepCompletedSelector,
|
||||
situationSelector,
|
||||
targetUnitSelector,
|
||||
} from 'Selectors/simulationSelectors'
|
||||
import InputSuggestions from './conversation/InputSuggestions'
|
||||
import CurrencyInput from './CurrencyInput/CurrencyInput'
|
||||
import './TargetSelection.css'
|
||||
|
||||
|
@ -102,19 +101,7 @@ const Target = ({ dottedName }: TargetProps) => {
|
|||
unité: useSelector(targetUnitSelector),
|
||||
arrondi: 'oui',
|
||||
})
|
||||
const situation = useSelector(situationSelector)
|
||||
|
||||
const target: TargetType = { ...evaluation, ...rule.rawNode, ...rule }
|
||||
const dispatch = useDispatch()
|
||||
const onSuggestionClick = useCallback(
|
||||
(value) => {
|
||||
dispatch(updateSituation(dottedName, value))
|
||||
},
|
||||
[target.dottedName, dispatch]
|
||||
)
|
||||
const isActive =
|
||||
dottedName in situation || Object.keys(situation).length === 0
|
||||
|
||||
const isSmallTarget =
|
||||
!rule.rawNode.question ||
|
||||
(dottedName in evaluation.missingVariables &&
|
||||
|
@ -134,34 +121,14 @@ const Target = ({ dottedName }: TargetProps) => {
|
|||
<div>
|
||||
<div className="main">
|
||||
<Header target={target} />
|
||||
{isSmallTarget && (
|
||||
<span
|
||||
style={{
|
||||
flex: 1,
|
||||
borderBottom: '1px dashed #ffffff91',
|
||||
marginLeft: '1rem',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isSmallTarget && <span className="guide-lecture" />}
|
||||
<TargetInputOrValue
|
||||
{...{
|
||||
target,
|
||||
isActive,
|
||||
isSmallTarget,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{isActive && (
|
||||
<Animate.fromTop>
|
||||
<div css="display: flex; justify-content: flex-end; margin-bottom: -0.4rem">
|
||||
<InputSuggestions
|
||||
suggestions={target.suggestions}
|
||||
onFirstClick={onSuggestionClick}
|
||||
/>
|
||||
</div>
|
||||
</Animate.fromTop>
|
||||
)}
|
||||
</div>
|
||||
</Animate.appear>
|
||||
</li>
|
||||
|
@ -193,30 +160,35 @@ const Header = ({ target }: { target: TargetType }) => {
|
|||
|
||||
type TargetInputOrValueProps = {
|
||||
target: TargetType
|
||||
isActive: boolean
|
||||
isSmallTarget: boolean
|
||||
}
|
||||
|
||||
function TargetInputOrValue({
|
||||
target,
|
||||
isActive,
|
||||
isSmallTarget,
|
||||
}: TargetInputOrValueProps) {
|
||||
const { language } = useTranslation().i18n
|
||||
const colors = useContext(ThemeColorsContext)
|
||||
const dispatch = useDispatch()
|
||||
const [isActiveOrFocused, setActive] = useState(isActive)
|
||||
useEffect(() => setActive(isActive), [isActive])
|
||||
const [isFocused, setFocused] = useState(false)
|
||||
const targetUnit = useSelector(targetUnitSelector)
|
||||
const engine = useContext(EngineContext)
|
||||
const situation = useSelector(situationSelector)
|
||||
const value =
|
||||
(engine.evaluate({
|
||||
valeur: target.dottedName,
|
||||
unité: targetUnit,
|
||||
arrondi: 'oui',
|
||||
}).nodeValue as number) ?? undefined
|
||||
const blurValue = useInversionFail() && !isActiveOrFocused
|
||||
|
||||
const blurValue = useInversionFail() && !isFocused
|
||||
const onSuggestionClick = useCallback(
|
||||
(value) => {
|
||||
dispatch(updateSituation(target.dottedName, value))
|
||||
},
|
||||
[target.dottedName, dispatch]
|
||||
)
|
||||
const isSituationEmpty = Object.keys(situation).length === 0
|
||||
const isActive = target.dottedName in situation
|
||||
const onChange = useCallback(
|
||||
(evt) =>
|
||||
dispatch(
|
||||
|
@ -228,54 +200,68 @@ function TargetInputOrValue({
|
|||
[targetUnit, target, dispatch]
|
||||
)
|
||||
return (
|
||||
<span
|
||||
className="targetInputOrValue"
|
||||
style={blurValue ? { filter: 'blur(3px)' } : {}}
|
||||
>
|
||||
{target.question ? (
|
||||
<>
|
||||
{!isActiveOrFocused && <AnimatedTargetValue value={value} />}
|
||||
<CurrencyInput
|
||||
style={{
|
||||
color: colors.textColor,
|
||||
borderColor: colors.textColor,
|
||||
}}
|
||||
debounce={750}
|
||||
name={target.dottedName}
|
||||
value={value}
|
||||
className={
|
||||
isActiveOrFocused ||
|
||||
isNil(value) ||
|
||||
(target.question && isSmallTarget)
|
||||
? 'targetInput'
|
||||
: 'editableTarget'
|
||||
}
|
||||
onChange={onChange}
|
||||
onFocus={() => {
|
||||
setActive(true)
|
||||
}}
|
||||
language={language}
|
||||
/>
|
||||
<span className="targetInputBottomBorder">
|
||||
{formatValue(value, { language, displayedUnit: '€' })}
|
||||
<>
|
||||
<span
|
||||
className="targetInputOrValue"
|
||||
style={blurValue ? { filter: 'blur(3px)' } : {}}
|
||||
>
|
||||
{target.question ? (
|
||||
<>
|
||||
{!isFocused && <AnimatedTargetValue value={value} />}
|
||||
<CurrencyInput
|
||||
style={{
|
||||
color: colors.textColor,
|
||||
borderColor: colors.textColor,
|
||||
}}
|
||||
debounce={750}
|
||||
name={target.dottedName}
|
||||
value={value}
|
||||
className={classnames(
|
||||
isFocused ||
|
||||
isActive ||
|
||||
isSituationEmpty ||
|
||||
(target.question && isSmallTarget)
|
||||
? 'targetInput'
|
||||
: 'editableTarget',
|
||||
{ focused: isFocused }
|
||||
)}
|
||||
onChange={onChange}
|
||||
onFocus={() => {
|
||||
setFocused(true)
|
||||
}}
|
||||
onBlur={() => setTimeout(() => setFocused(false), 100)}
|
||||
language={language}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<span>
|
||||
{value && Number.isNaN(value) ? (
|
||||
'—'
|
||||
) : (
|
||||
<RuleLink dottedName={target.dottedName}>
|
||||
{formatValue(value, { displayedUnit: '€', language })}
|
||||
</RuleLink>
|
||||
)}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<span>
|
||||
{value && Number.isNaN(value) ? (
|
||||
'—'
|
||||
) : (
|
||||
<RuleLink dottedName={target.dottedName}>
|
||||
{formatValue(value, { displayedUnit: '€', language })}
|
||||
</RuleLink>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
{target.dottedName.includes('prix du travail') && <AidesGlimpse />}
|
||||
{target.dottedName === 'contrat salarié . rémunération . net' && (
|
||||
<TitreRestaurant />
|
||||
)}
|
||||
</span>
|
||||
{(isActive || isFocused) && (
|
||||
<div style={{ minWidth: '100%' }}>
|
||||
<Animate.fromTop>
|
||||
<div css="display: flex; justify-content: flex-end; margin-bottom: -0.4rem">
|
||||
<InputSuggestions
|
||||
suggestions={target.suggestions}
|
||||
onFirstClick={onSuggestionClick}
|
||||
/>
|
||||
</div>
|
||||
</Animate.fromTop>
|
||||
</div>
|
||||
)}
|
||||
{target.dottedName.includes('prix du travail') && <AidesGlimpse />}
|
||||
{target.dottedName === 'contrat salarié . rémunération . net' && (
|
||||
<TitreRestaurant />
|
||||
)}
|
||||
</span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
function TitreRestaurant() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { stepAction, goToQuestion, updateSituation } from 'Actions/actions'
|
||||
import RuleInput, { RuleInputProps } from 'Components/conversation/RuleInput'
|
||||
import { goToQuestion, stepAction, updateSituation } from 'Actions/actions'
|
||||
import RuleInput, { InputProps } from 'Components/conversation/RuleInput'
|
||||
import QuickLinks from 'Components/QuickLinks'
|
||||
import * as Animate from 'Components/ui/animate'
|
||||
import { EngineContext } from 'Components/utils/EngineContext'
|
||||
|
@ -41,7 +41,7 @@ export default function Conversation({ customEndMessages }: ConversationProps) {
|
|||
dispatch(stepAction(currentQuestion, source))
|
||||
}
|
||||
|
||||
const onChange: RuleInputProps['onChange'] = (value) => {
|
||||
const onChange: InputProps['onChange'] = (value) => {
|
||||
dispatch(updateSituation(currentQuestion, value))
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +1,8 @@
|
|||
import {
|
||||
InputCommonProps,
|
||||
RuleInputProps,
|
||||
} from 'Components/conversation/RuleInput'
|
||||
import { RuleNode } from 'publicodes'
|
||||
import { InputProps } from 'Components/conversation/RuleInput'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import InputSuggestions from './InputSuggestions'
|
||||
|
||||
type DateInputProps = {
|
||||
onChange: InputCommonProps['onChange']
|
||||
id: InputCommonProps['id']
|
||||
onSubmit: RuleInputProps['onSubmit']
|
||||
value: InputCommonProps['value']
|
||||
suggestions: RuleNode['suggestions']
|
||||
required: RuleInputProps['required']
|
||||
}
|
||||
|
||||
export default function DateInput({
|
||||
suggestions,
|
||||
onChange,
|
||||
|
@ -23,7 +10,7 @@ export default function DateInput({
|
|||
onSubmit,
|
||||
required,
|
||||
value,
|
||||
}: DateInputProps) {
|
||||
}: InputProps) {
|
||||
const dateValue = useMemo(() => {
|
||||
if (!value || typeof value !== 'string') return undefined
|
||||
const [day, month, year] = value.split('/')
|
||||
|
|
|
@ -8,7 +8,7 @@ import ToggleSwitch from 'Components/ui/ToggleSwitch'
|
|||
import { EngineContext } from 'Components/utils/EngineContext'
|
||||
import { DottedName } from 'modele-social'
|
||||
import Engine, { ASTNode, formatValue, reduceAST } from 'publicodes'
|
||||
import { Evaluation } from 'publicodes/dist/types/AST/types'
|
||||
import { EvaluatedNode, Evaluation } from 'publicodes/dist/types/AST/types'
|
||||
import { RuleNode } from 'publicodes/dist/types/rule'
|
||||
import React, { useContext } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
@ -17,30 +17,23 @@ import ParagrapheInput from './ParagrapheInput'
|
|||
import SelectEuropeCountry from './select/SelectEuropeCountry'
|
||||
import TextInput from './TextInput'
|
||||
|
||||
type Value = any
|
||||
export type RuleInputProps<Name extends string = DottedName> = {
|
||||
export type Props<Name extends string = DottedName> = Omit<
|
||||
React.HTMLAttributes<HTMLInputElement>,
|
||||
'onChange' | 'defaultValue'
|
||||
> & {
|
||||
required?: boolean
|
||||
dottedName: Name
|
||||
onChange: (value: Value | null) => void
|
||||
onChange: (value: Parameters<Engine<Name>['evaluate']>[0]) => void
|
||||
useSwitch?: boolean
|
||||
isTarget?: boolean
|
||||
autoFocus?: boolean
|
||||
required?: boolean
|
||||
id?: string
|
||||
className?: string
|
||||
onSubmit?: (source: string) => void
|
||||
}
|
||||
|
||||
export type InputCommonProps<Name extends string = string> = Pick<
|
||||
RuleInputProps<Name>,
|
||||
'onChange' | 'autoFocus' | 'className'
|
||||
> &
|
||||
export type InputProps<Name extends string = string> = Props<Name> &
|
||||
Pick<RuleNode, 'title' | 'suggestions'> & {
|
||||
question: RuleNode['rawNode']['question']
|
||||
key: string
|
||||
id: string
|
||||
value: any //TODO EvaluatedRule['nodeValue']
|
||||
value: EvaluatedNode['nodeValue']
|
||||
missing: boolean
|
||||
required: boolean
|
||||
}
|
||||
|
||||
export const binaryQuestion = [
|
||||
|
@ -56,30 +49,25 @@ export default function RuleInput({
|
|||
dottedName,
|
||||
onChange,
|
||||
useSwitch = false,
|
||||
id,
|
||||
isTarget = false,
|
||||
autoFocus = false,
|
||||
required = true,
|
||||
className,
|
||||
onSubmit = () => null,
|
||||
}: RuleInputProps) {
|
||||
...props
|
||||
}: Props<DottedName>) {
|
||||
const engine = useContext(EngineContext)
|
||||
const rule = engine.getRule(dottedName)
|
||||
const evaluation = engine.evaluate(dottedName)
|
||||
const language = useTranslation().i18n.language
|
||||
const value = evaluation.nodeValue
|
||||
const commonProps: InputCommonProps = {
|
||||
key: dottedName,
|
||||
const commonProps: InputProps<DottedName> = {
|
||||
dottedName,
|
||||
value,
|
||||
missing: !!evaluation.missingVariables[dottedName],
|
||||
onChange,
|
||||
autoFocus,
|
||||
className,
|
||||
required,
|
||||
title: rule.title,
|
||||
id: id ?? dottedName,
|
||||
id: props.id ?? dottedName,
|
||||
question: rule.rawNode.question,
|
||||
suggestions: rule.suggestions,
|
||||
...props,
|
||||
}
|
||||
if (getVariant(engine.getRule(dottedName))) {
|
||||
return (
|
||||
|
@ -149,12 +137,12 @@ export default function RuleInput({
|
|||
return (
|
||||
<>
|
||||
<CurrencyInput
|
||||
{...commonProps}
|
||||
className="targetInput"
|
||||
language={language}
|
||||
debounce={750}
|
||||
value={value as any}
|
||||
name={dottedName}
|
||||
className="targetInput"
|
||||
{...commonProps}
|
||||
value={value as number}
|
||||
onChange={(evt) => onChange({ valeur: evt.target.value, unité })}
|
||||
/>
|
||||
</>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { updateSituation } from 'Actions/actions'
|
||||
import Conversation from 'Components/conversation/Conversation'
|
||||
import { Condition } from 'Components/EngineValue'
|
||||
import RuleLink from 'Components/RuleLink'
|
||||
import SearchButton from 'Components/SearchButton'
|
||||
import SimulateurWarning from 'Components/SimulateurWarning'
|
||||
import { SimulationGoal, SimulationGoals } from 'Components/SimulationGoals'
|
||||
import StackedBarChart from 'Components/StackedBarChart'
|
||||
|
@ -12,41 +13,47 @@ import { useDispatch } from 'react-redux'
|
|||
import AidesCovid from '../../components/simulationExplanation/AidesCovid'
|
||||
|
||||
export default function AutoEntrepreneur() {
|
||||
const dispatch = useDispatch()
|
||||
|
||||
return (
|
||||
<>
|
||||
<SimulateurWarning simulateur="auto-entrepreneur" />
|
||||
<SimulationGoals className="plain">
|
||||
<ActivitéMixte />
|
||||
<SearchButton invisibleButton />
|
||||
|
||||
<SimulationGoals className="plain">
|
||||
<Condition expression="entreprise . activité . mixte = non">
|
||||
<SimulationGoal
|
||||
appear={false}
|
||||
labelWithTitle
|
||||
dottedName="dirigeant . auto-entrepreneur . chiffre d'affaires"
|
||||
/>
|
||||
<ActivitéMixte />
|
||||
</Condition>
|
||||
<Condition expression="entreprise . activité . mixte">
|
||||
<h2 className="optionTitle">
|
||||
<RuleLink dottedName="entreprise . chiffre d'affaires" />
|
||||
</h2>
|
||||
|
||||
<SimulationGoal
|
||||
small
|
||||
appear={false}
|
||||
editable={false}
|
||||
labelWithTitle
|
||||
dottedName="entreprise . chiffre d'affaires . vente restauration hébergement"
|
||||
/>
|
||||
<SimulationGoal
|
||||
labelWithTitle
|
||||
small
|
||||
titleLevel={3}
|
||||
dottedName="entreprise . chiffre d'affaires . prestations de service . BIC"
|
||||
/>
|
||||
<SimulationGoal
|
||||
labelWithTitle
|
||||
small
|
||||
dottedName="entreprise . chiffre d'affaires . prestations de service . BNC"
|
||||
dottedName="entreprise . chiffre d'affaires"
|
||||
/>
|
||||
<ActivitéMixte />
|
||||
<li>
|
||||
<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
|
||||
|
@ -58,6 +65,7 @@ export default function AutoEntrepreneur() {
|
|||
dottedName="dirigeant . auto-entrepreneur . net après impôt"
|
||||
/>
|
||||
</SimulationGoals>
|
||||
<Conversation />
|
||||
<Explanation />
|
||||
</>
|
||||
)
|
||||
|
@ -67,22 +75,37 @@ function ActivitéMixte() {
|
|||
const defaultCheked = !!useEngine().evaluate('entreprise . activité . mixte')
|
||||
.nodeValue
|
||||
const dispatch = useDispatch()
|
||||
|
||||
return (
|
||||
<label>
|
||||
Activité mixte{' '}
|
||||
<input
|
||||
type="checkbox"
|
||||
defaultChecked={defaultCheked}
|
||||
onChange={(evt) =>
|
||||
dispatch(
|
||||
updateSituation(
|
||||
'entreprise . activité . mixte',
|
||||
evt.target.checked ? 'oui' : 'non'
|
||||
<li
|
||||
className="ui__ notice small-target"
|
||||
css={`
|
||||
margin-top: -0.4rem;
|
||||
`}
|
||||
>
|
||||
<label
|
||||
css={`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`}
|
||||
>
|
||||
Activité mixte {' '}
|
||||
<input
|
||||
type="checkbox"
|
||||
defaultChecked={defaultCheked}
|
||||
onChange={(evt) =>
|
||||
setTimeout(() =>
|
||||
dispatch(
|
||||
updateSituation(
|
||||
'entreprise . activité . mixte',
|
||||
evt.target.checked ? 'oui' : 'non'
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
}
|
||||
/>{' '}
|
||||
</label>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
objectifs:
|
||||
- dirigeant . auto-entrepreneur . chiffre d'affaires
|
||||
- dirigeant . auto-entrepreneur . net de cotisations
|
||||
- dirigeant . auto-entrepreneur . net après impôt
|
||||
- dirigeant . auto-entrepreneur . cotisations et contributions
|
||||
- dirigeant . auto-entrepreneur . net après impôt . impôt
|
||||
|
||||
|
@ -23,5 +26,5 @@ questions:
|
|||
- entreprise . chiffre d'affaires . prestations de service . BNC
|
||||
unité par défaut: €/an
|
||||
situation:
|
||||
entreprise . activité . mixte: oui
|
||||
entreprise . activité . mixte: non
|
||||
dirigeant: "'auto-entrepreneur'"
|
||||
|
|
|
@ -150,11 +150,11 @@ ${e.message}`
|
|||
const chainableMecanisms = [
|
||||
applicable,
|
||||
nonApplicable,
|
||||
parDéfaut,
|
||||
arrondi,
|
||||
unité,
|
||||
plancher,
|
||||
plafond,
|
||||
parDéfaut,
|
||||
situation,
|
||||
abattement,
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue