🔥 Remplace UNSAFE_evaluateNode par de meilleurs abstractions
parent
8c0b925956
commit
adcbd330bd
|
@ -1,11 +1,7 @@
|
|||
import { hideNotification } from 'Actions/actions'
|
||||
import animate from 'Components/ui/animate'
|
||||
import {
|
||||
useInversionFail,
|
||||
EngineContext,
|
||||
useEngine,
|
||||
} from 'Components/utils/EngineContext'
|
||||
import { useContext } from 'react'
|
||||
import { useEngine, useInversionFail } from 'Components/utils/EngineContext'
|
||||
import Engine, { EvaluatedRule } from 'publicodes'
|
||||
import emoji from 'react-easy-emoji'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
|
@ -13,7 +9,6 @@ import { RootState } from 'Reducers/rootReducer'
|
|||
import './Notifications.css'
|
||||
import { Markdown } from './utils/markdown'
|
||||
import { ScrollToElement } from './utils/Scroll'
|
||||
import Engine, { EvaluatedRule, ASTNode, UNSAFE_evaluateRule } from 'publicodes'
|
||||
|
||||
// To add a new notification to a simulator, you should create a publicode rule
|
||||
// with the "type: notification" attribute. The display can be customized with
|
||||
|
@ -27,11 +22,11 @@ type Notification = Pick<EvaluatedRule, 'dottedName' | 'description'> & {
|
|||
export function getNotifications(engine: Engine) {
|
||||
return Object.values(engine.getRules())
|
||||
.filter(
|
||||
(rule: ASTNode & { nodeKind: 'rule' }) =>
|
||||
rule.rawNode['type'] === 'notification'
|
||||
(rule) =>
|
||||
rule.rawNode['type'] === 'notification' &&
|
||||
!!engine.evaluate(rule.dottedName).nodeValue
|
||||
)
|
||||
.map((node) => UNSAFE_evaluateRule(engine, node.dottedName))
|
||||
.filter((node) => !!node.nodeValue)
|
||||
.map((node) => node.dottedName)
|
||||
}
|
||||
export default function Notifications() {
|
||||
const { t } = useTranslation()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { setActiveTarget, updateSituation } from 'Actions/actions'
|
||||
import InputSuggestions from 'Components/conversation/InputSuggestions'
|
||||
import Value, { Condition } from 'Components/EngineValue'
|
||||
import PeriodSwitch from 'Components/PeriodSwitch'
|
||||
import RuleLink from 'Components/RuleLink'
|
||||
import Animate from 'Components/ui/animate'
|
||||
|
@ -11,14 +12,9 @@ import {
|
|||
useInversionFail,
|
||||
} from 'Components/utils/EngineContext'
|
||||
import { SitePathsContext } from 'Components/utils/SitePathsContext'
|
||||
import {
|
||||
ASTNode,
|
||||
EvaluatedNode,
|
||||
EvaluatedRule,
|
||||
UNSAFE_evaluateRule,
|
||||
formatValue,
|
||||
reduceAST,
|
||||
} from 'publicodes'
|
||||
import { DottedName } from 'modele-social'
|
||||
import { Names } from 'modele-social/dist/names'
|
||||
import { ASTNode, EvaluatedRule, formatValue, reduceAST } from 'publicodes'
|
||||
import { isNil } from 'ramda'
|
||||
import { Fragment, useCallback, useContext } from 'react'
|
||||
import emoji from 'react-easy-emoji'
|
||||
|
@ -26,11 +22,9 @@ import { Trans, useTranslation } from 'react-i18next'
|
|||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { RootState } from 'Reducers/rootReducer'
|
||||
import { DottedName } from 'modele-social'
|
||||
import { targetUnitSelector } from 'Selectors/simulationSelectors'
|
||||
import CurrencyInput from './CurrencyInput/CurrencyInput'
|
||||
import './TargetSelection.css'
|
||||
import { Names } from 'modele-social/dist/names'
|
||||
|
||||
export default function TargetSelection({ showPeriodSwitch = true }) {
|
||||
const objectifs = useSelector(
|
||||
|
@ -89,10 +83,13 @@ type TargetProps = {
|
|||
const Target = ({ dottedName }: TargetProps) => {
|
||||
const activeInput = useSelector((state: RootState) => state.activeTargetInput)
|
||||
const engine = useEngine()
|
||||
const target = UNSAFE_evaluateRule(engine, dottedName, {
|
||||
const rule = engine.getRule(dottedName)
|
||||
const evaluation = engine.evaluate({
|
||||
valeur: dottedName,
|
||||
unité: useSelector(targetUnitSelector),
|
||||
arrondi: 'oui',
|
||||
})
|
||||
const target = { ...evaluation, ...rule.rawNode, ...rule }
|
||||
const dispatch = useDispatch()
|
||||
const onSuggestionClick = useCallback(
|
||||
(value) => {
|
||||
|
@ -269,30 +266,26 @@ function TargetInputOrValue({
|
|||
}
|
||||
function TitreRestaurant() {
|
||||
const targetUnit = useSelector(targetUnitSelector)
|
||||
const { language } = useTranslation().i18n
|
||||
|
||||
const titresRestaurant = UNSAFE_evaluateRule(
|
||||
useEngine(),
|
||||
'contrat salarié . frais professionnels . titres-restaurant . montant',
|
||||
{
|
||||
unité: targetUnit,
|
||||
arrondi: 'oui',
|
||||
}
|
||||
)
|
||||
|
||||
if (!titresRestaurant?.nodeValue) return null
|
||||
const dottedName =
|
||||
'contrat salarié . frais professionnels . titres-restaurant . montant'
|
||||
return (
|
||||
<Animate.fromTop>
|
||||
<div className="aidesGlimpse">
|
||||
<RuleLink dottedName={titresRestaurant.dottedName}>
|
||||
+{' '}
|
||||
<strong>
|
||||
{formatValue(titresRestaurant, { displayedUnit: '€', language })}
|
||||
</strong>{' '}
|
||||
<Trans>en titres-restaurant</Trans> {emoji(' 🍽')}
|
||||
</RuleLink>
|
||||
</div>
|
||||
</Animate.fromTop>
|
||||
<Condition expression={dottedName}>
|
||||
<Animate.fromTop>
|
||||
<div className="aidesGlimpse">
|
||||
<RuleLink dottedName={dottedName}>
|
||||
+{' '}
|
||||
<strong>
|
||||
<Value
|
||||
expression={dottedName}
|
||||
displayedUnit="€"
|
||||
unit={targetUnit}
|
||||
/>
|
||||
</strong>{' '}
|
||||
<Trans>en titres-restaurant</Trans> {emoji(' 🍽')}
|
||||
</RuleLink>
|
||||
</div>
|
||||
</Animate.fromTop>
|
||||
</Condition>
|
||||
)
|
||||
}
|
||||
function AidesGlimpse() {
|
||||
|
@ -300,14 +293,7 @@ function AidesGlimpse() {
|
|||
const { language } = useTranslation().i18n
|
||||
const dottedName = 'contrat salarié . aides employeur' as Names
|
||||
const engine = useEngine()
|
||||
const evaluation = engine.evaluate({
|
||||
valeur: dottedName,
|
||||
unité: targetUnit,
|
||||
arrondi: 'oui',
|
||||
})
|
||||
const aides = engine.getRule(dottedName)
|
||||
if (!evaluation?.nodeValue) return null
|
||||
|
||||
// Dans le cas où il n'y a qu'une seule aide à l'embauche qui s'applique, nous
|
||||
// faisons un lien direct vers cette aide, plutôt qu'un lien vers la liste qui
|
||||
// est une somme des aides qui sont toutes nulle sauf l'aide active.
|
||||
|
@ -329,18 +315,22 @@ function AidesGlimpse() {
|
|||
aides
|
||||
)
|
||||
return (
|
||||
<Animate.fromTop>
|
||||
<div className="aidesGlimpse">
|
||||
<RuleLink dottedName={aideLink}>
|
||||
<Trans>en incluant</Trans>{' '}
|
||||
<strong>
|
||||
<span>
|
||||
{formatValue(evaluation, { displayedUnit: '€', language })}
|
||||
</span>
|
||||
</strong>{' '}
|
||||
<Trans>d'aides</Trans> {emoji(aides.rawNode.icônes ?? '')}
|
||||
</RuleLink>
|
||||
</div>
|
||||
</Animate.fromTop>
|
||||
<Condition expression={dottedName}>
|
||||
<Animate.fromTop>
|
||||
<div className="aidesGlimpse">
|
||||
<RuleLink dottedName={aideLink}>
|
||||
<Trans>en incluant</Trans>{' '}
|
||||
<strong>
|
||||
<Value
|
||||
expression={dottedName}
|
||||
displayedUnit="€"
|
||||
unit={targetUnit}
|
||||
/>
|
||||
</strong>{' '}
|
||||
<Trans>d'aides</Trans> {emoji(aides.rawNode.icônes ?? '')}
|
||||
</RuleLink>
|
||||
</div>
|
||||
</Animate.fromTop>
|
||||
</Condition>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { setSimulationConfig, updateSituation } from 'Actions/actions'
|
|||
import Aide from 'Components/conversation/Aide'
|
||||
import { Explicable, ExplicableRule } from 'Components/conversation/Explicable'
|
||||
import RuleInput from 'Components/conversation/RuleInput'
|
||||
import { Condition } from 'Components/EngineValue'
|
||||
import Value, { Condition } from 'Components/EngineValue'
|
||||
import RuleLink from 'Components/RuleLink'
|
||||
import 'Components/TargetSelection.css'
|
||||
import Animate from 'Components/ui/animate'
|
||||
|
@ -344,7 +344,8 @@ type SimpleFieldProps = {
|
|||
function SimpleField({ dottedName, question, summary }: SimpleFieldProps) {
|
||||
const dispatch = useDispatch()
|
||||
const engine = useContext(EngineContext)
|
||||
const evaluatedRule = UNSAFE_evaluateRule(engine, dottedName)
|
||||
const evaluation = engine.evaluate(dottedName)
|
||||
const rule = engine.getRule(dottedName)
|
||||
const situation = useSelector(situationSelector)
|
||||
|
||||
const dispatchValue = useCallback(
|
||||
|
@ -361,8 +362,8 @@ function SimpleField({ dottedName, question, summary }: SimpleFieldProps) {
|
|||
|
||||
if (
|
||||
!(dottedName in situation) &&
|
||||
evaluatedRule.nodeValue === false &&
|
||||
!(dottedName in evaluatedRule.missingVariables)
|
||||
evaluation.nodeValue === false &&
|
||||
!(dottedName in evaluation.missingVariables)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
@ -381,10 +382,10 @@ function SimpleField({ dottedName, question, summary }: SimpleFieldProps) {
|
|||
`}
|
||||
>
|
||||
<p>
|
||||
{question ?? evaluatedRule.question}
|
||||
{question ?? rule.rawNode.question}
|
||||
<ExplicableRule dottedName={dottedName} />
|
||||
</p>
|
||||
<p className="ui__ notice">{summary ?? evaluatedRule.résumé}</p>
|
||||
<p className="ui__ notice">{summary ?? rule.rawNode.résumé}</p>
|
||||
</div>
|
||||
<RuleInput dottedName={dottedName} onChange={dispatchValue} />
|
||||
</Question>
|
||||
|
@ -395,8 +396,8 @@ function SimpleField({ dottedName, question, summary }: SimpleFieldProps) {
|
|||
|
||||
function Results() {
|
||||
const engine = useEngine()
|
||||
const results = (simulationConfig.objectifs as DottedName[]).map((objectif) =>
|
||||
UNSAFE_evaluateRule(engine, objectif, { unité: '€/an' })
|
||||
const rules = (simulationConfig.objectifs as DottedName[]).map((objectif) =>
|
||||
engine.getRule(objectif)
|
||||
)
|
||||
return (
|
||||
<div
|
||||
|
@ -411,18 +412,22 @@ function Results() {
|
|||
</h1>
|
||||
<>
|
||||
<Animate.fromTop>
|
||||
{results.map((r) => (
|
||||
{rules.map((r) => (
|
||||
<Fragment key={r.dottedName}>
|
||||
<h4>
|
||||
{r.title} <small>{r.résumé}</small>
|
||||
{r.title} <small>{r.rawNode.résumé}</small>
|
||||
</h4>
|
||||
{r.description && <p className="ui__ notice">{r.description}</p>}
|
||||
{r.rawNode.description && (
|
||||
<p className="ui__ notice">{r.rawNode.description}</p>
|
||||
)}
|
||||
<p className="ui__ lead" css="margin-bottom: 1rem;">
|
||||
<RuleLink dottedName={r.dottedName}>
|
||||
{formatValue(r, {
|
||||
displayedUnit: '€',
|
||||
precision: 0,
|
||||
})}
|
||||
<Value
|
||||
expression={r.dottedName}
|
||||
displayedUnit="€"
|
||||
unit="€/an"
|
||||
precision={0}
|
||||
/>
|
||||
</RuleLink>
|
||||
</p>
|
||||
</Fragment>
|
||||
|
|
|
@ -3,11 +3,11 @@ import Simulation from 'Components/Simulation'
|
|||
import Animate from 'Components/ui/animate'
|
||||
import Warning from 'Components/ui/WarningBlock'
|
||||
import { IsEmbeddedContext } from 'Components/utils/embeddedContext'
|
||||
import { EngineContext, useEngine } from 'Components/utils/EngineContext'
|
||||
import { EvaluatedRule, UNSAFE_evaluateRule, formatValue } from 'publicodes'
|
||||
import { useEngine } from 'Components/utils/EngineContext'
|
||||
import { DottedName } from 'modele-social'
|
||||
import { formatValue } from 'publicodes'
|
||||
import React, { useContext, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { DottedName } from 'modele-social'
|
||||
import styled from 'styled-components'
|
||||
|
||||
declare global {
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
// renamed the test configuration may be adapted but the persisted snapshot will remain unchanged).
|
||||
|
||||
/* eslint-disable no-undef */
|
||||
import Engine, { UNSAFE_evaluateRule } from 'publicodes'
|
||||
import rules from 'modele-social'
|
||||
import artisteAuteurConfig from '../../source/site/pages/Simulateurs/configs/artiste-auteur.yaml'
|
||||
import autoentrepreneurConfig from '../../source/site/pages/Simulateurs/configs/auto-entrepreneur.yaml'
|
||||
|
@ -17,11 +16,10 @@ import professionLibéraleConfig from '../../source/site/pages/Simulateurs/confi
|
|||
import aideDéclarationConfig from '../../source/site/pages/Gérer/AideDéclarationIndépendant/config.yaml'
|
||||
import artisteAuteurSituations from './simulations-artiste-auteur.yaml'
|
||||
import autoEntrepreneurSituations from './simulations-auto-entrepreneur.yaml'
|
||||
import professionsLibéralesSituations from './simulations-professions-libérales.yaml'
|
||||
import independentSituations from './simulations-indépendant.yaml'
|
||||
import professionsLibéralesSituations from './simulations-professions-libérales.yaml'
|
||||
import remunerationDirigeantSituations from './simulations-rémunération-dirigeant.yaml'
|
||||
import employeeSituations from './simulations-salarié.yaml'
|
||||
import aideDéclarationIndépendantsSituations from './aide-déclaration-indépendants.yaml'
|
||||
|
||||
const roundResult = (arr) => arr.map((x) => Math.round(x))
|
||||
const engine = new Engine(rules)
|
||||
|
@ -40,9 +38,11 @@ const runSimulations = (situations, targets, baseSituation = {}) =>
|
|||
const res = targets.map((target) => engine.evaluate(target).nodeValue)
|
||||
|
||||
const evaluatedNotifications = Object.values(engine.getRules())
|
||||
.filter((rule) => rule.rawNode['type'] === 'notification')
|
||||
.map((node) => UNSAFE_evaluateRule(engine, node.dottedName))
|
||||
.filter((node) => !!node.nodeValue)
|
||||
.filter(
|
||||
(rule) =>
|
||||
rule.rawNode['type'] === 'notification' &&
|
||||
!!engine.evaluate(rule.dottedName).nodeValue
|
||||
)
|
||||
.map((node) => node.dottedName)
|
||||
|
||||
const snapshotedDisplayedNotifications = evaluatedNotifications.length
|
||||
|
|
|
@ -9,4 +9,4 @@ export const transformAST = publicodes.transformAST
|
|||
export const formatValue = publicodes.formatValue
|
||||
export const utils = publicodes.utils
|
||||
export const translateRules = publicodes.translateRules
|
||||
export const UNSAFE_evaluateRule = publicodes.UNSAFE_evaluateRule
|
||||
export const UNSAFE_isNotApplicable = publicodes.UNSAFE_isNotApplicable
|
||||
|
|
Loading…
Reference in New Issue