✨ Refacto de ExonérationCovid
parent
82016d3a17
commit
2f43d541e7
|
@ -2,9 +2,9 @@
|
|||
// sub-section of them. We might support "code-splitting" the rules in the
|
||||
// future.
|
||||
import { Rule } from 'publicodes'
|
||||
import { Names } from './dist/names'
|
||||
import { Names as ExoCovidDottedNames } from './dist/names'
|
||||
|
||||
export type DottedNames = Names
|
||||
declare let rules: Record<Names, Rule>
|
||||
declare let rules: Record<ExoCovidDottedNames, Rule>
|
||||
|
||||
export type { ExoCovidDottedNames }
|
||||
export default rules
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { DottedName } from 'modele-social'
|
||||
import Engine, { PublicodesExpression, Rule } from 'publicodes'
|
||||
import React, { createContext, useContext, useRef } from 'react'
|
||||
import React, { createContext, useContext } from 'react'
|
||||
import i18n from '../../locales/i18n'
|
||||
|
||||
export type Rules = Record<DottedName, Rule>
|
||||
|
@ -23,21 +23,8 @@ export function engineFactory(rules: Rules, options = {}) {
|
|||
export const EngineContext = createContext<Engine>(new Engine())
|
||||
export const EngineProvider = EngineContext.Provider
|
||||
|
||||
export function useEngine<Names extends string = DottedName>() {
|
||||
return useContext(EngineContext) as Engine<Names>
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this hooks to keep state of engine with the react fast refresh
|
||||
* @param originalEngine
|
||||
* @returns engine
|
||||
*/
|
||||
export const useEngineKeepState = <Names extends string>(
|
||||
originalEngine: Engine<Names>
|
||||
) => {
|
||||
const { current: engine } = useRef(originalEngine)
|
||||
|
||||
return engine
|
||||
export function useEngine() {
|
||||
return useContext(EngineContext) as Engine<DottedName>
|
||||
}
|
||||
|
||||
type SituationProviderProps<Names extends string> = {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { PublicodesExpression } from 'publicodes'
|
||||
import Engine, { PublicodesExpression } from 'publicodes'
|
||||
import {
|
||||
createContext,
|
||||
Dispatch,
|
||||
|
@ -7,7 +7,6 @@ import {
|
|||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useEngine } from './EngineContext'
|
||||
|
||||
export type Situation<Names extends string> = Partial<
|
||||
Record<Names, PublicodesExpression | undefined>
|
||||
|
@ -28,10 +27,9 @@ export interface SituationState<Names extends string> {
|
|||
* @returns situation state
|
||||
*/
|
||||
export const useSynchronizedSituationState = <Names extends string>(
|
||||
engine: Engine<Names>,
|
||||
defaultSituation: Situation<Names> | (() => Situation<Names>) = {}
|
||||
): SituationState<Names> => {
|
||||
const engine = useEngine<Names>()
|
||||
|
||||
const [localSituation, setLocalSituation] =
|
||||
useState<Situation<Names>>(defaultSituation)
|
||||
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
import RuleInput from '@/components/conversation/RuleInput'
|
||||
import {
|
||||
SituationStateProvider,
|
||||
useSynchronizedSituationState,
|
||||
} from '@/components/utils/SituationContext'
|
||||
import { Button } from '@/design-system/buttons'
|
||||
import { Spacing } from '@/design-system/layout'
|
||||
import { H3 } from '@/design-system/typography/heading'
|
||||
import { Grid } from '@mui/material'
|
||||
import { ExoCovidDottedNames } from 'exoneration-covid'
|
||||
import { PublicodesExpression } from 'publicodes'
|
||||
import { useCallback, useEffect } from 'react'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { useLocation } from 'react-router'
|
||||
import { useExoCovidEngine } from '.'
|
||||
import { FormulaireS1S1Bis } from './FormulaireS1S1Bis'
|
||||
import { FormulaireS2 } from './FormulaireS2'
|
||||
|
||||
const rootDottedNames = [
|
||||
'secteur',
|
||||
"début d'activité",
|
||||
"lieu d'exercice",
|
||||
] as const
|
||||
|
||||
export const ExonérationCovid = () => {
|
||||
const location = useLocation()
|
||||
const searchParams = new URLSearchParams(location.search)
|
||||
const params = Object.fromEntries(searchParams.entries()) as {
|
||||
[key in typeof rootDottedNames[number]]?: string
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0)
|
||||
}, [location])
|
||||
|
||||
const engine = useExoCovidEngine()
|
||||
const situationState = useSynchronizedSituationState(engine, params)
|
||||
const { situation, setSituation } = situationState
|
||||
|
||||
const updateSituation = useCallback(
|
||||
(name: ExoCovidDottedNames, value: PublicodesExpression | undefined) => {
|
||||
setSituation({ ...situation, [name]: value })
|
||||
},
|
||||
[setSituation, situation]
|
||||
)
|
||||
|
||||
const setStep1Situation = useCallback(() => {
|
||||
const step1Situation = Object.fromEntries(
|
||||
Object.entries(situation).filter(([dotName]) =>
|
||||
(rootDottedNames as readonly string[]).includes(dotName)
|
||||
)
|
||||
)
|
||||
setSituation(step1Situation)
|
||||
}, [setSituation, situation])
|
||||
|
||||
const step2 = rootDottedNames.every((names) => params[names])
|
||||
|
||||
return (
|
||||
<SituationStateProvider value={situationState}>
|
||||
{step2 ? (
|
||||
engine.evaluate('secteur').nodeValue !== 'S2' ? (
|
||||
<FormulaireS1S1Bis onChange={updateSituation} />
|
||||
) : (
|
||||
<FormulaireS2 onChange={updateSituation} />
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
<Grid item xs={12}>
|
||||
<H3>{engine.getRule('secteur').rawNode.question}</H3>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={8}>
|
||||
<RuleInput
|
||||
dottedName={'secteur'}
|
||||
onChange={(value) => updateSituation('secteur', value)}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<H3>{engine.getRule("début d'activité").rawNode.question}</H3>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6}>
|
||||
<RuleInput
|
||||
dottedName="début d'activité"
|
||||
onChange={(value) => updateSituation("début d'activité", value)}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<H3>{engine.getRule("lieu d'exercice").rawNode.question}</H3>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<RuleInput
|
||||
dottedName="lieu d'exercice"
|
||||
onChange={(value) => updateSituation("lieu d'exercice", value)}
|
||||
/>
|
||||
</Grid>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Spacing lg />
|
||||
|
||||
<Grid container justifyContent={step2 ? '' : 'end'}>
|
||||
<Grid item xs={6} sm="auto">
|
||||
{step2 ? (
|
||||
<Button
|
||||
size="MD"
|
||||
to={{
|
||||
pathname: location.pathname,
|
||||
search: '',
|
||||
}}
|
||||
onClick={setStep1Situation}
|
||||
>
|
||||
← <Trans>Précédent</Trans>
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
size="MD"
|
||||
isDisabled={!rootDottedNames.every((names) => situation[names])}
|
||||
{...(rootDottedNames.every((names) => situation[names])
|
||||
? {
|
||||
to: () => {
|
||||
rootDottedNames.forEach((key) =>
|
||||
searchParams.append(
|
||||
key,
|
||||
situation[key]?.toString() ?? ''
|
||||
)
|
||||
)
|
||||
|
||||
return {
|
||||
pathname: location.pathname,
|
||||
search: searchParams.toString(),
|
||||
}
|
||||
},
|
||||
}
|
||||
: null)}
|
||||
>
|
||||
<Trans>Suivant</Trans> →
|
||||
</Button>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Spacing lg />
|
||||
</SituationStateProvider>
|
||||
)
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
import Value from '@/components/EngineValue'
|
||||
import { EngineContext } from '@/components/utils/EngineContext'
|
||||
import {
|
||||
Situation,
|
||||
useSituationState,
|
||||
|
@ -8,16 +7,16 @@ import { Spacing } from '@/design-system/layout'
|
|||
import { H3 } from '@/design-system/typography/heading'
|
||||
import { Li, Ul } from '@/design-system/typography/list'
|
||||
import { Grid } from '@mui/material'
|
||||
import { DottedNames } from 'exoneration-covid'
|
||||
import { ExoCovidDottedNames } from 'exoneration-covid'
|
||||
import Engine, { EvaluatedNode, PublicodesExpression } from 'publicodes'
|
||||
import { useContext } from 'react'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { useExoCovidEngine } from '.'
|
||||
import { Bold, GridTotal, Italic, Recap, RecapExpert, Total } from './Recap'
|
||||
import { Row, Table, Tbody, Th, Thead, Tr } from './Table'
|
||||
|
||||
const getTotalByMonth = (
|
||||
engine: Engine<DottedNames>,
|
||||
situation: Situation<DottedNames>
|
||||
engine: Engine<ExoCovidDottedNames>,
|
||||
situation: Situation<ExoCovidDottedNames>
|
||||
) => {
|
||||
const ret = Object.fromEntries(
|
||||
Object.entries(situation)
|
||||
|
@ -45,12 +44,15 @@ const getTotalByMonth = (
|
|||
}
|
||||
|
||||
interface Props {
|
||||
onChange?: (dottedName: DottedNames, value: PublicodesExpression) => void
|
||||
onChange?: (
|
||||
dottedName: ExoCovidDottedNames,
|
||||
value: PublicodesExpression
|
||||
) => void
|
||||
}
|
||||
|
||||
export const FormulaireS1S1Bis = ({ onChange }: Props) => {
|
||||
const engine = useContext(EngineContext) as Engine<DottedNames>
|
||||
const { situation = {} } = useSituationState<DottedNames>()
|
||||
const engine = useExoCovidEngine()
|
||||
const { situation = {} } = useSituationState<ExoCovidDottedNames>()
|
||||
|
||||
const totalByMonth = getTotalByMonth(engine, situation) ?? {}
|
||||
|
||||
|
@ -111,7 +113,7 @@ export const FormulaireS1S1Bis = ({ onChange }: Props) => {
|
|||
total={totalByMonth[dotName]}
|
||||
onSelectionChange={(key) => {
|
||||
const val = (key as string).replace(/\.\d+$/, '')
|
||||
onChange?.(dotName as DottedNames, `'${val}'`)
|
||||
onChange?.(dotName as ExoCovidDottedNames, `'${val}'`)
|
||||
}}
|
||||
key={dotName}
|
||||
/>
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
import Value from '@/components/EngineValue'
|
||||
import { EngineContext } from '@/components/utils/EngineContext'
|
||||
import { Radio, ToggleGroup } from '@/design-system/field'
|
||||
import { Spacing } from '@/design-system/layout'
|
||||
import { H3 } from '@/design-system/typography/heading'
|
||||
import { Li } from '@/design-system/typography/list'
|
||||
import { Body } from '@/design-system/typography/paragraphs'
|
||||
import { Grid } from '@mui/material'
|
||||
import { DottedNames } from 'exoneration-covid'
|
||||
import Engine, { Evaluation, PublicodesExpression } from 'publicodes'
|
||||
import { useContext } from 'react'
|
||||
import { ExoCovidDottedNames } from 'exoneration-covid'
|
||||
import { Evaluation, PublicodesExpression } from 'publicodes'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
import { useExoCovidEngine } from '.'
|
||||
import { Bold, GridTotal, Italic, Recap, RecapExpert, Total } from './Recap'
|
||||
|
||||
const Info = styled(Body)`
|
||||
|
@ -23,9 +22,12 @@ const Info = styled(Body)`
|
|||
export const FormulaireS2 = ({
|
||||
onChange,
|
||||
}: {
|
||||
onChange?: (dottedName: DottedNames, value: PublicodesExpression) => void
|
||||
onChange?: (
|
||||
dottedName: ExoCovidDottedNames,
|
||||
value: PublicodesExpression
|
||||
) => void
|
||||
}) => {
|
||||
const engine = useContext(EngineContext) as Engine<DottedNames>
|
||||
const engine = useExoCovidEngine()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const monthNames = [
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { Item, Select } from '@/design-system/field/Select'
|
||||
import { baseParagraphStyle } from '@/design-system/typography/paragraphs'
|
||||
import { getMeta } from '@/utils'
|
||||
import { Key, useContext } from 'react'
|
||||
import { ExoCovidDottedNames } from 'exoneration-covid'
|
||||
import { EvaluatedNode, formatValue } from 'publicodes'
|
||||
import { Key } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import styled, { css } from 'styled-components'
|
||||
import { DottedNames } from 'exoneration-covid'
|
||||
import { EngineContext } from '@/components/utils/EngineContext'
|
||||
import Engine, { EvaluatedNode, formatValue } from 'publicodes'
|
||||
import { useExoCovidEngine } from '.'
|
||||
|
||||
export const Th = styled.th<{ alignSelf?: string }>`
|
||||
flex: 2;
|
||||
|
@ -94,7 +94,7 @@ const Empty = styled.div`
|
|||
`
|
||||
|
||||
type RowProps = {
|
||||
dottedNames: DottedNames[]
|
||||
dottedNames: ExoCovidDottedNames[]
|
||||
actualMonth: string
|
||||
title?: string
|
||||
total?: EvaluatedNode<number>
|
||||
|
@ -112,7 +112,7 @@ export const Row = ({
|
|||
}: RowProps) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const engine = useContext(EngineContext) as Engine<DottedNames>
|
||||
const engine = useExoCovidEngine()
|
||||
|
||||
const choices = {
|
||||
non: [t('Aucun')],
|
||||
|
@ -141,7 +141,9 @@ export const Row = ({
|
|||
: true)
|
||||
)
|
||||
.flatMap((node) => {
|
||||
const name = (actualMonth + ' . ' + node.dottedName) as DottedNames
|
||||
const name = (actualMonth +
|
||||
' . ' +
|
||||
node.dottedName) as ExoCovidDottedNames
|
||||
const rawNode = engine.getRule(name).rawNode
|
||||
|
||||
type Meta = { "baisse d'au moins"?: string }
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
import RuleInput from '@/components/conversation/RuleInput'
|
||||
import {
|
||||
EngineProvider,
|
||||
useEngineKeepState,
|
||||
useEngine,
|
||||
} from '@/components/utils/EngineContext'
|
||||
import {
|
||||
useSynchronizedSituationState,
|
||||
SituationStateProvider,
|
||||
} from '@/components/utils/SituationContext'
|
||||
import { Button } from '@/design-system/buttons'
|
||||
import { Spacing } from '@/design-system/layout'
|
||||
import { H3 } from '@/design-system/typography/heading'
|
||||
import { Grid } from '@mui/material'
|
||||
import exonerationCovid, { DottedNames } from 'exoneration-covid'
|
||||
import Engine, { PublicodesExpression } from 'publicodes'
|
||||
import { useCallback, useEffect } from 'react'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { useLocation } from 'react-router'
|
||||
import { FormulaireS1S1Bis } from './FormulaireS1S1Bis'
|
||||
import { FormulaireS2 } from './FormulaireS2'
|
||||
import { EngineContext, EngineProvider } from '@/components/utils/EngineContext'
|
||||
import exonerationCovid from 'exoneration-covid'
|
||||
import Engine from 'publicodes'
|
||||
import { useContext, useRef } from 'react'
|
||||
import { ExonérationCovid } from './ExonérationCovid'
|
||||
|
||||
const exoCovidEngine = new Engine(exonerationCovid)
|
||||
|
||||
export default function ExonérationCovidProvider() {
|
||||
export const useExoCovidEngine = () =>
|
||||
useContext(EngineContext) as typeof exoCovidEngine
|
||||
|
||||
/**
|
||||
* Use this hooks to keep state of engine with the react fast refresh
|
||||
* @param originalEngine
|
||||
* @returns engine
|
||||
*/
|
||||
export const useEngineKeepState = <Names extends string>(
|
||||
originalEngine: Engine<Names>
|
||||
) => {
|
||||
const { current: engine } = useRef(originalEngine)
|
||||
|
||||
return engine
|
||||
}
|
||||
|
||||
const ExonérationCovidProvider = () => {
|
||||
const engine = useEngineKeepState(exoCovidEngine)
|
||||
|
||||
return (
|
||||
|
@ -32,134 +32,4 @@ export default function ExonérationCovidProvider() {
|
|||
)
|
||||
}
|
||||
|
||||
const rootDottedNames = [
|
||||
'secteur',
|
||||
"début d'activité",
|
||||
"lieu d'exercice",
|
||||
] as const
|
||||
|
||||
const ExonérationCovid = () => {
|
||||
const location = useLocation()
|
||||
const searchParams = new URLSearchParams(location.search)
|
||||
const params = Object.fromEntries(searchParams.entries()) as {
|
||||
[key in typeof rootDottedNames[number]]?: string
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0)
|
||||
}, [location])
|
||||
|
||||
const engine = useEngine<DottedNames>()
|
||||
const situationState = useSynchronizedSituationState<DottedNames>(params)
|
||||
const { situation, setSituation } = situationState
|
||||
|
||||
const updateSituation = useCallback(
|
||||
(name: DottedNames, value: PublicodesExpression | undefined) => {
|
||||
setSituation({ ...situation, [name]: value })
|
||||
},
|
||||
[setSituation, situation]
|
||||
)
|
||||
|
||||
const setStep1Situation = useCallback(() => {
|
||||
const step1Situation = Object.fromEntries(
|
||||
Object.entries(situation).filter(([dotName]) =>
|
||||
(rootDottedNames as readonly string[]).includes(dotName)
|
||||
)
|
||||
)
|
||||
setSituation(step1Situation)
|
||||
}, [setSituation, situation])
|
||||
|
||||
const step2 = rootDottedNames.every((names) => params[names])
|
||||
|
||||
return (
|
||||
<SituationStateProvider value={situationState}>
|
||||
{step2 ? (
|
||||
engine.evaluate('secteur').nodeValue !== 'S2' ? (
|
||||
<FormulaireS1S1Bis onChange={updateSituation} />
|
||||
) : (
|
||||
<FormulaireS2 onChange={updateSituation} />
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
<Grid item xs={12}>
|
||||
<H3>{engine.getRule('secteur').rawNode.question}</H3>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={8}>
|
||||
<RuleInput
|
||||
dottedName={'secteur'}
|
||||
onChange={(value) => updateSituation('secteur', value)}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<H3>{engine.getRule("début d'activité").rawNode.question}</H3>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6}>
|
||||
<RuleInput
|
||||
dottedName="début d'activité"
|
||||
onChange={(value) => updateSituation("début d'activité", value)}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<H3>{engine.getRule("lieu d'exercice").rawNode.question}</H3>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<RuleInput
|
||||
dottedName="lieu d'exercice"
|
||||
onChange={(value) => updateSituation("lieu d'exercice", value)}
|
||||
/>
|
||||
</Grid>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Spacing lg />
|
||||
|
||||
<Grid container justifyContent={step2 ? '' : 'end'}>
|
||||
<Grid item xs={6} sm="auto">
|
||||
{step2 ? (
|
||||
<Button
|
||||
size="MD"
|
||||
to={{
|
||||
pathname: location.pathname,
|
||||
search: '',
|
||||
}}
|
||||
onClick={setStep1Situation}
|
||||
>
|
||||
← <Trans>Précédent</Trans>
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
size="MD"
|
||||
isDisabled={!rootDottedNames.every((names) => situation[names])}
|
||||
{...(rootDottedNames.every((names) => situation[names])
|
||||
? {
|
||||
to: () => {
|
||||
rootDottedNames.forEach((key) =>
|
||||
searchParams.append(
|
||||
key,
|
||||
situation[key]?.toString() ?? ''
|
||||
)
|
||||
)
|
||||
|
||||
return {
|
||||
pathname: location.pathname,
|
||||
search: searchParams.toString(),
|
||||
}
|
||||
},
|
||||
}
|
||||
: null)}
|
||||
>
|
||||
<Trans>Suivant</Trans> →
|
||||
</Button>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Spacing lg />
|
||||
</SituationStateProvider>
|
||||
)
|
||||
}
|
||||
export default ExonérationCovidProvider
|
||||
|
|
Loading…
Reference in New Issue