From 978f3ed759eed4cb1772132fde6ee378769497d5 Mon Sep 17 00:00:00 2001 From: Maxime Quandalle Date: Tue, 24 Mar 2020 09:41:47 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Redesign=20les=20r=C3=A9sultats?= =?UTF-8?q?=20du=20simulateur=20ch=C3=B4mage=20partiel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Affichage des résultats sous forme de tableau comparatif. --- publicode/rules/chômage-partiel.yaml | 15 +- .../simulationConfigs/chômage-partiel.yaml | 3 +- source/locales/en.yaml | 5 + source/locales/rules-en.yaml | 12 +- .../mon-entreprise.fr/pages/Coronavirus.tsx | 292 +++++++++++++----- 5 files changed, 243 insertions(+), 84 deletions(-) diff --git a/publicode/rules/chômage-partiel.yaml b/publicode/rules/chômage-partiel.yaml index ac351d8ed..de0f8ff61 100644 --- a/publicode/rules/chômage-partiel.yaml +++ b/publicode/rules/chômage-partiel.yaml @@ -1,12 +1,13 @@ -différence de revenu chômage partiel: - formule: - allègement: - assiette: net sans chômage partiel - abattement: contrat salarié . rémunération . net - -différence de revenu chômage partiel . net sans chômage partiel: +chômage partiel . revenu net habituel: formule: recalcul: règle: contrat salarié . rémunération . net avec: contrat salarié . activité partielle: non + +chômage partiel . coût employeur habituel: + formule: + recalcul: + règle: contrat salarié . prix du travail + avec: + contrat salarié . activité partielle: non diff --git a/source/components/simulationConfigs/chômage-partiel.yaml b/source/components/simulationConfigs/chômage-partiel.yaml index c9bde1a1c..3a09a296e 100644 --- a/source/components/simulationConfigs/chômage-partiel.yaml +++ b/source/components/simulationConfigs/chômage-partiel.yaml @@ -4,7 +4,8 @@ objectifs: objectifs secondaires: - contrat salarié . prix du travail - contrat salarié . rémunération . net - - différence de revenu chômage partiel + - chômage partiel . revenu net habituel + - chômage partiel . coût employeur habituel - contrat salarié . activité partielle . indemnités questions: diff --git a/source/locales/en.yaml b/source/locales/en.yaml index 6ea4c260d..92fdc1115 100644 --- a/source/locales/en.yaml +++ b/source/locales/en.yaml @@ -13,6 +13,7 @@ Au-dessus de: Above Aucun résultat: No result$ Auto-entrepreneur: Auto-entrepreneur Auto-entrepreneur en EIRL: Auto-entrepreneur with EIRL option +Avec chômage partiel: With partial unemployment Calcul: Formula Cette règle ne s'applique pas pour: This rule does not apply for Changer: Change @@ -40,6 +41,7 @@ Désactivée: Inactive Détail annuel des cotisations: Annual detail of my contributions Effacer: Reset Embauche: Hiring process +Employeur: Employer En incluant l'indemnité de chômage partiel: Including short-time working allowance En-dessous de: Below Entreprise Individuelle: Sole Proprietorship @@ -55,6 +57,7 @@ Fiche de paie: Payslip Guide du statut juridique: Legal status guide Gérant majoritaire: Chairman Gérant minoritaire: Managing director +Habituellement: Usually Imprimer: Print Impôts: Taxes 'Indemnité chômage partiel prise en charge par l''état :': 'State-paid short-time working allowance :' @@ -93,6 +96,7 @@ Plafonds des tranches: Wafer ceilings Plein écran: Fullscreen Plus d'informations: More information (fr) Plusieurs associés: Several partners +Prise en charge du revenu net avec chômage partiel: Net income support with short-time working Prochaines questions: Next questions Protection sociale: Social security Précédent: Previous @@ -1201,6 +1205,7 @@ trouver: une de ces conditions: one of these applies À quoi servent mes cotisations ?: How are my contributions distributed? Échap: Esc +État: State à: to économieCollaborative: accueil: diff --git a/source/locales/rules-en.yaml b/source/locales/rules-en.yaml index 57d68da29..be2a71fb2 100644 --- a/source/locales/rules-en.yaml +++ b/source/locales/rules-en.yaml @@ -260,6 +260,12 @@ artiste-auteur . revenus . traitements et salaires: résumé.fr: Le montant brut hors TVA de vos droits d'auteur (recettes précomptées) titre.en: Income in wages and salaries titre.fr: Revenu en traitements et salaires +chômage partiel . coût employeur habituel: + titre.en: '[automatic] regular employer cost' + titre.fr: coût employeur habituel +chômage partiel . revenu net habituel: + titre.en: '[automatic] usual net income' + titre.fr: revenu net habituel contrat salarié: contrôles.0.en: > [automatic] Remember that a fixed-term contract must always correspond to a @@ -3693,12 +3699,6 @@ contrat salarié . vieillesse . taux salarié déplafonné: contrat salarié . vieillesse . taux salarié plafonné: titre.en: capped employee rate titre.fr: taux salarié plafonné -différence de revenu chômage partiel: - titre.en: '[automatic] difference in income from short-time work' - titre.fr: différence de revenu chômage partiel -différence de revenu chômage partiel . net sans chômage partiel: - titre.en: '[automatic] net without short-time work' - titre.fr: net sans chômage partiel dirigeant: question.en: What is the director's social regime? question.fr: Quel est le régime social du dirigeant ? diff --git a/source/sites/mon-entreprise.fr/pages/Coronavirus.tsx b/source/sites/mon-entreprise.fr/pages/Coronavirus.tsx index 8bddb68d8..678a3bb2c 100644 --- a/source/sites/mon-entreprise.fr/pages/Coronavirus.tsx +++ b/source/sites/mon-entreprise.fr/pages/Coronavirus.tsx @@ -9,12 +9,14 @@ import { Markdown } from 'Components/utils/markdown' import { ScrollToTop } from 'Components/utils/Scroll' import { formatValue } from 'Engine/format' import { getRuleFromAnalysis } from 'Engine/rules' -import React, { useContext } from 'react' +import React, { useContext, useState } from 'react' import { Helmet } from 'react-helmet' import { Trans, useTranslation } from 'react-i18next' import { useDispatch, useSelector } from 'react-redux' import { useLocation } from 'react-router' import { analysisWithDefaultsSelector } from 'Selectors/analyseSelectors' +import styled from 'styled-components' +import { EvaluatedRule } from 'Types/rule' import Animate from 'Ui/animate' export default function ChômagePartiel() { @@ -27,7 +29,6 @@ export default function ChômagePartiel() { return ( <> - {t( @@ -46,7 +47,11 @@ export default function ChômagePartiel() { <ScrollToTop /> {!inIframe && ( <Trans i18nKey="coronavirus.description"> - <h1> + <h1 + css={` + margin-top: 1rem; + `} + > <span css={` font-size: 0.65em; @@ -79,94 +84,241 @@ export default function ChômagePartiel() { function ExplanationSection() { const analysis = useSelector(analysisWithDefaultsSelector) - const { language } = useTranslation().i18n + const { + i18n: { language }, + t + } = useTranslation() const { palettes } = useContext(ThemeColorsContext) const getRule = getRuleFromAnalysis(analysis) + const [showTable, setShowTable] = useState(false) const net = getRule('contrat salarié . rémunération . net') + const netHabituel = getRule('chômage partiel . revenu net habituel') const totalEntreprise = getRule('contrat salarié . prix du travail') - const perteRevenu = getRule('différence de revenu chômage partiel') + const totalEntrepriseHabituel = getRule( + 'chômage partiel . coût employeur habituel' + ) if (!net?.nodeValue) { return null } return ( <Animate.fromTop> <div - css={` - margin-top: 2rem; - `} - ></div> - <div + id="targetSelection" className="ui__ light card" css={` - margin: 3rem 0; + margin: 1rem 0; + padding: 1rem 0; `} > - <div id="targetSelection"> - <ul className="targets"> - <li> - <div className="main"> - <div> - <div className="optionTitle"> - <Trans>Revenu net mensuel</Trans> - </div> - <p> - <Trans>En incluant l'indemnité de chômage partiel</Trans> - </p> - </div> - <div className="targetInputOrValue"> - <RuleLink {...net}> - {formatValue({ - value: net.nodeValue, - language, - unit: '€', - maximumFractionDigits: 0 - })} - </RuleLink> - </div> - </div> - </li> - <li className="small-target"> - <div className="main"> - <Trans>Coût pour l'entreprise</Trans> - <div className="targetInputOrValue"> - <RuleLink {...totalEntreprise}> - {formatValue({ - value: totalEntreprise.nodeValue, - language, - unit: '€', - maximumFractionDigits: 0 - })} - </RuleLink> - </div> - </div> - </li> - <li> - <span className="optionTitle"> - <Trans>Part du salaire net maintenue</Trans> - </span> - <StackedBarChart - data={[ - { - ...net, - title: 'net avec chômage partiel', - color: palettes[0][0] - }, - { - ...perteRevenu, - title: 'Différence de revenu', - color: palettes[1][0] - } - ]} - /> - </li> - </ul> + <p> + Le chômage partiel permet d'obenir un revenu net de{' '} + <strong> + {formatValue({ + value: net.nodeValue, + language, + unit: '€', + maximumFractionDigits: 0 + })} + </strong> + . + <br /> + Soit{' '} + <strong> + {formatValue({ + value: (net.nodeValue / netHabituel.nodeValue) * 100, + unit: '%', + maximumFractionDigits: 0 + })} + </strong>{' '} + du revenu net habituel.{' '} + <button + className="ui__ link-button" + onClick={() => setShowTable(!showTable)} + > + Détails ▾ + </button> + </p> + {showTable && ( + <Animate.fromTop> + <ComparaisonTable + rows={[ + ['', t('Habituellement'), t('Avec chômage partiel')], + [net, netHabituel, net], + [totalEntreprise, totalEntrepriseHabituel, totalEntreprise] + ]} + /> + </Animate.fromTop> + )} + <div + className="optionTitle" + css={` + padding: 1rem 0; + border-top: 1px solid rgba(0, 0, 0, 0.1); + `} + > + <Trans>Prise en charge du revenu net avec chômage partiel</Trans> </div> + <StackedBarChart + data={[ + { + ...getRule( + 'contrat salarié . activité partielle . indemnisation entreprise' + ), + title: t('État'), + color: palettes[0][0] + }, + { + ...totalEntreprise, + title: t('Employeur'), + color: palettes[1][0] + } + ]} + /> </div> </Animate.fromTop> ) } +function ComparaisonTable({ rows: [head, ...body] }) { + const columns = head.filter(x => x !== '') + const [currentColumnIndex, setCurrentColumnIndex] = useState( + columns.length - 1 + ) + + return ( + <> + <ResultTable className="ui__ mobile-version"> + <tr> + <th></th> + <th> + <select + onChange={evt => setCurrentColumnIndex(Number(evt.target.value))} + > + {columns.map((name, i) => ( + <option value={i} selected={i === currentColumnIndex}> + {name} + </option> + ))} + </select> + </th> + </tr> + <tbody> + {body.map(([label, ...line], i) => ( + <tr key={i}> + <td> + <RowLabel {...label} /> + </td> + <td> + <ValueWithLink {...line[currentColumnIndex]} /> + </td> + </tr> + ))} + </tbody> + </ResultTable> + <ResultTable> + <tr> + {head.map((label, i) => ( + <th key={i}>{label}</th> + ))} + </tr> + {body.map(([label, ...line], i) => ( + <tr key={i}> + <td> + <RowLabel {...label} /> + </td> + {line.map((cell, j) => ( + <td key={j}> + {' '} + <ValueWithLink {...cell} /> + </td> + ))} + </tr> + ))} + </ResultTable> + </> + ) +} + +function ValueWithLink(rule: EvaluatedRule) { + const { language } = useTranslation().i18n + return ( + <RuleLink {...rule}> + {formatValue({ + value: rule.nodeValue as number, + language, + unit: '€', + maximumFractionDigits: 0 + })} + </RuleLink> + ) +} + +function RowLabel(target: EvaluatedRule) { + return ( + <> + {' '} + <div className="optionTitle">{target.title}</div> + <p>{target.summary}</p> + </> + ) +} + +const ResultTable = styled.table` + width: 100%; + border-collapse: collapse; + margin-top: 5px; + + &.ui__.mobile-version { + display: none; + @media (max-width: 660px) { + display: table; + } + } + + &:not(.mobile-version) { + display: none; + @media (min-width: 660px) { + display: table; + } + + td:nth-child(2) { + font-size: 1em; + opacity: 0.8; + } + } + + td { + border-top: 1px solid rgba(0, 0, 0, 0.1); + padding: 8px 16px 0; + + p { + font-style: italic; + } + } + + th:nth-child(n + 2) { + white-space: nowrap; + text-align: right; + padding: 8px 16px; + } + + th:first-child { + width: 100%; + padding-left: 10px; + } + + td:nth-child(n + 2) { + text-align: right; + font-size: 1.3em; + } + + td:last-child, + th:last-child { + background: var(--lighterColor); + } +` + function TextExplanations() { const { i18n } = useTranslation() if (i18n.language !== 'fr') {