✨ Réduit l'utilisation de Ramda
En vue de sa suppression dans un prochain commitpull/2052/head
parent
af0458ae41
commit
5233abc7ef
|
@ -25,7 +25,6 @@ Nous utilisons :
|
|||
- [Prettier](https://prettier.io/) pour formater le code source, l'idéal est de configurer votre éditeur de texte pour que les fichiers soit formatés automatiquement quand vous sauvegardez un fichier. Si vous utilisez [VS Code](https://code.visualstudio.com/) cette configuration est automatique.
|
||||
- [ViteJS](https://vitejs.dev) pour le “bundling” et le serveur de développement
|
||||
- [Eslint](http://eslint.org) qui permet par exemple d'éviter de garder des variables inutilisées
|
||||
- [Ramda](https://ramdajs.com) comme libraire d'utilitaires pour manipuler les listes/objects/etc (c'est une alternative à lodash ou underscore)
|
||||
- [Vitest](https://vitest.dev) et [Cypress](https://www.cypress.io) pour les l'execution des tests. Plus d'informations dans la section consacrée aux tests.
|
||||
|
||||
### Démarrage
|
||||
|
|
|
@ -2,8 +2,6 @@ import 'dotenv/config.js'
|
|||
import 'isomorphic-fetch'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { filter, flatten, map, partition, pipe } from 'ramda'
|
||||
import { compose } from 'redux'
|
||||
import { fileURLToPath } from 'url'
|
||||
import { createDataDir, writeInDataDir } from './utils.js'
|
||||
|
||||
|
@ -157,21 +155,23 @@ const last36Months = {
|
|||
.slice(0, 8) + '01',
|
||||
end: yesterday,
|
||||
}
|
||||
const uniformiseData = pipe(
|
||||
// For some reason, an artifact create ghost page with unlogical chapter metrics...
|
||||
// It seems to only by one per month thought... This hacks resolves it
|
||||
filter(({ m_visits }) => m_visits === undefined || m_visits > 2),
|
||||
map(({ d_evo_day, d_evo_month, m_visits, m_events, ...data }) => ({
|
||||
date: d_evo_day != null ? d_evo_day : d_evo_month,
|
||||
nombre: m_visits != null ? m_visits : m_events,
|
||||
...data,
|
||||
}))
|
||||
)
|
||||
const flattenPage = compose(
|
||||
flatten,
|
||||
map(({ Rows, ...page }) => Rows.map((r) => ({ ...page, ...r }))),
|
||||
filter((p) => p.page_chapter2 !== 'N/A') // Remove simulateur landing page
|
||||
)
|
||||
const uniformiseData = (data) =>
|
||||
data
|
||||
.map(({ d_evo_day, d_evo_month, m_visits, m_events, ...data }) => ({
|
||||
date: d_evo_day != null ? d_evo_day : d_evo_month,
|
||||
nombre: m_visits != null ? m_visits : m_events,
|
||||
...data,
|
||||
}))
|
||||
// For some reason, an artifact create ghost page with unlogical chapter metrics...
|
||||
// It seems to only by one per month thought... This hacks resolves it
|
||||
.filter(({ m_visits }) => m_visits === undefined || m_visits > 2)
|
||||
|
||||
const flattenPage = (list) =>
|
||||
list
|
||||
.flat()
|
||||
.map(({ Rows, ...page }) => Rows.map((r) => ({ ...page, ...r })))
|
||||
.filter((p) => p.page_chapter2 !== 'N/A') // Remove simulateur landing page
|
||||
|
||||
async function fetchDailyVisits() {
|
||||
const pages = uniformiseData(
|
||||
flattenPage(await fetchApi(buildSimulateursQuery(last60days, 'D')))
|
||||
|
@ -261,12 +261,12 @@ async function fetchUserFeedbackIssues() {
|
|||
const issues = Object.entries(data.data.repository)
|
||||
.filter(([, value]) => !!value)
|
||||
.map(([k, value]) => ({ ...value, count: +/[\d]+$/.exec(k)[0] }))
|
||||
const [closed, open] = partition((s) => s.closedAt, issues)
|
||||
|
||||
return {
|
||||
open,
|
||||
closed: closed.sort(
|
||||
(i1, i2) => new Date(i2.closedAt) - new Date(i1.closedAt)
|
||||
),
|
||||
open: issues.filter((s) => !s.closedAt),
|
||||
closed: issues
|
||||
.filter((s) => s.closedAt)
|
||||
.sort((i1, i2) => new Date(i2.closedAt) - new Date(i1.closedAt)),
|
||||
}
|
||||
}
|
||||
async function main() {
|
||||
|
|
|
@ -2,7 +2,7 @@ import 'dotenv/config.js'
|
|||
import { readFileSync } from 'fs'
|
||||
import 'isomorphic-fetch'
|
||||
import { stringify } from 'querystring'
|
||||
import { equals, mergeAll, path as _path, pick, toPairs } from 'ramda'
|
||||
import { equals, mergeAll, path as _path, pick } from 'ramda'
|
||||
import yaml from 'yaml'
|
||||
import rules from '../../../modele-social/dist/index.js'
|
||||
|
||||
|
@ -36,7 +36,7 @@ export function getRulesMissingTranslations() {
|
|||
])
|
||||
.map(([dottedName, rule]) => ({
|
||||
[dottedName]: mergeAll(
|
||||
toPairs(rule)
|
||||
Object.entries(rule)
|
||||
.filter(([, v]) => !!v)
|
||||
.map(([k, v]) => {
|
||||
let attrToTranslate = attributesToTranslate.find(equals(k))
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { EngineContext, useEngine } from '@/components/utils/EngineContext'
|
||||
import { DottedName } from 'modele-social'
|
||||
import { max } from 'ramda'
|
||||
import { useContext } from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { targetUnitSelector } from '@/selectors/simulationSelectors'
|
||||
|
@ -28,7 +27,7 @@ export default function Distribution() {
|
|||
.filter(([, value]) => value > 0)
|
||||
.sort(([, a], [, b]) => b - a)
|
||||
|
||||
const maximum = distribution.map(([, value]) => value).reduce(max, 0)
|
||||
const maximum = Math.max(...distribution.map(([, value]) => value))
|
||||
|
||||
return (
|
||||
<div className="distribution-chart__container">
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { Link } from '@/design-system/typography/link'
|
||||
import { SmallBody } from '@/design-system/typography/paragraphs'
|
||||
import { ASTNode } from 'publicodes'
|
||||
import { toPairs } from 'ramda'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
@ -27,7 +26,7 @@ export default function InputSuggestions({
|
|||
|
||||
return (
|
||||
<StyledInputSuggestion className={className}>
|
||||
{toPairs(suggestions).map(([text, value]: [string, ASTNode]) => {
|
||||
{Object.entries(suggestions).map(([text, value]: [string, ASTNode]) => {
|
||||
return (
|
||||
<Link
|
||||
key={text}
|
||||
|
|
|
@ -28,19 +28,19 @@ export default function Footer() {
|
|||
? `${window.location.protocol}//${window.location.host}`
|
||||
: '') + window.location.pathname
|
||||
const uri = decodeURIComponent(encodedUri || '').replace(/\/$/, '')
|
||||
const hrefLink = hrefLangLink[language][uri] || []
|
||||
const hrefLink = hrefLangLink[language][uri]
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
{hrefLink.map(({ href, hrefLang }) => (
|
||||
{hrefLink && (
|
||||
<link
|
||||
key={hrefLang}
|
||||
key={hrefLink.hrefLang}
|
||||
rel="alternate"
|
||||
hrefLang={hrefLang}
|
||||
href={href}
|
||||
hrefLang={hrefLink.hrefLang}
|
||||
href={hrefLink.href}
|
||||
/>
|
||||
))}
|
||||
)}
|
||||
</Helmet>
|
||||
<div
|
||||
css={`
|
||||
|
@ -97,23 +97,23 @@ export default function Footer() {
|
|||
<InscriptionBetaTesteur />
|
||||
</li>
|
||||
)}
|
||||
{hrefLink.map(({ hrefLang, href }) => (
|
||||
<li key={hrefLang}>
|
||||
<Link href={href} openInSameWindow>
|
||||
{hrefLang === 'fr' ? (
|
||||
{hrefLink && (
|
||||
<li key={hrefLink.hrefLang}>
|
||||
<Link href={hrefLink.href} openInSameWindow>
|
||||
{hrefLink.hrefLang === 'fr' ? (
|
||||
<>
|
||||
Passer en français <Emoji emoji="🇫🇷" />
|
||||
</>
|
||||
) : hrefLang === 'en' ? (
|
||||
) : hrefLink.hrefLang === 'en' ? (
|
||||
<>
|
||||
Switch to English <Emoji emoji="🇬🇧" />
|
||||
</>
|
||||
) : (
|
||||
hrefLang
|
||||
hrefLink.hrefLang
|
||||
)}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
)}
|
||||
</ul>
|
||||
</FooterColumn>
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ import { Li, Ul } from '@/design-system/typography/list'
|
|||
import { SmallBody } from '@/design-system/typography/paragraphs'
|
||||
import { targetUnitSelector } from '@/selectors/simulationSelectors'
|
||||
import { DottedName } from 'modele-social'
|
||||
import { max } from 'ramda'
|
||||
import { useContext } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
@ -115,7 +114,7 @@ function Distribution() {
|
|||
.filter(([, value]) => value > 0)
|
||||
.sort(([, a], [, b]) => b - a)
|
||||
|
||||
const maximum = distribution.map(([, value]) => value).reduce(max, 0)
|
||||
const maximum = Math.max(...distribution.map(([, value]) => value))
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -8,7 +8,6 @@ import { configSelector } from '@/selectors/simulationSelectors'
|
|||
import Engine, { ParsedRules, serializeEvaluation } from 'publicodes'
|
||||
import { DottedName } from 'modele-social'
|
||||
import { setActiveTarget, batchUpdateSituation } from '@/actions/actions'
|
||||
import { isEmpty } from 'ramda'
|
||||
|
||||
type Objectifs = (string | { objectifs: string[] })[]
|
||||
type ShortName = string
|
||||
|
@ -44,7 +43,7 @@ export default function useSearchParamsSimulationSharing() {
|
|||
searchParams,
|
||||
dottedNameParamName
|
||||
)
|
||||
if (!isEmpty(newSituation)) {
|
||||
if (Object.keys(newSituation).length > 0) {
|
||||
dispatch(batchUpdateSituation(newSituation as Situation))
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { assoc, mapObjIndexed } from 'ramda'
|
||||
import { Rule } from 'publicodes'
|
||||
|
||||
type Translation = Record<string, string>
|
||||
|
@ -15,21 +14,17 @@ const translateSuggestion: translateAttribute = (
|
|||
rule,
|
||||
translation,
|
||||
lang
|
||||
) =>
|
||||
assoc(
|
||||
'suggestions',
|
||||
Object.entries(rule.suggestions!).reduce(
|
||||
(acc, [name, value]) => ({
|
||||
...acc,
|
||||
[translation[`${prop}.${name}.${lang}`]?.replace(
|
||||
/^\[automatic\] /,
|
||||
''
|
||||
)]: value,
|
||||
}),
|
||||
{}
|
||||
),
|
||||
rule
|
||||
)
|
||||
) => ({
|
||||
...rule,
|
||||
suggestions: Object.entries(rule.suggestions!).reduce(
|
||||
(acc, [name, value]) => ({
|
||||
...acc,
|
||||
[translation[`${prop}.${name}.${lang}`]?.replace(/^\[automatic\] /, '')]:
|
||||
value,
|
||||
}),
|
||||
{}
|
||||
),
|
||||
})
|
||||
|
||||
export const attributesToTranslate = [
|
||||
'titre',
|
||||
|
@ -49,7 +44,7 @@ const translateProp =
|
|||
let propTrans = translation[prop + '.' + lang]
|
||||
propTrans = propTrans?.replace(/^\[automatic\] /, '')
|
||||
|
||||
return propTrans ? assoc(prop, propTrans, rule) : rule
|
||||
return propTrans ? { ...rule, [prop]: propTrans } : rule
|
||||
}
|
||||
|
||||
function translateRule<Names extends string>(
|
||||
|
@ -74,13 +69,14 @@ export default function translateRules<Names extends string>(
|
|||
translations: Record<Names, Translation>,
|
||||
rules: Record<Names, Rule>
|
||||
): Record<Names, Rule> {
|
||||
const translatedRules = mapObjIndexed(
|
||||
(rule: Rule, name: string) =>
|
||||
const translatedRules = Object.fromEntries(
|
||||
Object.entries<Rule>(rules).map(([name, rule]) => [
|
||||
name,
|
||||
rule && typeof rule === 'object'
|
||||
? translateRule(lang, translations, name, rule)
|
||||
: rule,
|
||||
rules
|
||||
])
|
||||
)
|
||||
|
||||
return translatedRules
|
||||
return translatedRules as Record<Names, Rule>
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import { ScrollToTop } from '@/components/utils/Scroll'
|
|||
import { Item, Select } from '@/design-system/field/Select'
|
||||
import { H1, H2 } from '@/design-system/typography/heading'
|
||||
import { formatValue } from 'publicodes'
|
||||
import { sum, uniq } from 'ramda'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
@ -33,17 +32,21 @@ const ressources = {
|
|||
2022: ressources2022,
|
||||
} as const
|
||||
|
||||
const arraySum = (arr: number[]) => arr.reduce((a, b) => a + b, 0)
|
||||
|
||||
export default function Budget() {
|
||||
const years = ['2019', '2020', '2021', '2022'] as const
|
||||
const quarters = ['T1', 'T2', 'T3', 'T4']
|
||||
const [selectedYear, setSelectedYear] = useState<typeof years[number]>(
|
||||
years[years.length - 1]
|
||||
)
|
||||
const categories = uniq(
|
||||
quarters
|
||||
.map((q) => Object.keys(budget[selectedYear]?.[q] ?? {}))
|
||||
.reduce((acc, curr) => [...acc, ...curr], [])
|
||||
)
|
||||
const categories = [
|
||||
...new Set(
|
||||
quarters
|
||||
.map((q) => Object.keys(budget[selectedYear]?.[q] ?? {}))
|
||||
.reduce((acc, curr) => [...acc, ...curr], [])
|
||||
),
|
||||
]
|
||||
|
||||
const { language } = useTranslation().i18n
|
||||
|
||||
|
@ -115,7 +118,7 @@ export default function Budget() {
|
|||
})}
|
||||
<td>
|
||||
{formatValue(
|
||||
sum(
|
||||
arraySum(
|
||||
quarters.map(
|
||||
(q) => budget[selectedYear]?.[q]?.[label] ?? 0
|
||||
)
|
||||
|
@ -133,7 +136,7 @@ export default function Budget() {
|
|||
<tr>
|
||||
<td>Total HT</td>
|
||||
{quarters.map((q) => {
|
||||
const value = sum(
|
||||
const value = arraySum(
|
||||
Object.values(budget[selectedYear]?.[q] ?? {})
|
||||
)
|
||||
|
||||
|
@ -150,9 +153,11 @@ export default function Budget() {
|
|||
})}
|
||||
<td>
|
||||
{formatValue(
|
||||
sum(
|
||||
arraySum(
|
||||
quarters.map((q) =>
|
||||
sum(Object.values(budget[selectedYear]?.[q] ?? {}))
|
||||
arraySum(
|
||||
Object.values(budget[selectedYear]?.[q] ?? {})
|
||||
)
|
||||
)
|
||||
),
|
||||
{
|
||||
|
@ -166,7 +171,8 @@ export default function Budget() {
|
|||
<td>Total TTC</td>
|
||||
{quarters.map((q) => {
|
||||
const value = Math.round(
|
||||
sum(Object.values(budget[selectedYear]?.[q] ?? {})) * 1.2
|
||||
arraySum(Object.values(budget[selectedYear]?.[q] ?? {})) *
|
||||
1.2
|
||||
)
|
||||
|
||||
return (
|
||||
|
@ -183,10 +189,10 @@ export default function Budget() {
|
|||
<td>
|
||||
{formatValue(
|
||||
Math.round(
|
||||
sum(
|
||||
arraySum(
|
||||
quarters.map(
|
||||
(q) =>
|
||||
sum(
|
||||
arraySum(
|
||||
Object.values(budget[selectedYear]?.[q] ?? {})
|
||||
) * 1.2
|
||||
)
|
||||
|
|
|
@ -2,7 +2,6 @@ import { SitePathsContext } from '@/components/utils/SitePathsContext'
|
|||
import { Button } from '@/design-system/buttons'
|
||||
import { H2, H3 } from '@/design-system/typography/heading'
|
||||
import { Body } from '@/design-system/typography/paragraphs'
|
||||
import { filter } from 'ramda'
|
||||
import { Fragment, useContext } from 'react'
|
||||
import { Helmet } from 'react-helmet-async'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
|
@ -111,20 +110,25 @@ export default function SetMainStatus() {
|
|||
)}
|
||||
</H2>
|
||||
|
||||
{Object.keys(filter(Boolean, possibleStatus)).map(
|
||||
/* https://github.com/microsoft/TypeScript/issues/32811 */
|
||||
(statut: any) => (
|
||||
<Fragment key={statut}>
|
||||
<H3>
|
||||
<StatutTitle statut={statut} language={i18n.language} />
|
||||
</H3>
|
||||
<Body>
|
||||
<StatutDescription statut={statut} />
|
||||
</Body>
|
||||
<StatutButton statut={statut} />
|
||||
</Fragment>
|
||||
)
|
||||
)}
|
||||
{Object.entries(possibleStatus)
|
||||
.filter(([, v]) => Boolean(v))
|
||||
.map(
|
||||
/* https://github.com/microsoft/TypeScript/issues/32811 */
|
||||
([statut]) => (
|
||||
<Fragment key={statut}>
|
||||
<H3>
|
||||
<StatutTitle
|
||||
statut={statut as LegalStatus}
|
||||
language={i18n.language}
|
||||
/>
|
||||
</H3>
|
||||
<Body>
|
||||
<StatutDescription statut={statut as LegalStatus} />
|
||||
</Body>
|
||||
<StatutButton statut={statut as LegalStatus} />
|
||||
</Fragment>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { SitePathsContext } from '@/components/utils/SitePathsContext'
|
||||
import { Link } from '@/design-system/typography/link'
|
||||
import { isNil } from 'ramda'
|
||||
import { useContext } from 'react'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
@ -77,7 +76,7 @@ export default function PreviousAnswers() {
|
|||
<PreviousAnswersList>
|
||||
{Object.entries(legalStatus).map(
|
||||
([key, value]) =>
|
||||
!isNil(value) && (
|
||||
value !== undefined && (
|
||||
<PreviousAnswersItem key={key}>
|
||||
<Link
|
||||
to={
|
||||
|
|
|
@ -3,7 +3,6 @@ import { FromBottom } from '@/components/ui/animate'
|
|||
import { SitePathsContext } from '@/components/utils/SitePathsContext'
|
||||
import { H1 } from '@/design-system/typography/heading'
|
||||
import { Link } from '@/design-system/typography/link'
|
||||
import { dropWhile, toPairs } from 'ramda'
|
||||
import { useContext, useEffect } from 'react'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
|
@ -29,21 +28,24 @@ const useResetFollowingAnswers = () => {
|
|||
) as (keyof typeof state.choixStatutJuridique.companyLegalStatus)[]
|
||||
)
|
||||
useEffect(() => {
|
||||
const companyStatusCurrentQuestionName = (toPairs(
|
||||
const companyStatusCurrentQuestionName = (Object.entries(
|
||||
sitePaths.créer.guideStatut
|
||||
).find(([, pathname]) => location.pathname === pathname) || [])[0]
|
||||
if (!companyStatusCurrentQuestionName) {
|
||||
return
|
||||
}
|
||||
|
||||
const answersToReset = dropWhile(
|
||||
(a) => a !== companyStatusCurrentQuestionName,
|
||||
answeredQuestion
|
||||
const firstAnswerToResetIndex = answeredQuestion.findIndex(
|
||||
(a) => a === companyStatusCurrentQuestionName
|
||||
)
|
||||
if (!answersToReset.length) {
|
||||
return
|
||||
|
||||
if (firstAnswerToResetIndex !== -1) {
|
||||
dispatch(
|
||||
resetCompanyStatusChoice(
|
||||
answeredQuestion.slice(firstAnswerToResetIndex)
|
||||
)
|
||||
)
|
||||
}
|
||||
dispatch(resetCompanyStatusChoice(answersToReset))
|
||||
}, [location.pathname, dispatch, sitePaths.créer.guideStatut])
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import { ScrollToTop } from '@/components/utils/Scroll'
|
|||
import { Spacing } from '@/design-system/layout'
|
||||
import { H1, H2 } from '@/design-system/typography/heading'
|
||||
import { Body, SmallBody } from '@/design-system/typography/paragraphs'
|
||||
import { intersection } from 'ramda'
|
||||
import { useContext } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { TrackPage } from '../../../ATInternetTracking'
|
||||
|
@ -90,10 +89,9 @@ export const ActivitéSelection = ({
|
|||
}: ActivitéSelectionProps) => {
|
||||
const { state } = useContext(StoreContext)
|
||||
const activitéRépondue = activitésRéponduesSelector(state)
|
||||
const nextButtonDisabled = !intersection(
|
||||
activitésEffectuéesSelector(state),
|
||||
activités
|
||||
).length
|
||||
const nextButtonDisabled = activitésEffectuéesSelector(state).every(
|
||||
(a) => !activités.includes(a)
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -3,7 +3,6 @@ import Emoji from '@/components/utils/Emoji'
|
|||
import { Strong } from '@/design-system/typography'
|
||||
import { Li, Ul } from '@/design-system/typography/list'
|
||||
import { Body } from '@/design-system/typography/paragraphs'
|
||||
import { add, mapObjIndexed } from 'ramda'
|
||||
import {
|
||||
Bar,
|
||||
BarChart,
|
||||
|
@ -25,9 +24,11 @@ export const SatisfactionStyle: [
|
|||
]
|
||||
|
||||
function toPercentage(data: Record<string, number>): Record<string, number> {
|
||||
const total = Object.values(data).reduce(add)
|
||||
const total = Object.values(data).reduce((a, b: number) => a + b, 0)
|
||||
|
||||
return { ...mapObjIndexed((value) => (100 * value) / total, data), total }
|
||||
return Object.fromEntries(
|
||||
Object.entries(data).map(([key, value]) => [key, (100 * value) / total])
|
||||
)
|
||||
}
|
||||
|
||||
type SatisfactionChartProps = {
|
||||
|
|
|
@ -8,7 +8,7 @@ import { Item, Select } from '@/design-system/field/Select'
|
|||
import { Spacing } from '@/design-system/layout'
|
||||
import { H2, H3 } from '@/design-system/typography/heading'
|
||||
import { formatValue } from 'publicodes'
|
||||
import { add, groupBy, mapObjIndexed, mergeWith, toPairs } from 'ramda'
|
||||
import { add, groupBy, mapObjIndexed, mergeWith } from 'ramda'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { useHistory, useLocation } from 'react-router-dom'
|
||||
|
@ -46,7 +46,7 @@ const isPAM = (name: string | undefined) =>
|
|||
].includes(name)
|
||||
|
||||
const filterByChapter2 = (pages: Pageish[], chapter2: Chapter2 | '') => {
|
||||
return toPairs(
|
||||
return Object.entries(
|
||||
groupBy(
|
||||
(p) => ('date' in p ? p.date : p.month),
|
||||
pages.filter(
|
||||
|
@ -67,7 +67,7 @@ const filterByChapter2 = (pages: Pageish[], chapter2: Chapter2 | '') => {
|
|||
}
|
||||
|
||||
function groupByDate(data: Pageish[]) {
|
||||
return toPairs(
|
||||
return Object.entries(
|
||||
groupBy(
|
||||
(p) => ('date' in p ? p.date : p.month),
|
||||
data.filter((d) => 'page' in d && d.page === 'accueil')
|
||||
|
|
|
@ -10,11 +10,10 @@ import { Button } from '@/design-system/buttons'
|
|||
import { Spacing } from '@/design-system/layout'
|
||||
import { headings } from '@/design-system/typography'
|
||||
import { Intro, SmallBody } from '@/design-system/typography/paragraphs'
|
||||
import { evaluateQuestion, hash } from '@/utils'
|
||||
import { evaluateQuestion, hash, omit } from '@/utils'
|
||||
import { Grid } from '@mui/material'
|
||||
import { DottedName } from 'modele-social'
|
||||
import Engine, { PublicodesExpression } from 'publicodes'
|
||||
import { omit } from 'ramda'
|
||||
import {
|
||||
Fragment,
|
||||
lazy,
|
||||
|
@ -80,7 +79,7 @@ function FormulairePublicodes() {
|
|||
const onChange = useCallback(
|
||||
(dottedName, value) => {
|
||||
if (value === undefined) {
|
||||
setSituation((situation) => omit([dottedName], situation))
|
||||
setSituation((situation) => omit(situation, dottedName))
|
||||
} else {
|
||||
setSituation((situation) => ({
|
||||
...situation,
|
||||
|
|
|
@ -2,7 +2,6 @@ import { Action } from '@/actions/actions'
|
|||
import { Commune } from '@/api/commune'
|
||||
import { PreviousSimulation } from '@/selectors/previousSimulationSelectors'
|
||||
import { DottedName } from 'modele-social'
|
||||
import { defaultTo, without } from 'ramda'
|
||||
import reduceReducers from 'reduce-reducers'
|
||||
import { combineReducers, Reducer } from 'redux'
|
||||
import { objectifsSelector } from '../selectors/simulationSelectors'
|
||||
|
@ -114,17 +113,16 @@ function simulation(
|
|||
}
|
||||
|
||||
case 'UPDATE_SITUATION': {
|
||||
const objectifs = without(
|
||||
['entreprise . charges'],
|
||||
objectifsSelector({ simulation: state } as RootState)
|
||||
)
|
||||
const objectifs = objectifsSelector({
|
||||
simulation: state,
|
||||
} as RootState).filter((name) => name !== 'entreprise . charges')
|
||||
const situation = state.situation
|
||||
const { fieldName: dottedName, value } = action
|
||||
if (value === undefined) {
|
||||
return { ...state, situation: omit(situation, dottedName) }
|
||||
}
|
||||
if (objectifs.includes(dottedName)) {
|
||||
const objectifsToReset = without([dottedName], objectifs)
|
||||
const objectifsToReset = objectifs.filter((name) => name !== dottedName)
|
||||
const newSituation = Object.fromEntries(
|
||||
Object.entries(situation).filter(
|
||||
([dottedName]) =>
|
||||
|
@ -160,7 +158,7 @@ function simulation(
|
|||
if (name === 'unfold') {
|
||||
return {
|
||||
...state,
|
||||
foldedSteps: without([step], state.foldedSteps),
|
||||
foldedSteps: state.foldedSteps.filter((name) => name !== step),
|
||||
unfoldedStep: step,
|
||||
}
|
||||
}
|
||||
|
@ -197,7 +195,7 @@ const mainReducer = combineReducers({
|
|||
explainedVariable,
|
||||
simulation,
|
||||
companySituation,
|
||||
previousSimulation: defaultTo(null) as Reducer<PreviousSimulation | null>,
|
||||
previousSimulation: ((p) => p ?? null) as Reducer<PreviousSimulation | null>,
|
||||
activeTargetInput,
|
||||
choixStatutJuridique,
|
||||
})
|
||||
|
|
|
@ -3,7 +3,7 @@ import { DottedName } from 'modele-social'
|
|||
|
||||
export type PreviousSimulation = {
|
||||
situation: Simulation['situation']
|
||||
activeTargetInput: RootState['activeTargetInput']
|
||||
activeTargetInput: DottedName | null
|
||||
foldedSteps: Array<DottedName> | undefined
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { MetadataSrc } from 'pages/Simulateurs/metadata-src'
|
||||
import { reduce, toPairs, zipObj } from 'ramda'
|
||||
import { LegalStatus } from '@/selectors/companyStatusSelectors'
|
||||
|
||||
export const LANDING_LEGAL_STATUS_LIST: Array<LegalStatus> = [
|
||||
|
@ -235,13 +234,12 @@ export const constructLocalizedSitePath = (language: 'en' | 'fr') => {
|
|||
export type SitePathsType = ReturnType<typeof constructLocalizedSitePath>
|
||||
|
||||
const deepReduce = (fn: any, initialValue?: any, object?: any): any =>
|
||||
reduce(
|
||||
Object.entries(object).reduce(
|
||||
(acc, [key, value]) =>
|
||||
typeof value === 'object'
|
||||
? deepReduce(fn, acc, value)
|
||||
: fn(acc, value, key),
|
||||
initialValue,
|
||||
toPairs(object)
|
||||
initialValue
|
||||
)
|
||||
|
||||
type SiteMap = Array<string>
|
||||
|
@ -271,12 +269,10 @@ const frSiteMap = generateSiteMap(constructLocalizedSitePath('fr')).map(
|
|||
)
|
||||
|
||||
export const hrefLangLink = {
|
||||
en: zipObj(
|
||||
enSiteMap,
|
||||
frSiteMap.map((href) => [{ href, hrefLang: 'fr' }])
|
||||
en: Object.fromEntries(
|
||||
enSiteMap.map((key, i) => [key, { href: frSiteMap[i], hrefLang: 'fr' }])
|
||||
),
|
||||
fr: zipObj(
|
||||
frSiteMap,
|
||||
enSiteMap.map((href) => [{ href, hrefLang: 'en' }])
|
||||
fr: Object.fromEntries(
|
||||
frSiteMap.map((key, i) => [key, { href: enSiteMap[i], hrefLang: 'en' }])
|
||||
),
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { Action } from '@/actions/actions'
|
||||
import { RootState } from '@/reducers/rootReducer'
|
||||
import { PreviousSimulation } from '@/selectors/previousSimulationSelectors'
|
||||
import { isEmpty } from 'ramda'
|
||||
import { Store } from 'redux'
|
||||
import { debounce } from '../utils'
|
||||
import * as safeLocalStorage from './safeLocalStorage'
|
||||
|
@ -21,7 +20,7 @@ export function setupSimulationPersistence(
|
|||
if (!state.simulation?.url) {
|
||||
return
|
||||
}
|
||||
if (isEmpty(state.simulation?.situation)) {
|
||||
if (Object.keys(state.simulation?.situation).length === 0) {
|
||||
return
|
||||
}
|
||||
safeLocalStorage.setItem(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { pipe } from 'ramda'
|
||||
import { currentSimulationSelector } from '@/selectors/previousSimulationSelectors'
|
||||
|
||||
export const serialize = pipe(currentSimulationSelector, JSON.stringify)
|
||||
export const serialize = (
|
||||
...args: Parameters<typeof currentSimulationSelector>
|
||||
) => JSON.stringify(currentSimulationSelector(...args))
|
||||
|
||||
export const deserialize = JSON.parse
|
||||
|
|
|
@ -77,8 +77,7 @@ export function hash(str: string): number {
|
|||
}
|
||||
|
||||
export function omit<T, K extends keyof T>(obj: T, key: K): Omit<T, K> {
|
||||
const returnObject = { ...obj }
|
||||
delete returnObject[key]
|
||||
const { [key]: _ignore, ...returnObject } = obj
|
||||
|
||||
return returnObject
|
||||
}
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
import { it, expect, describe } from 'vitest'
|
||||
import { parsePublicodes } from 'publicodes'
|
||||
import { uniq } from 'ramda'
|
||||
import rawRules from 'modele-social'
|
||||
import unitsTranslations from '../source/locales/units.yaml'
|
||||
|
||||
describe('Tests units', function () {
|
||||
it('use unit that exists in publicodes', function () {
|
||||
const { parsedRules } = parsePublicodes(rawRules)
|
||||
const units = uniq(
|
||||
Object.keys(parsedRules).reduce(
|
||||
(prev, name) => [
|
||||
...prev,
|
||||
...(parsedRules[name].unit?.numerators ?? []),
|
||||
...(parsedRules[name].unit?.denumerators ?? []),
|
||||
],
|
||||
[]
|
||||
)
|
||||
)
|
||||
const units = [
|
||||
...new Set(
|
||||
Object.keys(parsedRules).reduce(
|
||||
(prev, name) => [
|
||||
...prev,
|
||||
...(parsedRules[name].unit?.numerators ?? []),
|
||||
...(parsedRules[name].unit?.denumerators ?? []),
|
||||
],
|
||||
[]
|
||||
)
|
||||
),
|
||||
]
|
||||
|
||||
const blackList = ['€', '%']
|
||||
const translatedKeys = Object.keys(unitsTranslations.en)
|
||||
|
|
Loading…
Reference in New Issue