Ajoute des types TypeScript

Transforme quelques derniers composants class en fonctions
pull/960/head
Maxime Quandalle 2020-04-05 23:19:02 +02:00
parent 10372882ff
commit 0a03b7550c
No known key found for this signature in database
GPG Key ID: 428641C03D29CA10
32 changed files with 8981 additions and 248 deletions

View File

@ -134,6 +134,8 @@
"@types/react-redux": "^7.1.5",
"@types/react-router": "^5.1.2",
"@types/react-router-dom": "^5.1.0",
"@types/react-router-hash-link": "^1.2.1",
"@types/react-syntax-highlighter": "^11.0.4",
"@types/styled-components": "^4.1.19",
"@types/webpack-env": "^1.14.1",
"akh": "^3.1.2",

View File

@ -1,10 +1,10 @@
import { ThemeColorsProvider } from 'Components/utils/colors'
import { SitePathProvider, SitePaths } from 'Components/utils/withSitePaths'
import { TrackerProvider } from 'Components/utils/withTracker'
import { createBrowserHistory, History } from 'history'
import { createBrowserHistory } from 'history'
import { AvailableLangs } from 'i18n'
import i18next from 'i18next'
import React, { PureComponent } from 'react'
import React, { useEffect } from 'react'
import { I18nextProvider } from 'react-i18next'
import { Provider as ReduxProvider } from 'react-redux'
import { Router } from 'react-router-dom'
@ -41,7 +41,7 @@ if (
})
}
type ProviderProps = {
export type ProviderProps = {
tracker?: Tracker
basename: string
sitePaths: SitePaths
@ -49,39 +49,50 @@ type ProviderProps = {
initialStore: RootState
onStoreCreated: (store: Store) => void
reduxMiddlewares: Array<Middleware>
children: React.ReactNode
}
export default class Provider extends PureComponent<ProviderProps> {
history: History
store: Store
constructor(props: ProviderProps) {
super(props)
this.history = createBrowserHistory({
basename: process.env.NODE_ENV === 'production' ? '' : this.props.basename
})
this.props.tracker?.connectToHistory(this.history)
const storeEnhancer = composeEnhancers(
applyMiddleware(
// Allows us to painlessly do route transition in action creators
thunk.withExtraArgument({
history: this.history,
sitePaths: this.props.sitePaths
}),
...(props.reduxMiddlewares ?? [])
)
)
if (this.props.language) {
i18next.changeLanguage(this.props.language)
if (this.props.initialStore)
this.props.initialStore.lang = this.props.language
export default function Provider({
tracker,
basename,
sitePaths,
reduxMiddlewares,
language,
initialStore,
onStoreCreated,
children
}: ProviderProps) {
const history = createBrowserHistory({
basename: process.env.NODE_ENV === 'production' ? '' : basename
})
useEffect(() => {
tracker?.connectToHistory(history)
return () => {
tracker?.disconnectFromHistory()
}
this.store = createStore(reducers, this.props.initialStore, storeEnhancer)
this.props.onStoreCreated?.(this.store)
})
// Remove loader
var css = document.createElement('style')
css.type = 'text/css'
css.innerHTML = `
const storeEnhancer = composeEnhancers(
applyMiddleware(
// Allows us to painlessly do route transition in action creators
thunk.withExtraArgument({
history,
sitePaths
}),
...(reduxMiddlewares ?? [])
)
)
if (language) {
i18next.changeLanguage(language)
if (initialStore) initialStore.lang = language
}
const store = createStore(reducers, initialStore, storeEnhancer)
onStoreCreated?.(store)
// Remove loader
const css = document.createElement('style')
css.type = 'text/css'
css.innerHTML = `
#js {
animation: appear 0.5s;
opacity: 1;
@ -89,33 +100,28 @@ export default class Provider extends PureComponent<ProviderProps> {
#loading {
display: none !important;
}`
document.body.appendChild(css)
}
componentWillUnmount() {
this.props.tracker?.disconnectFromHistory()
}
render() {
const iframeCouleur =
new URLSearchParams(document?.location.search.substring(1)).get(
'couleur'
) ?? undefined
return (
// If IE < 11 display nothing
<ReduxProvider store={this.store}>
<ThemeColorsProvider
color={iframeCouleur && decodeURIComponent(iframeCouleur)}
>
<TrackerProvider value={this.props.tracker as Tracker}>
<SitePathProvider value={this.props.sitePaths}>
<I18nextProvider i18n={i18next}>
<Router history={this.history}>
<>{this.props.children}</>
</Router>
</I18nextProvider>
</SitePathProvider>
</TrackerProvider>
</ThemeColorsProvider>
</ReduxProvider>
)
}
document.body.appendChild(css)
const iframeCouleur =
new URLSearchParams(document?.location.search.substring(1)).get(
'couleur'
) ?? undefined
return (
// If IE < 11 display nothing
<ReduxProvider store={store}>
<ThemeColorsProvider
color={iframeCouleur && decodeURIComponent(iframeCouleur)}
>
<TrackerProvider value={tracker!}>
<SitePathProvider value={sitePaths}>
<I18nextProvider i18n={i18next}>
<Router history={history}>
<>{children}</>
</Router>
</I18nextProvider>
</SitePathProvider>
</TrackerProvider>
</ThemeColorsProvider>
</ReduxProvider>
)
}

View File

@ -1,34 +0,0 @@
import React, { Component } from 'react'
import emoji from 'react-easy-emoji'
import { withTranslation } from 'react-i18next'
const languageCodeToEmoji = {
en: '🇬🇧',
fr: '🇫🇷'
}
export default withTranslation()(
class LangSwitcher extends Component {
getUnusedLanguageCode = () => {
let languageCode = this.props.i18n.language
return !languageCode || languageCode === 'fr' ? 'en' : 'fr'
}
changeLanguage = () => {
let nextLanguage = this.getUnusedLanguageCode()
this.props.i18n.changeLanguage(nextLanguage)
this.forceUpdate()
}
render() {
const languageCode = this.getUnusedLanguageCode()
return (
<button
className={this.props.className || 'ui__ link-button'}
onClick={this.changeLanguage}>
{emoji(languageCodeToEmoji[languageCode])}{' '}
{languageCode.toUpperCase()}
</button>
)
}
}
)

View File

@ -0,0 +1,27 @@
import React from 'react'
import emoji from 'react-easy-emoji'
import { useTranslation } from 'react-i18next'
const languageCodeToEmoji = {
en: '🇬🇧',
fr: '🇫🇷'
}
export default function LangSwitcher({ className }: { className: string }) {
const { i18n } = useTranslation()
const languageCode = i18n.language
const unusedLanguageCode =
!languageCode || languageCode === 'fr' ? 'en' : 'fr'
const changeLanguage = () => {
i18n.changeLanguage(unusedLanguageCode)
}
return (
<button
className={className ?? 'ui__ link-button'}
onClick={changeLanguage}
>
{emoji(languageCodeToEmoji[languageCode as 'fr' | 'en'])}{' '}
{languageCode.toUpperCase()}
</button>
)
}

View File

@ -5,7 +5,7 @@ import { DottedName } from 'Publicode/rules'
import React from 'react'
import { Trans } from 'react-i18next'
import { connect, useSelector } from 'react-redux'
import { Redirect } from 'react-router-dom'
import { Redirect, useParams } from 'react-router-dom'
import {
noUserInputSelector,
parsedRulesSelector,
@ -15,12 +15,12 @@ import Rule from './rule/Rule'
import './RulePage.css'
import SearchButton from './SearchButton'
export default function RulePage({ match }) {
export default function RulePage() {
const parsedRules = useSelector(parsedRulesSelector)
const brancheName = useSelector(situationBranchNameSelector)
const valuesToShow = !useSelector(noUserInputSelector)
let name = match?.params?.name,
decodedRuleName = decodeRuleName(name)
const { name } = useParams()
const decodedRuleName = decodeRuleName(name)
const renderRule = (dottedName: DottedName) => {
return (
@ -36,7 +36,8 @@ export default function RulePage({ match }) {
)
}
if (!parsedRules[decodedRuleName]) return <Redirect to="/404" />
if (!parsedRules.hasOwnProperty(decodedRuleName))
return <Redirect to="/404" />
return renderRule(decodedRuleName as DottedName)
}

View File

@ -15,7 +15,11 @@ import {
import { softCatch } from '../../utils'
import './AnswerList.css'
export default function AnswerList({ onClose }) {
type AnswerListProps = {
onClose: () => void
}
export default function AnswerList({ onClose }: AnswerListProps) {
const dispatch = useDispatch()
const { folded, next } = useSelector(stepsToRules)
return (

View File

@ -3,7 +3,7 @@ import React from 'react'
import { Trans } from 'react-i18next'
import './Destinataire.css'
export default function Rule({ destinataire }) {
export default function Rule({ destinataire }: { destinataire: string }) {
let destinataireData = possiblesDestinataires[destinataire]
return destinataire && destinataireData ? (

View File

@ -14,7 +14,6 @@ export default function RuleHeader({
description,
question,
flatRule,
flatRules,
acronyme,
name,
title,
@ -26,7 +25,7 @@ export default function RuleHeader({
<section id="ruleHeader">
<header className="ui__ plain card">
<div>
<Namespace {...{ dottedName, flatRules, color: colors.textColor }} />
<Namespace {...{ dottedName }} />
<h1 style={{ color: colors.textColor }}>
{title || capitalise0(name)}
{acronyme && <> ({acronyme})</>}

View File

@ -1,26 +1,32 @@
import { ThemeColorsContext } from 'Components/utils/colors'
import { SitePathsContext } from 'Components/utils/withSitePaths'
import { DottedName } from 'Publicode/rules'
import React, { useContext } from 'react'
import emoji from 'react-easy-emoji'
import { useSelector } from 'react-redux'
import { Link } from 'react-router-dom'
import { parsedRulesSelector } from 'Selectors/analyseSelectors'
import { capitalise0 } from '../../utils'
import './Namespace.css'
export default function Namespace({ dottedName, flatRules, color }) {
export default function Namespace({ dottedName }: { dottedName: DottedName }) {
const sitePaths = useContext(SitePathsContext)
const colors = useContext(ThemeColorsContext)
const flatRules = useSelector(parsedRulesSelector)
return (
<ul id="namespace">
{dottedName
.split(' . ')
.slice(0, -1)
.reduce(
(memo, next) => [
(memo: string[][], next: string) => [
...memo,
[...(memo.length ? memo.reverse()[0] : []), next]
],
[]
)
.map(fragments => {
let ruleName = fragments.join(' . '),
.map((fragments: string[]) => {
let ruleName = fragments.join(' . ') as DottedName,
rule = flatRules[ruleName]
if (!rule) {
throw new Error(
@ -28,7 +34,7 @@ export default function Namespace({ dottedName, flatRules, color }) {
)
}
let ruleText = rule.title || capitalise0(rule.name),
style = { color }
style = { color: colors.textColor }
return (
<li style={style} key={fragments.join()}>

View File

@ -4,16 +4,21 @@ import React from 'react'
import { capitalise0 } from '../../utils'
import './References.css'
const findRefKey = link =>
const findRefKey = (link: string) =>
Object.keys(references).find(r => link.indexOf(r) > -1)
const cleanDomain = link =>
const cleanDomain = (link: string) =>
(link.indexOf('://') > -1 ? link.split('/')[2] : link.split('/')[0]).replace(
'www.',
''
)
function Ref({ name, link }) {
type RefProps = {
name: string
link: string
}
function Ref({ name, link }: RefProps) {
let refKey = findRefKey(link),
refData = (refKey && references[refKey]) || {},
domain = cleanDomain(link)

View File

@ -105,7 +105,6 @@ export default AttachDictionary(mecanisms)(function Rule({ dottedName }) {
description,
question,
flatRule: rule,
flatRules: rules,
name,
acronyme,
title,

View File

@ -1,7 +1,7 @@
import React from 'react'
import './InfoBulle.css'
export default function InfoBulle({ children }) {
export default function InfoBulle({ children }: { children: React.ReactNode }) {
return (
<span style={{ position: 'relative', verticalAlign: 'bottom' }}>
<span className="info-bulle__interrogation-mark" tabIndex={0}>

View File

@ -6,7 +6,7 @@ import style from 'react-syntax-highlighter/dist/esm/styles/prism/atom-dark'
SyntaxHighlighter.registerLanguage('yaml', yaml)
export default ({ source }) => (
export default ({ source }: { source: string }) => (
<div css="position: relative; margin-bottom: 1rem">
<SyntaxHighlighter language="yaml" style={style}>
{source}

View File

@ -1,91 +0,0 @@
import { omit } from 'ramda'
import React, { Component } from 'react'
const forEachParent = (node, fn) => {
if (!node) {
return
}
fn(node)
forEachParent(node.parentNode, fn)
}
export class ScrollToTop extends Component {
static defaultProps = {
behavior: 'auto'
}
componentDidMount() {
if ('parentIFrame' in window) {
window.parentIFrame.scrollToOffset(0, 0)
return
}
forEachParent(this.ref, elem => (elem.scrollTop = 0))
try {
window.scroll({
top: 0,
behavior: this.props.behavior
})
} catch (e) {
window.scroll(0, 0)
}
}
render() {
return (
<div
ref={ref => {
this.ref = ref
}}
/>
)
}
}
export class ScrollToElement extends Component {
static defaultProps = {
behavior: 'smooth',
style: {},
onlyIfNotVisible: false
}
scrollIfNeeded = () => {
if (
this.props.when === false ||
(this.props.onlyIfNotVisible &&
this.ref.getBoundingClientRect().top >= 0 &&
this.ref.getBoundingClientRect().bottom <= window.innerHeight)
) {
return
}
try {
this.ref.scrollIntoView({
behavior: this.props.behavior,
block: 'nearest',
inline: 'nearest'
})
} catch (error) {
this.ref.scrollIntoView({
behavior: this.props.behavior
})
}
}
componentDidMount() {
this.scrollIfNeeded()
}
componentDidUpdate() {
this.scrollIfNeeded()
}
render() {
return (
<div
{...omit(['onlyIfNotVisible', 'when'], this.props)}
style={{
...this.props.style,
...(!this.props.children ? { position: 'absolute' } : {})
}}
ref={ref => (this.ref = ref)}
/>
)
}
}
export default {
toElement: ScrollToElement,
toTop: ScrollToTop
}

View File

@ -0,0 +1,90 @@
import React, { useEffect, useRef } from 'react'
const forEachParent = (node: Node | null, fn: (node: Node) => void) => {
if (!node) {
return
}
fn(node)
forEachParent(node.parentNode, fn)
}
export function ScrollToTop({
behavior = 'auto'
}: {
behavior?: ScrollBehavior
}) {
const ref = useRef<HTMLDivElement>(null)
useEffect(() => {
if ('parentIFrame' in window) {
;(window as any).parentIFrame.scrollToOffset(0, 0)
return
}
forEachParent(ref.current, elem => ((elem as any).scrollTop = 0))
try {
window.scroll({
top: 0,
behavior
})
} catch (e) {
window.scroll(0, 0)
}
}, [])
return <div ref={ref} />
}
type ScrollToElementProps = React.ComponentProps<'div'> & {
onlyIfNotVisible?: boolean
when?: boolean
behavior?: ScrollBehavior
}
export function ScrollToElement({
onlyIfNotVisible = false,
when,
behavior = 'smooth',
children,
style,
...otherProps
}: ScrollToElementProps) {
const ref = useRef<HTMLDivElement>(null)
const scrollIfNeeded = () => {
if (
when === false ||
(onlyIfNotVisible &&
ref.current &&
ref.current.getBoundingClientRect().top >= 0 &&
ref.current.getBoundingClientRect().bottom <= window.innerHeight)
) {
return
}
try {
ref.current?.scrollIntoView({
behavior,
block: 'nearest',
inline: 'nearest'
})
} catch (error) {
ref.current?.scrollIntoView({
behavior
})
}
}
useEffect(scrollIfNeeded)
return (
<div
{...otherProps}
style={{
...style,
...(!children ? { position: 'absolute' } : {})
}}
ref={ref}
/>
)
}
export default {
toElement: ScrollToElement,
toTop: ScrollToTop
}

View File

@ -65,7 +65,7 @@ const generateTheme = (themeColor?: string) => {
grayColor = '#00000099',
textColor = findContrastedTextColor(color, true), // the 'simple' version feels better...
inverseTextColor = textColor === '#ffffff' ? '#000' : '#fff',
lightenTextColor = textColor =>
lightenTextColor = (textColor: string) =>
textColor === '#ffffff' ? 'rgba(255, 255, 255, .7)' : 'rgba(0, 0, 0, .7)',
lighterTextColor = darkColor + 'cc',
lighterInverseTextColor = lightenTextColor(inverseTextColor),

View File

@ -4,7 +4,7 @@ import emoji from 'react-easy-emoji'
import ReactMarkdown, { ReactMarkdownProps } from 'react-markdown'
import { Link } from 'react-router-dom'
function LinkRenderer({ href, children }) {
function LinkRenderer({ href, children }: { href: string; children: string }) {
if (!href.startsWith('http')) {
return <Link to={href}>{children}</Link>
}
@ -25,14 +25,16 @@ function LinkRenderer({ href, children }) {
</a>
)
}
const TextRenderer = ({ children }) => <>{emoji(children)}</>
const TextRenderer = ({ children }: { children: string }) => (
<>{emoji(children)}</>
)
type MarkdownProps = ReactMarkdownProps & {
source: string | undefined
className?: string
}
const CodeBlock = ({ value, language }) =>
const CodeBlock = ({ value, language }: { value: string; language: string }) =>
language === 'yaml' ? (
<PublicodeHighlighter source={value} />
) : (

View File

@ -43,10 +43,10 @@ function getUnitKey(unit: string, lng: string): string {
return key || unit
}
let printUnits = (units: Array<string>, count: number, lng): string =>
let printUnits = (units: Array<string>, count: number, lng: string): string =>
units
.filter(unit => unit !== '%')
.map(unit => i18n.t(`units:${unit}`, { count, lng: lng }))
.map(unit => i18n.t(`units:${unit}`, { count, lng }))
.join('.')
const plural = 2
@ -193,7 +193,11 @@ export function convertUnit(
to: Unit | undefined,
value: Evaluation<number>
): Evaluation<number>
export function convertUnit(from, to, value): any {
export function convertUnit(
from: Unit | undefined,
to: Unit | undefined,
value: number | Evaluation<number>
) {
if (!areUnitConvertible(from, to)) {
throw new Error(
`Impossible de convertir l'unité '${serializeUnit(

View File

@ -260,6 +260,9 @@ 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:
titre.en: '[automatic] short-time working'
titre.fr: chômage partiel
chômage partiel . coût employeur habituel:
titre.en: '[automatic] regular employer cost'
titre.fr: coût employeur habituel

View File

@ -106,7 +106,7 @@ function updateSituation(
return { ...removePreviousTarget(situation), [fieldName]: value }
}
function updateDefaultUnit(situation, { toUnit, analysis }) {
function updateDefaultUnit(situation: Situation, { toUnit, analysis }) {
const unit = parseUnit(toUnit)
const goals = goalsFromAnalysis(analysis)
const convertedSituation = Object.keys(situation)

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@ import { Helmet } from 'react-helmet'
import { useTranslation } from 'react-i18next'
import { Route, Switch } from 'react-router-dom'
import 'Ui/index.css'
import Provider from '../../Provider'
import Provider, { ProviderProps } from '../../Provider'
import {
persistEverything,
retrievePersistedState
@ -55,7 +55,13 @@ const middlewares = [
trackSimulatorActions(tracker)
]
function InFranceRoute({ basename, language, rules }) {
type InFranceRouteProps = {
basename: ProviderProps['basename']
language: ProviderProps['language']
rules: ProviderProps['initialStore']['rules']
}
function InFranceRoute({ basename, language, rules }: InFranceRouteProps) {
useEffect(() => {
getSessionStorage()?.setItem('lang', language)
}, [language])

View File

@ -8,7 +8,6 @@ import translations from '../../locales/rules-en.yaml'
import App from './App'
let anchor = document.querySelector('#js')
console.log(translateRules('en', translations, rules))
render(
<App
language="en"

View File

@ -1,7 +1,12 @@
import React from 'react'
import { ChromePicker } from 'react-color'
import { ChromePicker, ChromePickerProps } from 'react-color'
export default function ColorPicker({ color, onChange }) {
type ColorPickerProps = {
color: ChromePickerProps['color']
onChange: (color: string) => void
}
export default function ColorPicker({ color, onChange }: ColorPickerProps) {
return (
<ChromePicker
color={color}

View File

@ -1,5 +1,6 @@
import CompanyDetails from 'Components/CompanyDetails'
import { formatValue } from 'Engine/format'
import { DottedName } from 'Publicode/rules'
import React, { useRef } from 'react'
import { Trans } from 'react-i18next'
import { useSelector } from 'react-redux'
@ -64,7 +65,6 @@ export function AideDéclarationIndépendantsRécapitulatif() {
/>
<SimpleField
label="Il cotise sur la base"
dottedName={
'dirigeant . indépendant . conjoint collaborateur . assiette'
}
@ -90,11 +90,10 @@ export function AideDéclarationIndépendantsRécapitulatif() {
}
type SimpleFieldProps = {
label?: string
dottedName: string
dottedName: DottedName
unit?: string
}
function SimpleField({ label, dottedName, unit }: SimpleFieldProps) {
function SimpleField({ dottedName, unit }: SimpleFieldProps) {
const situation = useSelector(situationSelector)
const rules = useSelector((state: RootState) => state.rules)
const value = situation[dottedName]

View File

@ -1,4 +1,5 @@
import { Markdown } from 'Components/utils/markdown'
import { ScrollToTop } from 'Components/utils/Scroll'
import { SitePathsContext } from 'Components/utils/withSitePaths'
import React, { useContext, useEffect } from 'react'
import emoji from 'react-easy-emoji'
@ -11,33 +12,38 @@ import { hideNewsBanner } from '../../layout/NewsBanner'
const fetcher = (url: RequestInfo) => fetch(url).then(r => r.json())
const slugify = (name: string) => name.toLowerCase().replace(' ', '-')
type ReleasesData = Array<{
name: string
description: string
}>
export default function Nouveautés() {
// The release.json file may be big, we don't want to include it in the main
// bundle, that's why we only fetch it on this page. Alternatively we could
// use import("data/release.json") and configure code splitting with Webpack.
const { data } = useSWR('/data/releases.json', fetcher)
const { data } = useSWR<ReleasesData>('/data/releases.json', fetcher)
const history = useHistory()
const sitePaths = useContext(SitePathsContext)
const slug = useRouteMatch<{ slug: string }>(`${sitePaths.nouveautés}/:slug`)
?.params?.slug
const selectedRelease = data?.findIndex(({ name }) => slugify(name) === slug)
useEffect(hideNewsBanner, [])
useEffect(() => {
window.scrollTo({ top: 0 })
}, [selectedRelease])
if (!data) {
return null
}
const selectedRelease = data.findIndex(({ name }) => slugify(name) === slug)
const getPath = (index: number) =>
`${sitePaths.nouveautés}/${slugify(data[index].name)}`
if (!data) {
return null
} else if (!slug || selectedRelease === -1) {
if (!slug || selectedRelease === -1) {
return <Redirect to={getPath(0)} />
}
return (
<>
<ScrollToTop key={selectedRelease} />
<h1>Les nouveautés {emoji('✨')}</h1>
<p>
Nous améliorons le site en continu à partir de vos retours. Découvrez
@ -101,7 +107,7 @@ export default function Nouveautés() {
const removeGithubIssuesReferences = (text: string) =>
text.replace(/#[0-9]{1,5}/g, '')
const TextRenderer = ({ children }) => (
const TextRenderer = ({ children }: { children: string }) => (
<>{emoji(removeGithubIssuesReferences(children))}</>
)

View File

@ -86,8 +86,6 @@ function SimpleField({ dottedName }: SimpleFieldProps) {
const parsedRules = useSelector(parsedRulesSelector)
const value = useSelector(situationSelector)[dottedName]
const onChange = x => dispatch(updateSituation(dottedName, x))
if (!analysis.isApplicable) {
return null
}
@ -109,7 +107,7 @@ function SimpleField({ dottedName }: SimpleFieldProps) {
dottedName={dottedName}
rules={parsedRules}
value={value}
onChange={onChange}
onChange={x => dispatch(updateSituation(dottedName, x))}
useSwitch
/>
</div>

View File

@ -8,14 +8,15 @@ export const flatActivités = pipe(
unnest
)(activités)
export const getActivité = a => flatActivités.find(item => item.titre === a)
export const getActivité = (a: string) =>
flatActivités.find(item => item.titre === a)
export const getTranslatedActivité = (title, language) => ({
export const getTranslatedActivité = (title: string, language: string) => ({
...getActivité(title),
...(language !== 'fr' && activitésEn[title])
})
export const getMinimumDéclaration = a => {
export const getMinimumDéclaration = (a: string) => {
const activité = getActivité(a)
if (activité['seuil pro'] === 0 && !activité['seuil régime général']) {
return 'RÉGIME_GÉNÉRAL_NON_DISPONIBLE'
@ -31,7 +32,7 @@ export const getMinimumDéclaration = a => {
}
return null
}
export const hasConditions = a => {
export const hasConditions = (a: string) => {
const activité = getActivité(a)
return !!(
activité['exonérée sauf si'] ||
@ -42,5 +43,5 @@ export const hasConditions = a => {
)
}
export const getSousActivités = a =>
(getActivité(a).activités || []).map(({ titre }) => titre)
export const getSousActivités = (a: string) =>
(getActivité(a).activités || []).map(({ titre }: { titre: string }) => titre)

View File

@ -141,7 +141,7 @@ export const constructLocalizedSitePath = (language: string) => {
export type SitePathsType = ReturnType<typeof constructLocalizedSitePath>
const deepReduce = (fn, initialValue?: any, object?: any) =>
const deepReduce = (fn: any, initialValue?: any, object?: any): any =>
reduce(
(acc, [key, value]) =>
typeof value === 'object'

View File

@ -80,9 +80,19 @@ export default function Studio() {
)
}
export const Results = ({ targets, onClickUpdate, onClickShare }) => {
type ResultsProps = {
targets: string[]
onClickUpdate: React.MouseEventHandler
onClickShare: React.MouseEventHandler
}
export const Results = ({
targets,
onClickUpdate,
onClickShare
}: ResultsProps) => {
const [rule, setCurrentTarget] = useState<string>()
const currentTarget = rule ?? last(targets)
const currentTarget = rule ?? (last(targets) as string)
const error = Engine.useError()
// EN ATTENDANT d'AVOIR une meilleure gestion d'erreur, on va mocker
// console.warn

View File

@ -1,6 +1,6 @@
export function capitalise0(name: undefined): undefined
export function capitalise0(name: string): string
export function capitalise0(name) {
export function capitalise0(name?: string) {
return name && name[0].toUpperCase() + name.slice(1)
}
@ -49,7 +49,7 @@ export function softCatch<ArgType, ReturnType>(
export function mapOrApply<A, B>(fn: (a: A) => B, x: A): B
export function mapOrApply<A, B>(fn: (a: A) => B, x: Array<A>): Array<B>
export function mapOrApply(fn, x) {
export function mapOrApply<A, B>(fn: (a: A) => B, x: A | Array<A>) {
return Array.isArray(x) ? x.map(fn) : fn(x)
}

View File

@ -1333,7 +1333,7 @@
hoist-non-react-statics "^3.3.0"
redux "^4.0.0"
"@types/react-router-dom@^5.1.0":
"@types/react-router-dom@*", "@types/react-router-dom@^5.1.0":
version "5.1.3"
resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.3.tgz#b5d28e7850bd274d944c0fbbe5d57e6b30d71196"
integrity sha512-pCq7AkOvjE65jkGS5fQwQhvUp4+4PVD9g39gXLZViP2UqFiFzsEpB3PKf0O6mdbKsewSK8N14/eegisa/0CwnA==
@ -1342,6 +1342,14 @@
"@types/react" "*"
"@types/react-router" "*"
"@types/react-router-hash-link@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@types/react-router-hash-link/-/react-router-hash-link-1.2.1.tgz#fba7dc351cef2985791023018b7a5dbd0653c843"
integrity sha512-jdzPGE8jFGq7fHUpPaKrJvLW1Yhoe5MQCrmgeesC+eSLseMj3cGCTYMDA4BNWG8JQmwO8NTYt/oT3uBZ77pmBA==
dependencies:
"@types/react" "*"
"@types/react-router-dom" "*"
"@types/react-router@*", "@types/react-router@^5.1.2":
version "5.1.4"
resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.4.tgz#7d70bd905543cb6bcbdcc6bd98902332054f31a6"
@ -1350,6 +1358,13 @@
"@types/history" "*"
"@types/react" "*"
"@types/react-syntax-highlighter@^11.0.4":
version "11.0.4"
resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.4.tgz#d86d17697db62f98046874f62fdb3e53a0bbc4cd"
integrity sha512-9GfTo3a0PHwQeTVoqs0g5bS28KkSY48pp5659wA+Dp4MqceDEa8EHBqrllJvvtyusszyJhViUEap0FDvlk/9Zg==
dependencies:
"@types/react" "*"
"@types/react@*", "@types/react@^16.9.11":
version "16.9.23"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.23.tgz#1a66c6d468ba11a8943ad958a8cb3e737568271c"