🔥 Supprime UNSAFE_evaluateRule
parent
adcbd330bd
commit
3a8c201d44
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
"strict": true
|
||||
},
|
||||
"include": ["build.js"]
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import { hideNotification } from 'Actions/actions'
|
||||
import animate from 'Components/ui/animate'
|
||||
import { useEngine, useInversionFail } from 'Components/utils/EngineContext'
|
||||
import Engine, { EvaluatedRule } from 'publicodes'
|
||||
import Engine, { RuleNode } from 'publicodes'
|
||||
import emoji from 'react-easy-emoji'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
|
@ -14,8 +14,9 @@ import { ScrollToElement } from './utils/Scroll'
|
|||
// with the "type: notification" attribute. The display can be customized with
|
||||
// the "sévérité" attribute. The notification will only be displayed if the
|
||||
// publicode rule is applicable.
|
||||
type Notification = Pick<EvaluatedRule, 'dottedName' | 'description'> & {
|
||||
type: 'notification'
|
||||
type Notification = {
|
||||
dottedName: RuleNode['dottedName']
|
||||
description: RuleNode['rawNode']['description']
|
||||
sévérité: 'avertissement' | 'information'
|
||||
}
|
||||
|
||||
|
@ -26,12 +27,15 @@ export function getNotifications(engine: Engine) {
|
|||
rule.rawNode['type'] === 'notification' &&
|
||||
!!engine.evaluate(rule.dottedName).nodeValue
|
||||
)
|
||||
.map((node) => node.dottedName)
|
||||
.map(({ dottedName, rawNode: { sévérité, description } }) => ({
|
||||
dottedName,
|
||||
sévérité,
|
||||
description,
|
||||
}))
|
||||
}
|
||||
export default function Notifications() {
|
||||
const { t } = useTranslation()
|
||||
const engine = useEngine()
|
||||
|
||||
const inversionFail = useInversionFail()
|
||||
const hiddenNotifications = useSelector(
|
||||
(state: RootState) => state.simulation?.hiddenNotifications
|
||||
|
@ -46,11 +50,10 @@ export default function Notifications() {
|
|||
'simulateurs.inversionFail',
|
||||
'Le montant saisi abouti à un résultat impossible. Cela est dû à un effet de seuil dans le calcul des cotisations.\n\nNous vous invitons à réessayer en modifiant légèrement le montant renseigné (quelques euros de plus par exemple).'
|
||||
),
|
||||
type: 'notification',
|
||||
sévérité: 'avertissement',
|
||||
},
|
||||
]
|
||||
: ((getNotifications(engine) as any) as Array<Notification>)
|
||||
: (getNotifications(engine) as Array<Notification>)
|
||||
if (!messages?.length) return null
|
||||
|
||||
return (
|
||||
|
|
|
@ -13,6 +13,7 @@ import { Trans, useTranslation } from 'react-i18next'
|
|||
import { DottedName } from 'modele-social'
|
||||
import './PaySlip.css'
|
||||
import { Line, SalaireBrutSection, SalaireNetSection } from './PaySlipSections'
|
||||
import { RuleNode } from 'publicodes/dist/types/rule'
|
||||
|
||||
export const SECTION_ORDER = [
|
||||
'protection sociale . santé',
|
||||
|
@ -27,7 +28,7 @@ export const SECTION_ORDER = [
|
|||
|
||||
type Section = typeof SECTION_ORDER[number]
|
||||
|
||||
function getSection(rule: ASTNode & { nodeKind: 'rule' }): Section {
|
||||
function getSection(rule: RuleNode): Section {
|
||||
const section = ('protection sociale . ' +
|
||||
rule.rawNode.cotisation?.branche) as Section
|
||||
if (SECTION_ORDER.includes(section)) {
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import RuleLink from 'Components/RuleLink'
|
||||
import useDisplayOnIntersecting from 'Components/utils/useDisplayOnIntersecting'
|
||||
import { EvaluatedNode, EvaluatedRule } from 'publicodes'
|
||||
import React, { useContext } from 'react'
|
||||
import { animated, useSpring } from 'react-spring'
|
||||
import { DottedName } from 'modele-social'
|
||||
import styled from 'styled-components'
|
||||
import { EngineContext, useEngine } from './utils/EngineContext'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { targetUnitSelector } from 'Selectors/simulationSelectors'
|
||||
import { Names } from 'modele-social/dist/names'
|
||||
import { EvaluatedNode } from 'publicodes'
|
||||
import React from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { animated, useSpring } from 'react-spring'
|
||||
import { targetUnitSelector } from 'Selectors/simulationSelectors'
|
||||
import styled from 'styled-components'
|
||||
import { useEngine } from './utils/EngineContext'
|
||||
|
||||
const BarStack = styled.div`
|
||||
display: flex;
|
||||
|
@ -150,7 +149,7 @@ export default function StackedRulesChart({ data }: StackedRulesChartProps) {
|
|||
key: dottedName,
|
||||
value: engine.evaluate({ valeur: dottedName, unité: targetUnit })
|
||||
.nodeValue,
|
||||
legend: <RuleLink dottedName={dottedName} children={title} />,
|
||||
legend: <RuleLink dottedName={dottedName}>{title}</RuleLink>,
|
||||
color,
|
||||
}))}
|
||||
/>
|
||||
|
|
|
@ -14,7 +14,13 @@ import {
|
|||
import { SitePathsContext } from 'Components/utils/SitePathsContext'
|
||||
import { DottedName } from 'modele-social'
|
||||
import { Names } from 'modele-social/dist/names'
|
||||
import { ASTNode, EvaluatedRule, formatValue, reduceAST } from 'publicodes'
|
||||
import {
|
||||
ASTNode,
|
||||
EvaluatedNode,
|
||||
formatValue,
|
||||
reduceAST,
|
||||
RuleNode,
|
||||
} from 'publicodes'
|
||||
import { isNil } from 'ramda'
|
||||
import { Fragment, useCallback, useContext } from 'react'
|
||||
import emoji from 'react-easy-emoji'
|
||||
|
@ -80,6 +86,10 @@ export default function TargetSelection({ showPeriodSwitch = true }) {
|
|||
type TargetProps = {
|
||||
dottedName: DottedName
|
||||
}
|
||||
type TargetType = EvaluatedNode &
|
||||
RuleNode['rawNode'] &
|
||||
RuleNode & { dottedName: DottedName }
|
||||
|
||||
const Target = ({ dottedName }: TargetProps) => {
|
||||
const activeInput = useSelector((state: RootState) => state.activeTargetInput)
|
||||
const engine = useEngine()
|
||||
|
@ -89,7 +99,7 @@ const Target = ({ dottedName }: TargetProps) => {
|
|||
unité: useSelector(targetUnitSelector),
|
||||
arrondi: 'oui',
|
||||
})
|
||||
const target = { ...evaluation, ...rule.rawNode, ...rule }
|
||||
const target: TargetType = { ...evaluation, ...rule.rawNode, ...rule }
|
||||
const dispatch = useDispatch()
|
||||
const onSuggestionClick = useCallback(
|
||||
(value) => {
|
||||
|
@ -155,7 +165,7 @@ const Target = ({ dottedName }: TargetProps) => {
|
|||
)
|
||||
}
|
||||
|
||||
const Header = ({ target }: { target: EvaluatedRule<DottedName> }) => {
|
||||
const Header = ({ target }: { target: TargetType }) => {
|
||||
const sitePaths = useContext(SitePathsContext)
|
||||
const { t } = useTranslation()
|
||||
const { pathname } = useLocation()
|
||||
|
@ -179,7 +189,7 @@ const Header = ({ target }: { target: EvaluatedRule<DottedName> }) => {
|
|||
}
|
||||
|
||||
type TargetInputOrValueProps = {
|
||||
target: EvaluatedRule<DottedName>
|
||||
target: TargetType
|
||||
isActiveInput: boolean
|
||||
isSmallTarget: boolean
|
||||
}
|
||||
|
@ -290,7 +300,6 @@ function TitreRestaurant() {
|
|||
}
|
||||
function AidesGlimpse() {
|
||||
const targetUnit = useSelector(targetUnitSelector)
|
||||
const { language } = useTranslation().i18n
|
||||
const dottedName = 'contrat salarié . aides employeur' as Names
|
||||
const engine = useEngine()
|
||||
const aides = engine.getRule(dottedName)
|
||||
|
|
|
@ -2,7 +2,7 @@ import {
|
|||
InputCommonProps,
|
||||
RuleInputProps,
|
||||
} from 'Components/conversation/RuleInput'
|
||||
import { EvaluatedRule } from 'publicodes'
|
||||
import { RuleNode } from 'publicodes'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import InputSuggestions from './InputSuggestions'
|
||||
|
@ -12,7 +12,7 @@ type DateInputProps = {
|
|||
id: InputCommonProps['id']
|
||||
onSubmit: RuleInputProps['onSubmit']
|
||||
value: InputCommonProps['value']
|
||||
suggestions: EvaluatedRule['suggestions']
|
||||
suggestions: RuleNode['suggestions']
|
||||
}
|
||||
|
||||
export default function DateInput({
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import classnames from 'classnames'
|
||||
import { Markdown } from 'Components/utils/markdown'
|
||||
import { ASTNode } from 'publicodes'
|
||||
import { RuleNode } from 'publicodes'
|
||||
import { References } from 'publicodes-react'
|
||||
import { Rule } from 'publicodes/dist/types/rule'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
|
@ -25,7 +25,7 @@ import { binaryQuestion, InputCommonProps, RuleInputProps } from './RuleInput'
|
|||
|
||||
*/
|
||||
|
||||
export type Choice = ASTNode & { nodeKind: 'rule' } & {
|
||||
export type Choice = RuleNode & {
|
||||
canGiveUp?: boolean
|
||||
children: Array<Choice>
|
||||
}
|
||||
|
|
|
@ -7,13 +7,9 @@ import PercentageField from 'Components/PercentageField'
|
|||
import ToggleSwitch from 'Components/ui/ToggleSwitch'
|
||||
import { EngineContext } from 'Components/utils/EngineContext'
|
||||
import { DottedName } from 'modele-social'
|
||||
import Engine, {
|
||||
ASTNode,
|
||||
EvaluatedRule,
|
||||
formatValue,
|
||||
reduceAST,
|
||||
} from 'publicodes'
|
||||
import Engine, { ASTNode, formatValue, reduceAST } from 'publicodes'
|
||||
import { Evaluation } from 'publicodes/dist/types/AST/types'
|
||||
import { RuleNode } from 'publicodes/dist/types/rule'
|
||||
import React, { useContext } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import DateInput from './DateInput'
|
||||
|
@ -37,7 +33,8 @@ export type InputCommonProps<Name extends string = string> = Pick<
|
|||
RuleInputProps<Name>,
|
||||
'dottedName' | 'onChange' | 'autoFocus' | 'className'
|
||||
> &
|
||||
Pick<EvaluatedRule<Name>, 'title' | 'question' | 'suggestions'> & {
|
||||
Pick<RuleNode, 'title' | 'suggestions'> & {
|
||||
question: RuleNode['rawNode']['question']
|
||||
key: string
|
||||
id: string
|
||||
value: any //TODO EvaluatedRule['nodeValue']
|
||||
|
@ -181,7 +178,7 @@ export default function RuleInput<Name extends string = DottedName>({
|
|||
)
|
||||
}
|
||||
|
||||
const getVariant = (node: ASTNode & { nodeKind: 'rule' }) =>
|
||||
const getVariant = (node: RuleNode) =>
|
||||
reduceAST<false | (ASTNode & { nodeKind: 'une possibilité' })>(
|
||||
(_, node) => {
|
||||
if (node.nodeKind === 'une possibilité') {
|
||||
|
|
|
@ -46,7 +46,7 @@ export default function SelectEuropeCountry({
|
|||
name="country"
|
||||
id={id}
|
||||
className="ui__"
|
||||
defaultValue={value?.slice(1, -1)}
|
||||
defaultValue={value ? value.slice(1, -1) : undefined}
|
||||
onChange={(e) => onChange(`'${e.target.value}'`)}
|
||||
>
|
||||
<option disabled selected hidden></option>
|
||||
|
|
|
@ -11,13 +11,13 @@ import { EngineContext, useEngine } from 'Components/utils/EngineContext'
|
|||
import { ScrollToTop } from 'Components/utils/Scroll'
|
||||
import useDisplayOnIntersecting from 'Components/utils/useDisplayOnIntersecting'
|
||||
import { useNextQuestions } from 'Components/utils/useNextQuestion'
|
||||
import { EvaluatedRule, UNSAFE_evaluateRule, formatValue } from 'publicodes'
|
||||
import { Fragment, useCallback, useContext, useEffect, useState } from 'react'
|
||||
import { DottedName } from 'modele-social'
|
||||
import { RuleNode } from 'publicodes'
|
||||
import { Fragment, useCallback, useContext, useEffect } from 'react'
|
||||
import emoji from 'react-easy-emoji'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { RootState } from 'Reducers/rootReducer'
|
||||
import { DottedName } from 'modele-social'
|
||||
import { situationSelector } from 'Selectors/simulationSelectors'
|
||||
import styled from 'styled-components'
|
||||
import { CompanySection } from '../Home'
|
||||
|
@ -338,8 +338,8 @@ function SubSection({
|
|||
|
||||
type SimpleFieldProps = {
|
||||
dottedName: DottedName
|
||||
summary?: EvaluatedRule['résumé']
|
||||
question?: EvaluatedRule['question']
|
||||
summary?: RuleNode['rawNode']['résumé']
|
||||
question?: RuleNode['rawNode']['question']
|
||||
}
|
||||
function SimpleField({ dottedName, question, summary }: SimpleFieldProps) {
|
||||
const dispatch = useDispatch()
|
||||
|
|
|
@ -2,11 +2,13 @@ import { BlobProvider } from '@react-pdf/renderer'
|
|||
import Overlay from 'Components/Overlay'
|
||||
import Checkbox from 'Components/ui/Checkbox'
|
||||
import { ThemeColorsContext } from 'Components/utils/colors'
|
||||
import { EngineContext, EngineProvider } from 'Components/utils/EngineContext'
|
||||
import { TrackerContext } from 'Components/utils/withTracker'
|
||||
import { RuleNode } from 'publicodes/dist/types/rule'
|
||||
import { lazy, Suspense, useContext, useRef, useState } from 'react'
|
||||
import emoji from 'react-easy-emoji'
|
||||
import SignaturePad from 'react-signature-pad-wrapper'
|
||||
import PDFDocument, { PDFDocumentProps } from './PDFDocument'
|
||||
import PDFDocument from './PDFDocument'
|
||||
|
||||
const IS_TOUCH_DEVICE = isOnTouchDevice()
|
||||
type SignaturePadInstance = {
|
||||
|
@ -15,7 +17,7 @@ type SignaturePadInstance = {
|
|||
}
|
||||
|
||||
type EndBlockProps = {
|
||||
fields: PDFDocumentProps['fields']
|
||||
fields: Array<RuleNode>
|
||||
isMissingValues: boolean
|
||||
}
|
||||
|
||||
|
@ -23,7 +25,7 @@ export default function EndBlock({ fields, isMissingValues }: EndBlockProps) {
|
|||
const [isCertified, setCertified] = useState(false)
|
||||
const [place, setPlace] = useState<string>()
|
||||
const [showDownloadLink, toggleDownloadLink] = useState(false)
|
||||
|
||||
const engine = useContext(EngineContext)
|
||||
const { darkColor } = useContext(ThemeColorsContext)
|
||||
const signatureRef = useRef<SignaturePadInstance>()
|
||||
const tracker = useContext(TrackerContext)
|
||||
|
@ -131,13 +133,15 @@ export default function EndBlock({ fields, isMissingValues }: EndBlockProps) {
|
|||
>
|
||||
<LazyBlobProvider
|
||||
document={
|
||||
<PDFDocument
|
||||
fields={fields}
|
||||
signatureURL={
|
||||
IS_TOUCH_DEVICE && signatureRef.current?.toDataURL()
|
||||
}
|
||||
place={place}
|
||||
/>
|
||||
<EngineProvider value={engine}>
|
||||
<PDFDocument
|
||||
fields={fields}
|
||||
signatureURL={
|
||||
IS_TOUCH_DEVICE && signatureRef.current?.toDataURL()
|
||||
}
|
||||
place={place}
|
||||
/>
|
||||
</EngineProvider>
|
||||
}
|
||||
>
|
||||
{({ url, loading, error }) =>
|
||||
|
@ -175,7 +179,7 @@ export default function EndBlock({ fields, isMissingValues }: EndBlockProps) {
|
|||
])
|
||||
}
|
||||
className="ui__ cta plain button"
|
||||
download="demande-mobilité-europe.pdf"
|
||||
download="demande-mobilité-internationale.pdf"
|
||||
>
|
||||
Télécharger le fichier
|
||||
</a>
|
||||
|
|
|
@ -1,47 +1,51 @@
|
|||
import { StyleSheet, Text, View } from '@react-pdf/renderer'
|
||||
import { formatValue, EvaluatedRule } from 'publicodes'
|
||||
import Value from 'Components/EngineValue'
|
||||
import { EngineContext } from 'Components/utils/EngineContext'
|
||||
import { formatValue } from 'publicodes'
|
||||
import { RuleNode } from 'publicodes/dist/types/rule'
|
||||
import { useContext } from 'react'
|
||||
|
||||
export type FieldsPDFProps = {
|
||||
fields: Array<EvaluatedRule<string>>
|
||||
type FieldsPDFProps = {
|
||||
fields: Array<RuleNode>
|
||||
}
|
||||
|
||||
export default function FieldsPDF({ fields }: FieldsPDFProps) {
|
||||
const engine = useContext(EngineContext)
|
||||
return (
|
||||
<>
|
||||
{fields.map((field) => (
|
||||
<View style={styles.field} key={field.dottedName} wrap={false}>
|
||||
{field.type === 'groupe' ? (
|
||||
<>
|
||||
<Text style={styles.subtitle}>
|
||||
{field.title}{' '}
|
||||
{field.note && (
|
||||
<Text style={styles.fieldNumber}>({field.note})</Text>
|
||||
)}
|
||||
</Text>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Text style={styles.name}>
|
||||
{field.question ?? field.title}{' '}
|
||||
{field.note && (
|
||||
<Text style={styles.fieldNumber}>({field.note})</Text>
|
||||
)}
|
||||
</Text>
|
||||
{field.nodeValue != null && (
|
||||
<Text style={styles.value}>
|
||||
{formatValue(field) +
|
||||
(field.API === 'commune'
|
||||
? ` (${
|
||||
(field.nodeValue as Record<string, unknown>)
|
||||
.codePostal as string
|
||||
})`
|
||||
: '')}
|
||||
{fields.map(
|
||||
({ rawNode: { type, question, note, API }, title, dottedName }) => (
|
||||
<View style={styles.field} key={dottedName} wrap={false}>
|
||||
{type === 'groupe' ? (
|
||||
<>
|
||||
<Text style={styles.subtitle}>
|
||||
{title}{' '}
|
||||
{note && <Text style={styles.fieldNumber}>({note})</Text>}
|
||||
</Text>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Text style={styles.name}>
|
||||
{question ?? title}{' '}
|
||||
{note && <Text style={styles.fieldNumber}>({note})</Text>}
|
||||
</Text>
|
||||
|
||||
<Text style={styles.value}>
|
||||
{formatValue(engine.evaluate(dottedName)) +
|
||||
(API === 'commune'
|
||||
? ` (${
|
||||
(engine.evaluate(dottedName).nodeValue as Record<
|
||||
string,
|
||||
unknown
|
||||
>)?.codePostal as string
|
||||
})`
|
||||
: '')}{' '}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,19 +2,20 @@ import ReactPDF, {
|
|||
Document,
|
||||
Font,
|
||||
Image,
|
||||
Link,
|
||||
Page,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
Link,
|
||||
} from '@react-pdf/renderer'
|
||||
import urssafPng from 'Images/destinataires/URSSAF.png'
|
||||
import FieldsPDF, { FieldsPDFProps, styles as fieldStyles } from './FieldsPDF'
|
||||
import { RuleNode } from 'publicodes'
|
||||
import FieldsPDF, { styles as fieldStyles } from './FieldsPDF'
|
||||
import montserratUrl from './Montserrat-SemiBold.ttf'
|
||||
import robotoUrl from './Roboto-Regular.ttf'
|
||||
|
||||
export type PDFDocumentProps = {
|
||||
fields: FieldsPDFProps['fields']
|
||||
fields: Array<RuleNode>
|
||||
signatureURL?: ReactPDF.SourceObject | false
|
||||
place?: string
|
||||
}
|
||||
|
@ -33,8 +34,8 @@ export default function PDFDocument({
|
|||
<View>
|
||||
<Text style={styles.title}>
|
||||
{fields.find(({ dottedName }) => dottedName === 'détachement')
|
||||
? 'Demande de détachement en Europe'
|
||||
: "Demande d'activité transfrontalière simultanée en Europe"}
|
||||
? 'Demande de détachement'
|
||||
: "Demande d'activité transfrontalière"}
|
||||
</Text>
|
||||
<Text style={styles.texte}>
|
||||
Afin d’examiner votre situation au regard des règlements
|
||||
|
|
|
@ -139,9 +139,9 @@ demande . détachement possible:
|
|||
|
||||
détachement:
|
||||
note: 3.3
|
||||
applicable si: demande . détachement possible
|
||||
titre: Demande de détachement
|
||||
formule: demande . détachement possible
|
||||
par défaut: non
|
||||
formule: oui
|
||||
type: groupe
|
||||
|
||||
détachement . pays:
|
||||
|
@ -195,10 +195,11 @@ détachement . activité . code postal:
|
|||
type: texte
|
||||
|
||||
activité transfrontalière simultanée:
|
||||
formule: demande . détachement possible = non
|
||||
par défaut: non
|
||||
non applicable si: demande . détachement possible
|
||||
formule: oui
|
||||
titre: Demande d'activité transfrontalière simultanée
|
||||
type: groupe
|
||||
|
||||
activité transfrontalière simultanée . salarié hors France:
|
||||
question: >
|
||||
Travaillez-vous en tant que salarié dans un autre pays ?
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
import { Explicable } from 'Components/conversation/Explicable'
|
||||
import RuleInput from 'Components/conversation/RuleInput'
|
||||
import { Condition } from 'Components/EngineValue'
|
||||
import * as Animate from 'Components/ui/animate'
|
||||
import Emoji from 'Components/utils/Emoji'
|
||||
import { EngineContext, EngineProvider } from 'Components/utils/EngineContext'
|
||||
import { Markdown } from 'Components/utils/markdown'
|
||||
import { usePersistingState } from 'Components/utils/persistState'
|
||||
import Engine, { UNSAFE_evaluateRule, EvaluatedRule } from 'publicodes'
|
||||
import { equals } from 'ramda'
|
||||
import Engine, { UNSAFE_isNotApplicable } from 'publicodes'
|
||||
import { equals, isEmpty } from 'ramda'
|
||||
import {
|
||||
lazy,
|
||||
createElement,
|
||||
lazy,
|
||||
Suspense,
|
||||
useCallback,
|
||||
useState,
|
||||
useContext,
|
||||
useState,
|
||||
} from 'react'
|
||||
import emoji from 'react-easy-emoji'
|
||||
import { hash } from '../../../../utils'
|
||||
|
@ -91,21 +92,24 @@ export default function FormulaireMobilitéIndépendant() {
|
|||
|
||||
const useFields = (
|
||||
engine: Engine<string>,
|
||||
fieldNames: Array<string>,
|
||||
situation: Record<string, unknown>
|
||||
): Array<EvaluatedRule> => {
|
||||
const fields = fieldNames
|
||||
.map((name) => UNSAFE_evaluateRule(engine, name))
|
||||
.filter(
|
||||
(node: EvaluatedRule) =>
|
||||
node.isNotApplicable !== true &&
|
||||
// TODO change this when not applicable value can be differenciated from false value
|
||||
(equals(node.missingVariables, { [node.dottedName]: 1 }) ||
|
||||
node.dottedName in situation ||
|
||||
(node.nodeValue !== false && node.nodeValue !== null)) &&
|
||||
(node.question || ((node.type || node.API) && node.nodeValue !== false))
|
||||
)
|
||||
return fields
|
||||
fieldNames: Array<string>
|
||||
): Array<ReturnType<Engine['getRule']>> => {
|
||||
return fieldNames
|
||||
.filter((dottedName) => {
|
||||
const isNotApplicable = UNSAFE_isNotApplicable(engine, dottedName)
|
||||
const evaluation = engine.evaluate(dottedName)
|
||||
const rule = engine.getRule(dottedName)
|
||||
return (
|
||||
isNotApplicable === false &&
|
||||
(equals(evaluation.missingVariables, { [dottedName]: 1 }) ||
|
||||
isEmpty(evaluation.missingVariables)) &&
|
||||
(rule.rawNode.question ||
|
||||
rule.rawNode.API ||
|
||||
rule.rawNode.type ||
|
||||
rule.rawNode.description)
|
||||
)
|
||||
})
|
||||
.map((dottedName) => engine.getRule(dottedName))
|
||||
}
|
||||
|
||||
const VERSION = hash(JSON.stringify(formulaire))
|
||||
|
@ -133,64 +137,66 @@ function FormulairePublicodes() {
|
|||
}, [clearFieldsKey, setSituation])
|
||||
|
||||
engine.setSituation(situation)
|
||||
const fields = useFields(engine, Object.keys(formulaire), situation)
|
||||
const missingValues = fields.filter(
|
||||
({ dottedName, type }) =>
|
||||
type !== 'groupe' &&
|
||||
(situation[dottedName] == null || situation[dottedName] === '')
|
||||
const fields = useFields(engine, Object.keys(formulaire))
|
||||
|
||||
const isMissingValues = fields.some(
|
||||
({ dottedName }) => !isEmpty(engine.evaluate(dottedName).missingVariables)
|
||||
)
|
||||
const isMissingValues = !!missingValues.length
|
||||
return (
|
||||
<Animate.fromTop key={clearFieldsKey}>
|
||||
{fields.map((field) => (
|
||||
<Animate.fromTop key={field.dottedName}>
|
||||
{field.type === 'groupe' ? (
|
||||
<>
|
||||
{createElement(
|
||||
`h${Math.min(field.dottedName.split(' . ').length + 1, 6)}`,
|
||||
{},
|
||||
field.title
|
||||
)}
|
||||
{field.description && <Markdown source={field.description} />}
|
||||
</>
|
||||
) : field.type === 'notification' && field.nodeValue === true ? (
|
||||
<small
|
||||
css={`
|
||||
color: #ff2d96;
|
||||
`}
|
||||
>
|
||||
{field.description}
|
||||
</small>
|
||||
) : (
|
||||
<>
|
||||
<label htmlFor={field.dottedName}>
|
||||
{field.question ? (
|
||||
<span
|
||||
css={`
|
||||
margin-top: 0.6rem;
|
||||
`}
|
||||
>
|
||||
{field.question}
|
||||
</span>
|
||||
) : (
|
||||
<small>{field.title}</small>
|
||||
)}{' '}
|
||||
</label>
|
||||
{field.description && (
|
||||
<Explicable>
|
||||
<h3>{field.title}</h3>
|
||||
<Markdown source={field.description} />
|
||||
</Explicable>
|
||||
)}
|
||||
<RuleInput
|
||||
id={field.dottedName}
|
||||
dottedName={field.dottedName}
|
||||
onChange={(value) => onChange(field.dottedName, value)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Animate.fromTop>
|
||||
))}
|
||||
{fields.map(
|
||||
({ rawNode: { description, type, question }, title, dottedName }) => (
|
||||
<Animate.fromTop key={dottedName}>
|
||||
{type === 'groupe' ? (
|
||||
<>
|
||||
{createElement(
|
||||
`h${Math.min(dottedName.split(' . ').length + 1, 6)}`,
|
||||
{},
|
||||
title
|
||||
)}
|
||||
{description && <Markdown source={description} />}
|
||||
</>
|
||||
) : type === 'notification' ? (
|
||||
<Condition expression={dottedName}>
|
||||
<small
|
||||
css={`
|
||||
color: #ff2d96;
|
||||
`}
|
||||
>
|
||||
{description}
|
||||
</small>
|
||||
</Condition>
|
||||
) : (
|
||||
<>
|
||||
<label htmlFor={dottedName}>
|
||||
{question ? (
|
||||
<span
|
||||
css={`
|
||||
margin-top: 0.6rem;
|
||||
`}
|
||||
>
|
||||
{question}
|
||||
</span>
|
||||
) : (
|
||||
<small>{title}</small>
|
||||
)}{' '}
|
||||
</label>
|
||||
{description && (
|
||||
<Explicable>
|
||||
<h3>{title}</h3>
|
||||
<Markdown source={description} />
|
||||
</Explicable>
|
||||
)}
|
||||
<RuleInput
|
||||
id={dottedName}
|
||||
dottedName={dottedName}
|
||||
onChange={(value) => onChange(dottedName, value)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Animate.fromTop>
|
||||
)
|
||||
)}
|
||||
|
||||
<Suspense fallback={null}>
|
||||
<LazyEndBlock fields={fields} isMissingValues={isMissingValues} />
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
/* eslint-disable no-undef */
|
||||
import rules from 'modele-social'
|
||||
import Engine from 'publicodes'
|
||||
import artisteAuteurConfig from '../../source/site/pages/Simulateurs/configs/artiste-auteur.yaml'
|
||||
import autoentrepreneurConfig from '../../source/site/pages/Simulateurs/configs/auto-entrepreneur.yaml'
|
||||
import independantConfig from '../../source/site/pages/Simulateurs/configs/indépendant.yaml'
|
||||
|
@ -20,6 +21,7 @@ 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)
|
||||
|
|
|
@ -115,9 +115,9 @@
|
|||
"prepare": "if [ -z \"$NETLIFY\" ]; then yarn workspaces run prepare; fi",
|
||||
"lint": "yarn lint:eslintrc && yarn lint:eslint && yarn lint:prettier",
|
||||
"test": "yarn workspaces run test",
|
||||
"test:type": "yarn workspace publicodes run tsc && yarn workspace publicodes-react run tsc && yarn workspace mon-entreprise run tsc",
|
||||
"test:type": "yarn workspaces run tsc",
|
||||
"test:regressions": "jest",
|
||||
"clean": "yarn workspaces run clean",
|
||||
"clean": "yarn workspaces run clean && rimraf node_modules",
|
||||
"start": "yarn workspace publicodes build --watch & yarn workspace publicodes-react build --watch & yarn workspace mon-entreprise start",
|
||||
"moso:up": "yarn workspace modele-social run up && yarn workspace mon-entreprise upgrade modele-social",
|
||||
"publicodes:up": "yarn workspace publicodes-react upgrade publicodes && yarn workspace mon-entreprise upgrade publicodes publicodes-react"
|
||||
|
|
|
@ -10,3 +10,4 @@ export const formatValue = publicodes.formatValue
|
|||
export const utils = publicodes.utils
|
||||
export const translateRules = publicodes.translateRules
|
||||
export const UNSAFE_isNotApplicable = publicodes.UNSAFE_isNotApplicable
|
||||
export const mecanismsDoc = publicodes.mecanismsDoc
|
||||
|
|
|
@ -1,9 +1,4 @@
|
|||
<<<<<<< HEAD:publicodes/core/source/error.ts
|
||||
const coerceArray = (x) => (Array.isArray(x) ? x : [x])
|
||||
=======
|
||||
import { Logger } from '.'
|
||||
import { Context } from './parsePublicodes'
|
||||
>>>>>>> 2c06fb45 (:fire: Ajoute la possibilité de définir un logger pour l'engine):publicodes/source/error.ts
|
||||
|
||||
export class EngineError extends Error {}
|
||||
export function syntaxError(
|
||||
|
|
|
@ -3,7 +3,6 @@ import { compose, mapObjIndexed } from 'ramda'
|
|||
import { reduceAST } from './AST'
|
||||
import { ASTNode, EvaluatedNode, NodeKind } from './AST/types'
|
||||
import { evaluationFunctions } from './evaluationFunctions'
|
||||
import { simplifyNodeUnit } from './nodeUnits'
|
||||
import parse from './parse'
|
||||
import parsePublicodes, { disambiguateReference } from './parsePublicodes'
|
||||
import {
|
||||
|
@ -13,11 +12,6 @@ import {
|
|||
} from './replacement'
|
||||
import { Rule, RuleNode } from './rule'
|
||||
import * as utils from './ruleUtils'
|
||||
<<<<<<< HEAD:publicodes/core/source/index.ts
|
||||
import { reduceAST } from './AST'
|
||||
import mecanismsDoc from '../../docs/mecanisms.yaml'
|
||||
=======
|
||||
>>>>>>> 2c06fb45 (:fire: Ajoute la possibilité de définir un logger pour l'engine):publicodes/source/index.ts
|
||||
|
||||
const emptyCache = () => ({
|
||||
_meta: { ruleStack: [] },
|
||||
|
@ -47,16 +41,13 @@ export type EvaluationOptions = Partial<{
|
|||
export * as cyclesLib from './AST/graph'
|
||||
export { reduceAST, transformAST } from './AST/index'
|
||||
export { Evaluation, Unit } from './AST/types'
|
||||
export { formatValue, capitalise0 } from './format'
|
||||
export { serializeUnit } from './units'
|
||||
export { capitalise0, formatValue } from './format'
|
||||
export { simplifyNodeUnit } from './nodeUnits'
|
||||
export { default as translateRules } from './translateRules'
|
||||
export { ASTNode, EvaluatedNode }
|
||||
export { parsePublicodes }
|
||||
export { mecanismsDoc }
|
||||
export { utils }
|
||||
export { Rule }
|
||||
|
||||
export { serializeUnit } from './units'
|
||||
export { parsePublicodes, utils }
|
||||
export { Rule, RuleNode, ASTNode, EvaluatedNode }
|
||||
export { default as mecanismsDoc } from '../../docs/mecanisms.yaml'
|
||||
type PublicodesExpression = string | Record<string, unknown> | number
|
||||
|
||||
export type Logger = {
|
||||
|
@ -166,7 +157,7 @@ export default class Engine<Name extends string = string> {
|
|||
disambiguateReference(this.parsedRules)
|
||||
)(
|
||||
parse(value, {
|
||||
dottedName: `evaluation`,
|
||||
dottedName: 'evaluation',
|
||||
parsedRules: {},
|
||||
logger: this.logger,
|
||||
})
|
||||
|
@ -189,7 +180,7 @@ export default class Engine<Name extends string = string> {
|
|||
}
|
||||
|
||||
/**
|
||||
This function allows to mimic the old 'isApplicable' property on evaluatedRules
|
||||
This function allows to mimic the former 'isApplicable' property on evaluatedRules
|
||||
|
||||
It will be deprecated when applicability will be encoded as a Literal type
|
||||
*/
|
||||
|
@ -216,7 +207,10 @@ export function UNSAFE_isNotApplicable<DottedName extends string = string>(
|
|||
return fn(engine.evaluate(rule))
|
||||
}
|
||||
if (node.nodeKind === 'applicable si') {
|
||||
return (node.explanation.condition as any).nodeValue === false
|
||||
return (
|
||||
(node.explanation.condition as any).nodeValue === false ||
|
||||
fn(node.explanation.valeur)
|
||||
)
|
||||
}
|
||||
if (node.nodeKind === 'non applicable si') {
|
||||
return (
|
||||
|
@ -224,45 +218,14 @@ export function UNSAFE_isNotApplicable<DottedName extends string = string>(
|
|||
(node.explanation.condition as any).nodeValue !== null
|
||||
)
|
||||
}
|
||||
if (node.nodeKind === 'rule') {
|
||||
return (
|
||||
(node.explanation.parent as any).nodeValue === false ||
|
||||
fn(node.explanation.valeur)
|
||||
)
|
||||
}
|
||||
},
|
||||
false,
|
||||
engine.evaluate(dottedName)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
This function allows smother migration to the new Engine API
|
||||
|
||||
It will be deprecated when applicability will be encoded as a Literal type
|
||||
Prefer the use of `engine.evaluate(engine.getRule(dottedName))`
|
||||
*/
|
||||
export function UNSAFE_evaluateRule<DottedName extends string = string>(
|
||||
engine: Engine<DottedName>,
|
||||
dottedName: DottedName,
|
||||
modifiers: Object = {}
|
||||
): EvaluatedRule<DottedName> {
|
||||
const evaluation = simplifyNodeUnit(
|
||||
engine.evaluate({ valeur: dottedName, ...modifiers })
|
||||
)
|
||||
const rule = engine.getRule(dottedName) as RuleNode & {
|
||||
dottedName: DottedName
|
||||
}
|
||||
|
||||
return {
|
||||
isNotApplicable: UNSAFE_isNotApplicable(engine, dottedName),
|
||||
...rule.rawNode,
|
||||
...rule,
|
||||
...evaluation,
|
||||
} as EvaluatedRule<DottedName>
|
||||
}
|
||||
|
||||
export type EvaluatedRule<Name extends string = string> = EvaluatedNode &
|
||||
Omit<
|
||||
(ASTNode & {
|
||||
nodeKind: 'rule'
|
||||
}) &
|
||||
(ASTNode & {
|
||||
nodeKind: 'rule'
|
||||
})['rawNode'] & { dottedName: Name; isNotApplicable: boolean },
|
||||
'nodeKind'
|
||||
>
|
||||
|
|
|
@ -24,6 +24,7 @@ export type Rule = {
|
|||
résumé?: string
|
||||
icônes?: string
|
||||
titre?: string
|
||||
sévérité?: string
|
||||
cotisation?: {
|
||||
branche: string
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"clean": "rimraf dist node_modules",
|
||||
"prepare": "yarn run build",
|
||||
"test": "echo \"Error: no test specified\""
|
||||
},
|
||||
|
|
|
@ -278,7 +278,7 @@ export function Leaf(
|
|||
const { dottedName, nodeValue, unit } = node
|
||||
const rule = engine?.getRule(node.dottedName)
|
||||
if (!rule) {
|
||||
throw new Error(`Unknown node`)
|
||||
throw new Error('Unknown node')
|
||||
}
|
||||
|
||||
const [folded, setFolded] = useState(true)
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
"license": "MIT",
|
||||
"scripts": {
|
||||
"prepare": "echo 1",
|
||||
"test": "echo 1"
|
||||
"test": "echo 1",
|
||||
"clean": "rimraf node_modules"
|
||||
},
|
||||
"devDependencies": {
|
||||
"core-js": "^3.8.1"
|
||||
|
|
Loading…
Reference in New Issue