diff --git a/site/source/locales/ui-en.yaml b/site/source/locales/ui-en.yaml index f98b77c3d..c8ed29009 100644 --- a/site/source/locales/ui-en.yaml +++ b/site/source/locales/ui-en.yaml @@ -275,6 +275,8 @@ Revenu du dirigeant par statut: Executive income by status Revenu net mensuel après impôts: Net monthly income after tax Règles de calculs: Calculation rules Réduction annuelle: Annual discount +Réduction générale: General reduction +"Réduction générale mois par mois :": "General discount month by month :" Réduction mensuelle: Monthly discount Réduction mois par mois: Monthly discount Régime d'imposition: Taxation system @@ -391,6 +393,7 @@ aide-déclaration-indépendant: results: title: Amounts to report on your tax return aides différées, voir le détail du calcul pour aides différées: deferred aid, see detailed calculation for deferred aid +août: August api: description: Tools for developers title: Use our REST API @@ -402,6 +405,7 @@ assistants: cta: Visit the site title: View your public data au bout de 10 ans: after 10 years +avril: April betawarning: "<0><0>This tool is in beta version: we are working on <3>validating the information and calculations, but <6>errors may still occur." @@ -593,6 +597,7 @@ design-system: prev-month: Previous month year: Year dont chômage: of which unemployment +décembre: December employeur: employer en cas d'accident pro: in the event of a professional accident en incluant: including @@ -637,6 +642,7 @@ footer: accessibilitéAriaLabel: "Accessibility: partially compliant, find out more" github: text: View source code on Github +février: February gestion sémantique de version, en savoir plus, nouvelle fenêtre: semantic version management, learn more, new window gérer: choix: @@ -679,7 +685,10 @@ info: info intégration: description: Tools for developers title: Integration +janvier: January jours: days +juillet: July +juin: June l'annuaire des entreprises, nouvelle fenêtre: business directory, new window landing: aboutUs: "<0>Who are we?<1>We're a small, independent, multi-disciplinary @@ -735,6 +744,8 @@ library: description: Tools for developers title: Calculation library loading: Loading in progress... +mai: May +mars: March moins: less mois: month navbar: @@ -753,6 +764,8 @@ nextSteps: cta: See the documentation title: Integrate the web module nombres de votes: number of votes +novembre: November +octobre: October pages: "404": description: The page you are looking for does not exist or no longer exists @@ -1674,6 +1687,7 @@ search-code-ape: select: value: default: Choose an option +septembre: September shareSimulation: banner: Generate a share link button: diff --git a/site/source/locales/ui-fr.yaml b/site/source/locales/ui-fr.yaml index 509b14971..cfc16a4c5 100644 --- a/site/source/locales/ui-fr.yaml +++ b/site/source/locales/ui-fr.yaml @@ -291,6 +291,8 @@ Revenu du dirigeant par statut: Revenu du dirigeant par statut Revenu net mensuel après impôts: Revenu net mensuel après impôts Règles de calculs: Règles de calculs Réduction annuelle: Réduction annuelle +Réduction générale: Réduction générale +"Réduction générale mois par mois :": "Réduction générale mois par mois :" Réduction mensuelle: Réduction mensuelle Réduction mois par mois: Réduction mois par mois Régime d'imposition: Régime d'imposition @@ -414,6 +416,7 @@ aide-déclaration-indépendant: results: title: Montants à reporter dans votre déclaration de revenus aides différées, voir le détail du calcul pour aides différées: aides différées, voir le détail du calcul pour aides différées +août: août api: description: Outils pour les développeurs title: Utiliser notre API REST @@ -426,6 +429,7 @@ assistants: cta: Visiter le site title: Voir vos données publiques au bout de 10 ans: au bout de 10 ans +avril: avril betawarning: "<0><0>Cet outil est en version bêta : nous travaillons à <3>valider les informations et les calculs, mais des <6>erreurs peuvent être présentes." @@ -624,6 +628,7 @@ design-system: prev-month: Mois précédent year: Année dont chômage: dont chômage +décembre: décembre employeur: employeur en cas d'accident pro: en cas d'accident pro en incluant: en incluant @@ -669,6 +674,7 @@ footer: accessibilitéAriaLabel: "Accessibilité : partiellement conforme, en savoir plus" github: text: Voir le code source sur Github +février: février gestion sémantique de version, en savoir plus, nouvelle fenêtre: gestion sémantique de version, en savoir plus, nouvelle fenêtre gérer: choix: @@ -714,7 +720,10 @@ info: info intégration: description: Outils pour les développeurs title: Intégration +janvier: janvier jours: jours +juillet: juillet +juin: juin l'annuaire des entreprises, nouvelle fenêtre: l'annuaire des entreprises, nouvelle fenêtre landing: aboutUs: "<0>Qui sommes-nous ?<1>Nous sommes une petite <2>équipe @@ -774,6 +783,8 @@ library: description: Outils pour les développeurs title: Librairie de calcul loading: Chargement en cours... +mai: mai +mars: mars moins: moins mois: mois navbar: @@ -792,6 +803,8 @@ nextSteps: cta: Voir la documentation title: Intégrer le module web nombres de votes: nombres de votes +novembre: novembre +octobre: octobre pages: "404": description: La page que vous cherchez n'existe pas ou n'existe plus @@ -1782,6 +1795,7 @@ search-code-ape: select: value: default: Choisissez une option +septembre: septembre shareSimulation: banner: Générer un lien de partage button: diff --git a/site/source/pages/simulateurs/reduction-generale/RéductionGénérale.tsx b/site/source/pages/simulateurs/reduction-generale/RéductionGénérale.tsx index 9db46aa71..f61f80acd 100644 --- a/site/source/pages/simulateurs/reduction-generale/RéductionGénérale.tsx +++ b/site/source/pages/simulateurs/reduction-generale/RéductionGénérale.tsx @@ -1,5 +1,6 @@ -import { useCallback, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' import { Trans, useTranslation } from 'react-i18next' +import { useSelector } from 'react-redux' import { styled } from 'styled-components' import { Condition } from '@/components/EngineValue/Condition' @@ -11,12 +12,19 @@ import Simulation, { SimulationGoals, } from '@/components/Simulation' import { SimulationValue } from '@/components/Simulation/SimulationValue' +import { useEngine } from '@/components/utils/EngineContext' import { Message } from '@/design-system' import { Spacing } from '@/design-system/layout' import { Li, Ul } from '@/design-system/typography/list' import { Body } from '@/design-system/typography/paragraphs' import EffectifSwitch from './components/EffectifSwitch' +import RéductionGénéraleMoisParMois from './RéductionGénéraleMoisParMois' +import { + getInitialRéductionGénéraleMoisParMois, + MonthState, + rémunérationBruteDottedName, +} from './utils' export default function RéductionGénéraleSimulation() { const { t } = useTranslation() @@ -80,22 +88,34 @@ function RéductionGénéraleSimulationGoals({ toggles?: React.ReactNode legend: string }) { + const engine = useEngine() const { t } = useTranslation() + const [réductionGénéraleMoisParMoisData, setData] = useState([]) + + const initializeRéductionGénéraleMoisParMoisData = useCallback(() => { + setData(getInitialRéductionGénéraleMoisParMois(engine)) + }, [engine, setData]) + + useEffect(() => { + if (réductionGénéraleMoisParMoisData.length === 0) { + initializeRéductionGénéraleMoisParMoisData() + } + }, [ + initializeRéductionGénéraleMoisParMoisData, + réductionGénéraleMoisParMoisData, + ]) return ( {monthByMonth ? ( -
- Réduction générale mensuelle -
+ ) : ( <> - {/* TODO: remplacer "salarié . cotisations . assiette" par "salarié . rémunération . brut" - lorsqu'elle n'incluera plus les frais professionnels. */} diff --git a/site/source/pages/simulateurs/reduction-generale/RéductionGénéraleMoisParMois.tsx b/site/source/pages/simulateurs/reduction-generale/RéductionGénéraleMoisParMois.tsx new file mode 100644 index 000000000..99df9639d --- /dev/null +++ b/site/source/pages/simulateurs/reduction-generale/RéductionGénéraleMoisParMois.tsx @@ -0,0 +1,133 @@ +import { formatValue } from 'publicodes' +import { useTranslation } from 'react-i18next' +import { styled } from 'styled-components' + +import { ExplicableRule } from '@/components/conversation/Explicable' +import NumberInput from '@/components/conversation/NumberInput' +import { useEngine } from '@/components/utils/EngineContext' + +import { MonthState, rémunérationBruteDottedName } from './utils' + +export default function RéductionGénéraleMoisParMois({ + data, +}: { + data: MonthState[] +}) { + const engine = useEngine() + const { t, i18n } = useTranslation() + const language = i18n.language + const displayedUnit = '€' + + const months = [ + t('janvier'), + t('février'), + t('mars'), + t('avril'), + t('mai'), + t('juin'), + t('juillet'), + t('août'), + t('septembre'), + t('octobre'), + t('novembre'), + t('décembre'), + ] + + const onRémunérationChange = () => {} + + // TODO: enlever les 4 premières props après résolution de #3123 + const ruleInputProps = { + dottedName: rémunérationBruteDottedName, + suggestions: {}, + description: undefined, + question: undefined, + engine, + 'aria-labelledby': 'simu-update-explaining', + formatOptions: { + maximumFractionDigits: 0, + }, + displayedUnit, + unit: { + numerators: ['€'], + denominators: [], + }, + } + + return ( + <> + + {t('Réduction générale mois par mois :')} + + + {t('Mois')} + + {t('Rémunération brute', 'Rémunération brute')} + + + + {t('Réduction générale')} + + + + + + {data.length > 0 && + months.map((monthName, monthIndex) => ( + + {monthName} + + + + + {data[monthIndex].réductionGénérale + ? formatValue( + { nodeValue: data[monthIndex].réductionGénérale }, + { + displayedUnit, + language, + } + ) + : formatValue(0, { displayedUnit, language })} + + + ))} + + + + ) +} + +const StyledTable = styled.table` + text-align: left; + width: 100%; + color: ${({ theme }) => theme.colors.bases.primary[100]}; + font-family: ${({ theme }) => theme.fonts.main}; + caption { + text-align: left; + margin: ${({ theme }) => `${theme.spacings.sm} 0 `}; + } + th { + padding: ${({ theme }) => `${theme.spacings.xs} 0 ${theme.spacings.lg} 0`}; + } + tbody tr td:not(:first-of-type) { + padding: ${({ theme }) => + `${theme.spacings.xs} ${theme.spacings.xxs} ${theme.spacings.lg} ${theme.spacings.xxs}`}; + } + tbody tr th { + text-transform: capitalize; + font-weight: normal; + } +` diff --git a/site/source/pages/simulateurs/reduction-generale/utils.ts b/site/source/pages/simulateurs/reduction-generale/utils.ts new file mode 100644 index 000000000..bc150527b --- /dev/null +++ b/site/source/pages/simulateurs/reduction-generale/utils.ts @@ -0,0 +1,59 @@ +import { DottedName } from 'modele-social' +import Engine from 'publicodes' + +// TODO: remplacer "salarié . cotisations . assiette" par "salarié . rémunération . brut" +// lorsqu'elle n'incluera plus les frais professionnels. +export const rémunérationBruteDottedName = 'salarié . cotisations . assiette' + +export type MonthState = { + rémunérationBrute: number + réductionGénérale: number +} + +export const getRéductionGénéraleFromRémunération = ( + engine: Engine, + rémunérationBrute: number +): number => { + const réductionGénérale = engine.evaluate({ + valeur: 'salarié . cotisations . exonérations . réduction générale', + unité: '€/mois', + contexte: { + [rémunérationBruteDottedName]: rémunérationBrute, + }, + }) + + return réductionGénérale.nodeValue as number +} + +export const getInitialRéductionGénéraleMoisParMois = ( + engine: Engine +): MonthState[] => { + const rémunérationBrute = + (engine.evaluate({ + valeur: rémunérationBruteDottedName, + arrondi: 'oui', + unité: '€/mois', + })?.nodeValue as number) || 0 + const réductionGénérale = getRéductionGénéraleFromRémunération( + engine, + rémunérationBrute + ) + + return Array(12).fill({ + rémunérationBrute, + réductionGénérale, + }) as MonthState[] +} + +export const reevaluateRéductionGénéraleMoisParMois = ( + data: MonthState[], + engine: Engine +): MonthState[] => { + return data.map((item) => ({ + ...item, + réductionGénérale: getRéductionGénéraleFromRémunération( + engine, + item.rémunérationBrute + ), + })) +}