From 9e307ebd36c39953b834483e5b78372509ad8f8d Mon Sep 17 00:00:00 2001 From: Alice Dahan Date: Thu, 28 Nov 2024 12:16:56 +0100 Subject: [PATCH] feat: ajout de champs heures sup sur le simulateur RGCP --- .../design-system/field/NumberField.tsx | 2 +- site/source/design-system/global-style.ts | 5 + site/source/locales/ui-en.yaml | 12 + site/source/locales/ui-fr.yaml | 13 + .../reduction-generale/RéductionGénérale.tsx | 23 +- .../RéductionGénéraleMoisParMois.tsx | 26 +- .../RéductionGénéraleMoisParMoisRow.tsx | 291 ++++++++++++------ 7 files changed, 273 insertions(+), 99 deletions(-) diff --git a/site/source/design-system/field/NumberField.tsx b/site/source/design-system/field/NumberField.tsx index 0de8493e5..1a1255b60 100644 --- a/site/source/design-system/field/NumberField.tsx +++ b/site/source/design-system/field/NumberField.tsx @@ -142,7 +142,7 @@ export default function NumberField(props: NumberFieldProps) { )} - {props.label && ( + {props.label && !props.small && ( {props.label} )} diff --git a/site/source/design-system/global-style.ts b/site/source/design-system/global-style.ts index 37e9c1361..d4af44c02 100644 --- a/site/source/design-system/global-style.ts +++ b/site/source/design-system/global-style.ts @@ -22,6 +22,11 @@ export const FocusStyle = css` box-shadow: 0 0 0 2px #ffffff; ` +export const FlexCenter = css` + display: flex; + align-items: center; +` + export const GlobalStyle = createGlobalStyle` html { transition: none !important; diff --git a/site/source/locales/ui-en.yaml b/site/source/locales/ui-en.yaml index b3920fbee..634548f92 100644 --- a/site/source/locales/ui-en.yaml +++ b/site/source/locales/ui-en.yaml @@ -89,6 +89,7 @@ Décrivez votre projet ou votre problème en donnant quelques éléments de cont the right advisor for your request. He or she will contact you by telephone within 5 days, and will provide you with assistance tailored to your situation. Décès: Deaths +Déplier: Unfold "Détail du montant :": "Amount in detail :" Effacer mes réponses: Delete my answers Effectif de l'entreprise: Number of employees @@ -208,6 +209,7 @@ Nouveau contenu disponible, cliquez sur recharger pour mettre à jour la page.: Nouveautés: News Option ACRE non activée: ACRE option not activated Option la plus avantageuse.: Most advantageous option. +Options: Options Oui: Yes Outils pour les développeurs: Tools for developers PFU (<1>"flat tax"): PFU (<1>"flat tax") @@ -1487,6 +1489,16 @@ pages: title: General reduction month-by-month: caption: "General discount month by month :" + options: + description: Adds fields to modulate employee activity + label: + heures-complémentaires: Overtime + heures-supplémentaires: Overtime + popover: "<0>The number of hours of overtime or complementary work is used to + calculate the general reduction: gross remuneration is compared with + the SMIC increased by this number of hours.<1>If you have answered + the question on overtime or complementary hours, the value will be + overwritten by the value you enter month by month." régularisation: annuelle: Annual adjustment progressive: Progressive regularization diff --git a/site/source/locales/ui-fr.yaml b/site/source/locales/ui-fr.yaml index f10e254be..69f71a26a 100644 --- a/site/source/locales/ui-fr.yaml +++ b/site/source/locales/ui-fr.yaml @@ -95,6 +95,7 @@ Décrivez votre projet ou votre problème en donnant quelques éléments de cont le conseiller compétent pour votre demande. Celui-ci vous contactera par téléphone sous 5 jours et vous accompagnera en fonction de votre situation. Décès: Décès +Déplier: Déplier "Détail du montant :": "Détail du montant :" Effacer mes réponses: Effacer mes réponses Effectif de l'entreprise: Effectif de l'entreprise @@ -220,6 +221,7 @@ Nouveau contenu disponible, cliquez sur recharger pour mettre à jour la page.: Nouveautés: Nouveautés Option ACRE non activée: Option ACRE non activée Option la plus avantageuse.: Option la plus avantageuse. +Options: Options Oui: Oui Outils pour les développeurs: Outils pour les développeurs PFU (<1>"flat tax"): PFU (<1>"flat tax") @@ -1581,6 +1583,17 @@ pages: title: Réduction générale month-by-month: caption: "Réduction générale mois par mois :" + options: + description: Ajoute des champs pour moduler l'activité du salarié + label: + heures-complémentaires: Heures complémentaires + heures-supplémentaires: Heures supplémentaires + popover: "<0>Le nombre d'heures supplémentaires et complémentaires est utilisé + dans le calcul de la réduction générale : la rémunération brute est + comparée au montant du SMIC majoré de ce nombre d'heures.<1>Si + vous avez répondu à la question sur les heures supplémentaires ou + complémentaires, la valeur sera écrasée par celle que vous saisissez + mois par mois." régularisation: annuelle: Régularisation annuelle progressive: Régularisation progressive 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 8f126c204..f76719521 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 @@ -31,6 +31,7 @@ import RéductionGénéraleMoisParMois from './RéductionGénéraleMoisParMois' import { getInitialRéductionGénéraleMoisParMois, MonthState, + Options, réductionGénéraleDottedName, reevaluateRéductionGénéraleMoisParMois, RégularisationMethod, @@ -125,7 +126,7 @@ function RéductionGénéraleSimulationGoals({ } }, [ initializeRéductionGénéraleMoisParMoisData, - réductionGénéraleMoisParMoisData, + réductionGénéraleMoisParMoisData.length, ]) const situation = useSelector(situationSelector) @@ -178,12 +179,30 @@ function RéductionGénéraleSimulationGoals({ }) } + const onOptionsChange = (monthIndex: number, options: Options) => { + setData((previousData) => { + const updatedData = [...previousData] + updatedData[monthIndex] = { + ...updatedData[monthIndex], + options, + } + + return reevaluateRéductionGénéraleMoisParMois( + updatedData, + engine, + year, + régularisationMethod + ) + }) + } + return ( {monthByMonth ? ( ) : ( <> 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 index fd86fcdcd..0bb393615 100644 --- a/site/source/pages/simulateurs/reduction-generale/RéductionGénéraleMoisParMois.tsx +++ b/site/source/pages/simulateurs/reduction-generale/RéductionGénéraleMoisParMois.tsx @@ -5,16 +5,18 @@ import { ExplicableRule } from '@/components/conversation/Explicable' import RéductionGénéraleMoisParMoisRow from './components/RéductionGénéraleMoisParMoisRow' import Warnings from './components/Warnings' -import { MonthState, réductionGénéraleDottedName } from './utils' +import { MonthState, Options, réductionGénéraleDottedName } from './utils' type Props = { data: MonthState[] - onChange: (monthIndex: number, rémunérationBrute: number) => void + onRémunérationChange: (monthIndex: number, rémunérationBrute: number) => void + onOptionsChange: (monthIndex: number, options: Options) => void } export default function RéductionGénéraleMoisParMois({ data, - onChange, + onRémunérationChange, + onOptionsChange, }: Props) { const { t } = useTranslation() @@ -49,6 +51,7 @@ export default function RéductionGénéraleMoisParMois({ {t('Rémunération brute')} + {t('Réduction générale')} @@ -64,14 +67,27 @@ export default function RéductionGénéraleMoisParMois({ monthName={monthName} data={data[monthIndex]} index={monthIndex} - onChange={(monthIndex: number, rémunérationBrute: number) => { - onChange(monthIndex, rémunérationBrute) + onRémunérationChange={( + monthIndex: number, + rémunérationBrute: number + ) => { + onRémunérationChange(monthIndex, rémunérationBrute) + }} + onOptionsChange={(monthIndex: number, options: Options) => { + onOptionsChange(monthIndex, options) }} /> ))} + + {t( + 'pages.simulateurs.réduction-générale.options.description', + "Ajoute des champs pour moduler l'activité du salarié" + )} + + ) diff --git a/site/source/pages/simulateurs/reduction-generale/components/RéductionGénéraleMoisParMoisRow.tsx b/site/source/pages/simulateurs/reduction-generale/components/RéductionGénéraleMoisParMoisRow.tsx index 9ec86dbbf..c26d84802 100644 --- a/site/source/pages/simulateurs/reduction-generale/components/RéductionGénéraleMoisParMoisRow.tsx +++ b/site/source/pages/simulateurs/reduction-generale/components/RéductionGénéraleMoisParMoisRow.tsx @@ -1,15 +1,22 @@ import { formatValue, PublicodesExpression } from 'publicodes' -import { useTranslation } from 'react-i18next' -import { styled } from 'styled-components' +import { useState } from 'react' +import { Trans, useTranslation } from 'react-i18next' +import { css, styled } from 'styled-components' import NumberInput from '@/components/conversation/NumberInput' import { Condition } from '@/components/EngineValue/Condition' +import { Appear } from '@/components/ui/animate' import { useEngine } from '@/components/utils/EngineContext' -import { SearchIcon, WarningIcon } from '@/design-system/icons' +import { Message, NumberField } from '@/design-system' +import { Button, HelpButtonWithPopover } from '@/design-system/buttons' +import { FlexCenter } from '@/design-system/global-style' +import { ChevronIcon, SearchIcon, WarningIcon } from '@/design-system/icons' import { Tooltip } from '@/design-system/tooltip' +import { Body, SmallBody } from '@/design-system/typography/paragraphs' import { MonthState, + Options, réductionGénéraleDottedName, rémunérationBruteDottedName, } from '../utils' @@ -20,7 +27,8 @@ type Props = { monthName: string data: MonthState index: number - onChange: (monthIndex: number, rémunérationBrute: number) => void + onRémunérationChange: (monthIndex: number, rémunérationBrute: number) => void + onOptionsChange: (monthIndex: number, options: Options) => void } type RémunérationBruteInput = { @@ -32,19 +40,14 @@ export default function RéductionGénéraleMoisParMoisRow({ monthName, data, index, - onChange, + onRémunérationChange, + onOptionsChange, }: Props) { const { t, i18n } = useTranslation() const language = i18n.language const displayedUnit = '€' const engine = useEngine() - - const onRémunérationChange = ( - monthIndex: number, - rémunérationBrute: RémunérationBruteInput - ) => { - onChange(monthIndex, rémunérationBrute.valeur) - } + const [isOptionVisible, setOptionVisible] = useState(false) // TODO: enlever les 4 premières props après résolution de #3123 const ruleInputProps = { @@ -64,6 +67,21 @@ export default function RéductionGénéraleMoisParMoisRow({ }, } + const isTempsPartiel = engine.evaluate( + 'salarié . contrat . temps de travail . temps partiel' + ).nodeValue as boolean + const additionalHours = isTempsPartiel ? 'complémentaires' : 'supplémentaires' + const additionalHoursLabels = { + supplémentaires: t( + 'pages.simulateurs.réduction-générale.options.label.heures-supplémentaires', + 'Heures supplémentaires' + ), + complémentaires: t( + 'pages.simulateurs.réduction-générale.options.label.heures-complémentaires', + 'Heures complémentaires' + ), + } + const tooltip = ( - {monthName} - - + + {monthName} + + + onRémunérationChange( + index, + (rémunérationBrute as RémunérationBruteInput).valeur + ) + } + value={data.rémunérationBrute} + formatOptions={{ + maximumFractionDigits: 2, + }} + /> + + + + + - onRémunérationChange( - index, - rémunérationBrute as RémunérationBruteInput - ) - } - value={data.rémunérationBrute} - formatOptions={{ - maximumFractionDigits: 2, - }} - /> - - - {data.réductionGénérale ? ( - - - {formatValue( - { - nodeValue: data.réductionGénérale, - }, - { - displayedUnit, - language, - } - )} - - - - ) : ( - - {formatValue(0, { displayedUnit, language })} + > + {data.réductionGénérale ? ( + + + {formatValue( + { + nodeValue: data.réductionGénérale, + }, + { + displayedUnit, + language, + } + )} + + + + ) : ( + + {formatValue(0, { displayedUnit, language })} - 1.6 * SMIC`} - contexte={{ - [rémunérationBruteDottedName]: data.rémunérationBrute, - }} - > - }> - {t('Attention')} - - - - - )} - - - {formatValue( - { - nodeValue: data.régularisation, - }, - { - displayedUnit, - language, - } - )} - - + 1.6 * SMIC`} + contexte={{ + [rémunérationBruteDottedName]: data.rémunérationBrute, + }} + > + }> + {t('Attention')} + + + + + )} + + + {formatValue( + { + nodeValue: data.régularisation, + }, + { + displayedUnit, + language, + } + )} + + + {isOptionVisible && ( + + + + + + + + {additionalHoursLabels[additionalHours]} + + + + + + + + onOptionsChange(index, { + heuresSupplémentaires: value, + heuresComplémentaires: 0, + }) + } + aria-labelledby={`heures-${additionalHours}-label`} + displayedUnit="heures" + /> + + + + + )} + ) } -const StyledDiv = styled.div` - display: flex; - align-items: center; - gap: ${({ theme }) => theme.spacings.sm}; +function HeuresSupplémentairesPopoverContent() { + return ( + + + Le nombre d'heures supplémentaires et complémentaires est utilisé dans + le calcul de la réduction générale : la rémunération brute est comparée + au montant du SMIC majoré de ce nombre d'heures. + + + Si vous avez répondu à la question sur les heures supplémentaires ou + complémentaires, la valeur sera écrasée par celle que vous saisissez + mois par mois. + + + ) +} + +const FlexDiv = styled.div` + ${FlexCenter} +` + +const StyledSearchIcon = styled(SearchIcon)` + margin-left: ${({ theme }) => theme.spacings.sm}; ` const StyledWarningIcon = styled(WarningIcon)` margin-top: ${({ theme }) => theme.spacings.xxs}; ` + +const OptionContainer = styled.div` + ${FlexCenter} + gap: ${({ theme }) => theme.spacings.lg}; + margin-top: -${({ theme }) => theme.spacings.md}; +` + +const StyledSmallBody = styled(SmallBody)` + margin: 0; +` + +const StyledChevron = styled(ChevronIcon)<{ $isOpen: boolean }>` + vertical-align: middle; + transform: rotate(-90deg); + transition: transform 0.3s; + ${({ $isOpen }) => + !$isOpen && + css` + transform: rotate(90deg); + `} +`