🎨 Ajoute une recherche globale de simulateurs et de règles
Cette recherche se base sur Algolia. Elle est accessible depuis toutes les pages. Elle remplace intégralement la précédente recherche. La fonctionnalité possède deux parties: - la mise à jour des données (au build) - l'UI sous la forme de composants L'UI se base sur la bibliothèque `react-instantsearch` qui est developpé et maintenu par Algolia. Co-authored-by: Johan Girod <johan.girod@beta.gouv.fr> Co-authored-by: Maxime Quandalle <maxime.quandalle@gmail.com> Co-authored-by: Alexandre Hajjar <alexandre.hajjar@gmail.com>pull/1726/head
parent
73b3711510
commit
50826d74e4
|
@ -58,10 +58,20 @@ jobs:
|
|||
env:
|
||||
AT_INTERNET_SITE_ID: ${{ needs.deploy-context.outputs.env-name == 'master' && 617190 || 617189 }}
|
||||
NODE_ENV: production
|
||||
ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }}
|
||||
ALGOLIA_SEARCH_KEY: ${{secrets.ALGOLIA_SEARCH_KEY}}
|
||||
ALGOLIA_INDEX_PREFIX: monentreprise-${{needs.deploy-context.outputs.env-name}}-
|
||||
- name: Replace site placeholders in netlify.toml redirection file
|
||||
run: sed -i "s|:SITE_FR|$FR_BASE_URL|g" netlify.toml;
|
||||
sed -i "s|:SITE_EN|$EN_BASE_URL|g" netlify.toml;
|
||||
sed -i "s|:SITE_PUBLICODES|$PUBLICODES_BASE_URL|g" netlify.toml
|
||||
- name: Update Algolia index
|
||||
run: yarn workspace mon-entreprise algolia:update
|
||||
env:
|
||||
ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }}
|
||||
ALGOLIA_ADMIN_KEY: ${{ secrets.ALGOLIA_APP_ID }}
|
||||
ALGOLIA_SEARCH_KEY: ${{secrets.ALGOLIA_SEARCH_KEY}}
|
||||
ALGOLIA_INDEX_PREFIX: monentreprise-${{needs.deploy-context.outputs.env-name}}-
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: static-site
|
||||
|
|
|
@ -31,7 +31,7 @@ Nous utilisons :
|
|||
### Démarrage
|
||||
|
||||
Si possible, assurez-vous d'avoir toutes les clés d'API nécessaires dans votre fichier
|
||||
`mon-entreprise/.env`.
|
||||
`mon-entreprise/.env` (un template est disponible dans `mon-entreprise/.env.template`).
|
||||
**NB : ne vous inquiétez pas, ceci n'est pas nécessaire pour effectuer une première contribution à
|
||||
la base de code !** Cependant, vous en aurez besoin pour la commande `yarn prepare` et pour les
|
||||
commandes de traduction automatique français -> anglais. Si vous êtes confronté à ce type de besoin,
|
||||
|
@ -173,6 +173,9 @@ Pour tester les règles, il est recommandé de:
|
|||
- créer des cas de tests de non-régression sous la forme de nouveaux snapshots (cf.
|
||||
`mon-entreprise/test/regressions`).
|
||||
|
||||
En local, le moteur de recherche n'est pas mis à jour automatiquement et la liste des règles
|
||||
est exposée ici: http://localhost:8080/mon-entreprise/documentation/dev
|
||||
|
||||
## Publicodes
|
||||
|
||||
### Documentation
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
ALGOLIA_APP_ID=
|
||||
ALGOLIA_ADMIN_KEY=
|
||||
ALGOLIA_SEARCH_KEY=
|
||||
ALGOLIA_INDEX_PREFIX=monentreprise-{env}-
|
||||
INSEE_SIRENE_API_SECRET=
|
||||
MATOMO_TOKEN=
|
||||
ATINTERNET_API_ACCESS_KEY=
|
||||
ATINTERNET_API_SECRET_KEY=
|
||||
DEEPL_API_SECRET=
|
||||
ZAMMAD_API_SECRET_KEY=
|
||||
GITHUB_API_SECRET=
|
|
@ -0,0 +1,29 @@
|
|||
const fr = Cypress.env('language') === 'fr'
|
||||
|
||||
const simulateursPath = '.ais-Hits-list'
|
||||
const reglesPath = '.ais-InfiniteHits-list'
|
||||
|
||||
describe('Recherche globales', () => {
|
||||
if (!fr) {
|
||||
return
|
||||
}
|
||||
|
||||
it('should display the search results when the magnifying glass is clicked', () => {
|
||||
cy.visit('/')
|
||||
|
||||
cy.get('#search-display-button').click()
|
||||
|
||||
cy.wait(30)
|
||||
cy.focused().should('have.attr', 'type', 'search')
|
||||
|
||||
cy.wait(100)
|
||||
cy.get(simulateursPath).children().should('have.length', 6)
|
||||
cy.get(reglesPath).children().should('have.length', 20)
|
||||
|
||||
cy.focused().type('avocat')
|
||||
|
||||
cy.wait(100)
|
||||
cy.get(simulateursPath).children().should('have.length', 1)
|
||||
cy.get(reglesPath).children().should('have.length', 1)
|
||||
})
|
||||
})
|
|
@ -67,12 +67,15 @@
|
|||
"@rehooks/local-storage": "^2.1.1",
|
||||
"@sentry/react": "^6.3.5",
|
||||
"@sentry/tracing": "^6.3.5",
|
||||
"@types/react-instantsearch-dom": "^6.10.1",
|
||||
"algoliasearch": "^4.10.2",
|
||||
"classnames": "^2.2.5",
|
||||
"color-convert": "^1.9.2",
|
||||
"core-js": "^3.2.1",
|
||||
"focus-trap-react": "^3.1.2",
|
||||
"fuse.js": "5.2.1",
|
||||
"iframe-resizer": "^4.1.1",
|
||||
"instantsearch.css": "^7.4.5",
|
||||
"modele-social": "^0.2.0",
|
||||
"publicodes": "^1.0.0-beta.15",
|
||||
"publicodes-react": "^1.0.0-beta.15",
|
||||
|
@ -83,6 +86,8 @@
|
|||
"react-easy-emoji": "^1.2.0",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-i18next": "^11.0.0",
|
||||
"react-instantsearch": "^6.11.2",
|
||||
"react-instantsearch-dom": "^6.11.2",
|
||||
"react-markdown": "^4.1.0",
|
||||
"react-monaco-editor": "^0.40.0",
|
||||
"react-number-format": "^4.3.1",
|
||||
|
@ -128,6 +133,7 @@
|
|||
"serve:dev": "concurrently -k \"yarn run serve:dev:mon-entreprise\" \"yarn run serve:dev:mycompanyinfrance & yarn run serve:dev:publicodes\"",
|
||||
"serve:dev:mon-entreprise": "PORT=5000 serve --config serve.mon-entreprise.json --no-clipboard",
|
||||
"serve:dev:publicodes": "PORT=5002 serve --config serve.publicodes.json --no-clipboard",
|
||||
"serve:dev:mycompanyinfrance": "PORT=5001 serve --config serve.infrance.json --no-clipboard"
|
||||
"serve:dev:mycompanyinfrance": "PORT=5001 serve --config serve.infrance.json --no-clipboard",
|
||||
"algolia:update": "node scripts/search/update-data.js"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
require('dotenv').config()
|
||||
|
||||
const algoliasearch = require('algoliasearch')
|
||||
const rawRules = require('modele-social')
|
||||
const rules = require('publicodes').parsePublicodes(rawRules)
|
||||
|
||||
const getSimulationData = require('../../source/pages/Simulateurs/metadata-src')
|
||||
|
||||
const {
|
||||
ALGOLIA_APP_ID,
|
||||
ALGOLIA_ADMIN_KEY,
|
||||
ALGOLIA_INDEX_PREFIX = '',
|
||||
} = process.env
|
||||
|
||||
const client = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_ADMIN_KEY)
|
||||
|
||||
const rulesIndex = client.initIndex(`${ALGOLIA_INDEX_PREFIX}rules`)
|
||||
const simulateursIndex = client.initIndex(`${ALGOLIA_INDEX_PREFIX}simulateurs`)
|
||||
|
||||
const formatRulesToAlgolia = (rules) =>
|
||||
Object.entries(rules)
|
||||
.map(([n, rule]) => {
|
||||
if (!rule) return
|
||||
const path = n.split(' . ')
|
||||
const {
|
||||
title,
|
||||
rawNode: { icônes = '', description, acronyme, résumé },
|
||||
} = rule
|
||||
const ruleName = `${title} ${' ' + icônes}`.trim()
|
||||
const namespace = path.slice(0, -1)
|
||||
|
||||
return {
|
||||
objectID: n,
|
||||
path,
|
||||
ruleName,
|
||||
namespace,
|
||||
pathDepth: path.length,
|
||||
acronyme: acronyme,
|
||||
titre: title,
|
||||
icone: icônes,
|
||||
description: description || résumé,
|
||||
}
|
||||
})
|
||||
.filter(Boolean)
|
||||
|
||||
const formatSimulationDataToAlgolia = (simulations) =>
|
||||
Object.entries(simulations).map(([id, simulation]) => ({
|
||||
...simulation,
|
||||
objectID: id,
|
||||
title: simulation.title || simulation.shortName || simulation.meta.title,
|
||||
tooltip: simulation.tooltip || '',
|
||||
description: simulation.meta?.description,
|
||||
}))
|
||||
|
||||
;(async function () {
|
||||
try {
|
||||
console.log('Algolia update START')
|
||||
|
||||
console.log('Clearing: rules')
|
||||
await rulesIndex.clearObjects().wait()
|
||||
console.log('Configure index: rules')
|
||||
await rulesIndex
|
||||
.setSettings({
|
||||
// Parameters are documented on Algolia website https://www.algolia.com/doc/api-reference/api-parameters/
|
||||
minWordSizefor1Typo: 4,
|
||||
minWordSizefor2Typos: 8,
|
||||
hitsPerPage: 20,
|
||||
maxValuesPerFacet: 100,
|
||||
attributesToIndex: ['unordered(ruleName)', 'unordered(namespace)'],
|
||||
numericAttributesToIndex: null,
|
||||
attributesToRetrieve: null,
|
||||
unretrievableAttributes: null,
|
||||
optionalWords: null,
|
||||
attributesForFaceting: null,
|
||||
attributesToSnippet: null,
|
||||
attributesToHighlight: ['ruleName', 'namespace'],
|
||||
paginationLimitedTo: 1000,
|
||||
attributeForDistinct: null,
|
||||
exactOnSingleWordQuery: 'attribute',
|
||||
ranking: [
|
||||
'typo',
|
||||
'geo',
|
||||
'words',
|
||||
'filters',
|
||||
'proximity',
|
||||
'attribute',
|
||||
'exact',
|
||||
'custom',
|
||||
],
|
||||
customRanking: ['asc(pathDepth)'],
|
||||
separatorsToIndex: '',
|
||||
removeWordsIfNoResults: 'none',
|
||||
queryType: 'prefixLast',
|
||||
highlightPreTag: '<em>',
|
||||
highlightPostTag: '</em>',
|
||||
snippetEllipsisText: '',
|
||||
alternativesAsExact: ['ignorePlurals', 'singleWordSynonym'],
|
||||
})
|
||||
.wait()
|
||||
console.log('Uploading: rules')
|
||||
await rulesIndex.saveObjects(formatRulesToAlgolia(rules)).wait()
|
||||
|
||||
console.log('Clearing: simulateurs')
|
||||
await simulateursIndex.clearObjects().wait()
|
||||
console.log('Configure index: simulateurs')
|
||||
await simulateursIndex
|
||||
.setSettings({
|
||||
// Parameters are documented on Algolia website https://www.algolia.com/doc/api-reference/api-parameters/
|
||||
minWordSizefor1Typo: 4,
|
||||
minWordSizefor2Typos: 8,
|
||||
hitsPerPage: 20,
|
||||
maxValuesPerFacet: 100,
|
||||
attributesToIndex: [
|
||||
'unordered(title)',
|
||||
'unordered(tooltip)',
|
||||
'unordered(description)',
|
||||
],
|
||||
numericAttributesToIndex: null,
|
||||
attributesToRetrieve: null,
|
||||
unretrievableAttributes: null,
|
||||
optionalWords: null,
|
||||
attributesForFaceting: null,
|
||||
attributesToSnippet: null,
|
||||
attributesToHighlight: ['title'],
|
||||
paginationLimitedTo: 1000,
|
||||
attributeForDistinct: null,
|
||||
exactOnSingleWordQuery: 'attribute',
|
||||
ranking: [
|
||||
'typo',
|
||||
'geo',
|
||||
'words',
|
||||
'filters',
|
||||
'proximity',
|
||||
'attribute',
|
||||
'exact',
|
||||
'custom',
|
||||
],
|
||||
customRanking: null,
|
||||
separatorsToIndex: '',
|
||||
removeWordsIfNoResults: 'none',
|
||||
queryType: 'prefixLast',
|
||||
highlightPreTag: '<em>',
|
||||
highlightPostTag: '</em>',
|
||||
snippetEllipsisText: '',
|
||||
alternativesAsExact: ['ignorePlurals', 'singleWordSynonym'],
|
||||
})
|
||||
.wait()
|
||||
console.log('Updloading: simulateurs')
|
||||
await simulateursIndex
|
||||
.saveObjects(formatSimulationDataToAlgolia(getSimulationData()))
|
||||
.wait()
|
||||
|
||||
console.log('Algolia update DONE')
|
||||
} catch (e) {
|
||||
console.log(JSON.stringify(e, null, 2))
|
||||
}
|
||||
})()
|
|
@ -11,3 +11,18 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Classe pour permettre la présence de labels pour les lecteur d'ecran
|
||||
* Source: https://a11y-guidelines.orange.com/en/web/components-examples/accessible-hiding/
|
||||
* */
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap; /* added line */
|
||||
border: 0;
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
import SearchBar from 'Components/SearchBar'
|
||||
import { Trans } from 'react-i18next'
|
||||
import './RulesList.css'
|
||||
|
||||
export default function RulesList() {
|
||||
return (
|
||||
<div id="RulesList" className="ui__ container">
|
||||
<h1>
|
||||
<Trans>Explorez notre documentation</Trans>
|
||||
</h1>
|
||||
<SearchBar />
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
li.active {
|
||||
background: var(--color);
|
||||
color: var(--textColor);
|
||||
}
|
||||
|
||||
li.active a {
|
||||
color: var(--textColor);
|
||||
}
|
|
@ -1,184 +0,0 @@
|
|||
import React, { useEffect, useMemo, useState } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { DottedName } from 'modele-social'
|
||||
import RuleLink from './RuleLink'
|
||||
import './SearchBar.css'
|
||||
import { useEngine } from './utils/EngineContext'
|
||||
import { utils } from 'publicodes'
|
||||
|
||||
// TODO: We should use a normal import here
|
||||
// We use a dynamic import to work around a typing problem https://github.com/betagouv/mon-entreprise/pull/1616#issuecomment-858629506
|
||||
let worker: any
|
||||
;(async function () {
|
||||
const Worker = ((await import('./SearchBar.worker.js')) as any).default
|
||||
worker = new Worker()
|
||||
})()
|
||||
|
||||
type SearchBarProps = {
|
||||
showListByDefault?: boolean
|
||||
}
|
||||
|
||||
type SearchItem = {
|
||||
title: string
|
||||
dottedName: DottedName
|
||||
espace: Array<string>
|
||||
}
|
||||
|
||||
type Matches = Array<{
|
||||
key: string
|
||||
value: string
|
||||
indices: Array<[number, number]>
|
||||
}>
|
||||
|
||||
function highlightMatches(str: string, matches: Matches) {
|
||||
if (!matches?.length) {
|
||||
return str
|
||||
}
|
||||
const indices = matches[0].indices
|
||||
.sort(([a], [b]) => a - b)
|
||||
.map(([x, y]) => [x, y + 1])
|
||||
.reduce(
|
||||
(acc, value) =>
|
||||
acc[acc.length - 1][1] <= value[0] ? [...acc, value] : acc,
|
||||
[[0, 0]]
|
||||
)
|
||||
.flat()
|
||||
return [...indices, str.length].reduce(
|
||||
([highlight, prevIndice, acc], currentIndice, i) => {
|
||||
const currentStr = str.slice(prevIndice, currentIndice)
|
||||
return [
|
||||
!highlight,
|
||||
currentIndice,
|
||||
[
|
||||
...acc,
|
||||
<span
|
||||
style={highlight ? { fontWeight: 'bold' } : {}}
|
||||
className={highlight ? 'ui__ light-bg' : ''}
|
||||
key={i}
|
||||
>
|
||||
{currentStr}
|
||||
</span>,
|
||||
],
|
||||
] as [boolean, number, Array<React.ReactNode>]
|
||||
},
|
||||
[false, 0, []] as [boolean, number, Array<React.ReactNode>]
|
||||
)[2]
|
||||
}
|
||||
export default function SearchBar({
|
||||
showListByDefault = false,
|
||||
}: SearchBarProps) {
|
||||
const rules = useEngine().getParsedRules()
|
||||
const [input, setInput] = useState('')
|
||||
const [results, setResults] = useState<
|
||||
Array<{
|
||||
item: SearchItem
|
||||
matches: Matches
|
||||
}>
|
||||
>([])
|
||||
const { i18n } = useTranslation()
|
||||
|
||||
const searchIndex: Array<SearchItem> = useMemo(
|
||||
() =>
|
||||
Object.values(rules)
|
||||
.filter(utils.ruleWithDedicatedDocumentationPage)
|
||||
.map((rule) => ({
|
||||
title:
|
||||
rule.title +
|
||||
(rule.rawNode.acronyme ? ` (${rule.rawNode.acronyme})` : ''),
|
||||
dottedName: rule.dottedName,
|
||||
espace: rule.dottedName.split(' . ').reverse(),
|
||||
})),
|
||||
[rules]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
worker.postMessage({
|
||||
rules: searchIndex,
|
||||
})
|
||||
|
||||
worker.onmessage = ({ data: results }: any) => setResults(results)
|
||||
return () => {
|
||||
worker.onmessage = null
|
||||
}
|
||||
}, [searchIndex, setResults])
|
||||
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type="search"
|
||||
className="ui__"
|
||||
value={input}
|
||||
placeholder={i18n.t('Entrez des mots clefs ici')}
|
||||
onChange={(e) => {
|
||||
const input = e.target.value
|
||||
if (input.length > 0) worker.postMessage({ input })
|
||||
setInput(input)
|
||||
}}
|
||||
/>
|
||||
{!!input.length && !results.length ? (
|
||||
<p
|
||||
className="ui__ notice light-bg"
|
||||
css={`
|
||||
padding: 0.4rem;
|
||||
border-radius: 0.3rem;
|
||||
margin-top: 0.6rem;
|
||||
`}
|
||||
>
|
||||
<Trans i18nKey="noresults">
|
||||
Aucun résultat ne correspond à cette recherche
|
||||
</Trans>
|
||||
</p>
|
||||
) : (
|
||||
<ul
|
||||
css={`
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
`}
|
||||
>
|
||||
{(showListByDefault && !results.length && !input.length
|
||||
? searchIndex
|
||||
.filter((item) => item.espace.length === 2)
|
||||
.map((item) => ({ item, matches: [] }))
|
||||
: results
|
||||
)
|
||||
.slice(0, showListByDefault ? 100 : 6)
|
||||
.map(({ item, matches }) => (
|
||||
<li key={item.dottedName}>
|
||||
<RuleLink
|
||||
dottedName={item.dottedName}
|
||||
style={{
|
||||
width: '100%',
|
||||
textDecoration: 'none',
|
||||
lineHeight: '1.5rem',
|
||||
}}
|
||||
>
|
||||
<small>
|
||||
{item.espace
|
||||
.slice(1)
|
||||
.reverse()
|
||||
.map((name) => (
|
||||
<span key={name}>
|
||||
{highlightMatches(
|
||||
name,
|
||||
matches.filter(
|
||||
(m) => m.key === 'espace' && m.value === name
|
||||
)
|
||||
)}{' '}
|
||||
›{' '}
|
||||
</span>
|
||||
))}
|
||||
<br />
|
||||
</small>
|
||||
{highlightMatches(
|
||||
item.title,
|
||||
matches.filter((m) => m.key === 'title')
|
||||
)}
|
||||
</RuleLink>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
import Fuse from 'fuse.js'
|
||||
|
||||
const searchWeights = [
|
||||
{
|
||||
name: 'espace',
|
||||
weight: 0.6,
|
||||
},
|
||||
{
|
||||
name: 'title',
|
||||
weight: 0.4,
|
||||
},
|
||||
]
|
||||
|
||||
let fuse = null
|
||||
onmessage = function (event) {
|
||||
if (event.data.rules)
|
||||
fuse = new Fuse(event.data.rules, {
|
||||
keys: searchWeights,
|
||||
includeMatches: true,
|
||||
minMatchCharLength: 2,
|
||||
useExtendedSearch: true,
|
||||
distance: 50,
|
||||
threshold: 0.3,
|
||||
})
|
||||
|
||||
if (event.data.input) {
|
||||
let results = fuse
|
||||
.search(event.data.input + '|' + event.data.input.replace(/ /g, '|'))
|
||||
.slice()
|
||||
postMessage(results)
|
||||
}
|
||||
}
|
|
@ -1,14 +1,25 @@
|
|||
import { useEffect, useState } from 'react'
|
||||
import emoji from 'react-easy-emoji'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { useLocation } from 'react-router'
|
||||
import styled from 'styled-components'
|
||||
import Overlay from './Overlay'
|
||||
import SearchBar from './SearchBar'
|
||||
import SearchRulesAndSimulators from './search/SearchRulesAndSimulators'
|
||||
|
||||
type SearchButtonProps = {
|
||||
invisibleButton?: boolean
|
||||
}
|
||||
const SearchTriggerButton = styled.button`
|
||||
display: flex;
|
||||
border: 0px solid;
|
||||
border-color: rgb(41, 117, 209);
|
||||
padding: 0.6rem;
|
||||
font-size: 2rem;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
color: rgb(31, 42, 106);
|
||||
margin: auto;
|
||||
`
|
||||
|
||||
export default function SearchButton({ invisibleButton }: SearchButtonProps) {
|
||||
export default function SearchButton() {
|
||||
const { pathname } = useLocation()
|
||||
const pathnameRef = useRef(pathname)
|
||||
const [visible, setVisible] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -19,7 +30,9 @@ export default function SearchButton({ invisibleButton }: SearchButtonProps) {
|
|||
e.preventDefault()
|
||||
return false
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown)
|
||||
}
|
||||
|
@ -27,19 +40,46 @@ export default function SearchButton({ invisibleButton }: SearchButtonProps) {
|
|||
|
||||
const close = () => setVisible(false)
|
||||
|
||||
return visible ? (
|
||||
<Overlay onClose={close}>
|
||||
<h1>
|
||||
<Trans>Chercher dans la documentation</Trans>
|
||||
</h1>
|
||||
<SearchBar />
|
||||
</Overlay>
|
||||
) : invisibleButton ? null : (
|
||||
<button
|
||||
className="ui__ simple small button"
|
||||
onClick={() => setVisible(true)}
|
||||
>
|
||||
{emoji('🔍')} <Trans>Rechercher</Trans>
|
||||
</button>
|
||||
useEffect(() => {
|
||||
if (pathname !== pathnameRef.current) {
|
||||
pathnameRef.current = pathname
|
||||
close()
|
||||
}
|
||||
}, [pathname])
|
||||
|
||||
return (
|
||||
<>
|
||||
{visible && (
|
||||
<Overlay onClose={close}>
|
||||
<h1>
|
||||
<Trans>Que cherchez-vous ?</Trans>
|
||||
</h1>
|
||||
<SearchRulesAndSimulators />
|
||||
</Overlay>
|
||||
)}
|
||||
|
||||
<SearchTriggerButton
|
||||
onClick={() => setVisible(true)}
|
||||
id="search-display-button"
|
||||
>
|
||||
<svg
|
||||
style={{ height: '2rem' }}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="square"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={3}
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
||||
/>
|
||||
</svg>
|
||||
<div className="sr-only">
|
||||
<Trans>Rechercher</Trans>
|
||||
</div>
|
||||
</SearchTriggerButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ import Conversation, {
|
|||
} from 'Components/conversation/Conversation'
|
||||
import ExportSimulationBanner from 'Components/ExportSimulationBanner'
|
||||
import PageFeedback from 'Components/Feedback'
|
||||
import SearchButton from 'Components/SearchButton'
|
||||
import ShareSimulationBanner from 'Components/ShareSimulationBanner'
|
||||
import TargetSelection from 'Components/TargetSelection'
|
||||
import Progress from 'Components/ui/Progress'
|
||||
|
@ -42,7 +41,6 @@ export default function Simulation({
|
|||
<ExportRecover />
|
||||
{simulationBloc}
|
||||
|
||||
<SearchButton invisibleButton />
|
||||
{!firstStepCompleted && <TrackPage name="accueil" />}
|
||||
{firstStepCompleted && (
|
||||
<>
|
||||
|
|
|
@ -7,6 +7,7 @@ import { useContext } from 'react'
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import { Link } from 'react-router-dom'
|
||||
import NewsBanner from './NewsBanner'
|
||||
import SearchButton from 'Components/SearchButton'
|
||||
|
||||
export default function Header() {
|
||||
const sitePaths = useContext(SitePathsContext)
|
||||
|
@ -56,6 +57,7 @@ export default function Header() {
|
|||
>
|
||||
<img alt="logo urssaf" style={{ height: '100%' }} src={urssafSvg} />
|
||||
</a>
|
||||
{language === 'fr' && <SearchButton />}
|
||||
</div>
|
||||
<NewsBanner />
|
||||
</>
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import {
|
||||
connectInfiniteHits,
|
||||
connectStats,
|
||||
Highlight,
|
||||
} from 'react-instantsearch-dom'
|
||||
import { Names } from '../../../../modele-social/dist/names'
|
||||
|
||||
import RuleLink from '../RuleLink'
|
||||
import { Hit as AlgoliaHit } from 'react-instantsearch-core'
|
||||
|
||||
type Hit = AlgoliaHit<{ objectID: Names; namespace?: string }>
|
||||
|
||||
const Hit = (hit: Hit) => {
|
||||
return (
|
||||
<RuleLink dottedName={hit.objectID} className="hit-content">
|
||||
{hit.namespace && (
|
||||
<div className="hit-amespace ui__ notice">
|
||||
<Highlight hit={hit} attribute="namespace" separator=" > " />
|
||||
</div>
|
||||
)}
|
||||
<div className="hit-ruleName">
|
||||
<Highlight hit={hit} attribute="ruleName" />
|
||||
</div>
|
||||
</RuleLink>
|
||||
)
|
||||
}
|
||||
|
||||
const HideableTitle = connectStats(({ nbHits }) => {
|
||||
return nbHits === 0 ? (
|
||||
<></>
|
||||
) : (
|
||||
<h2>
|
||||
<Trans>Règles de calculs</Trans>
|
||||
</h2>
|
||||
)
|
||||
})
|
||||
|
||||
const Hits = connectInfiniteHits(({ hits, hasMore, refineNext }) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div className="hit-container">
|
||||
<div className="ais-InfiniteHits">
|
||||
<>
|
||||
<ol className="ais-InfiniteHits-list">
|
||||
{hits.map((hit) => (
|
||||
<li className="ais-InfiniteHits-item" key={hit.objectID}>
|
||||
<Hit {...hit} />
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
{hasMore && (
|
||||
<button className="ais-InfiniteHits-loadMore" onClick={refineNext}>
|
||||
{t('Charger plus de résultats')}
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
export const RulesInfiniteHits = () => {
|
||||
return (
|
||||
<>
|
||||
<HideableTitle />
|
||||
<Hits />
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
import '../ui/Card.css'
|
||||
import './SearchRulesAndSimulators.css'
|
||||
import 'instantsearch.css/themes/satellite.css'
|
||||
|
||||
import algoliasearch from 'algoliasearch/lite'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { InstantSearch, SearchBox } from 'react-instantsearch-dom'
|
||||
import { RulesInfiniteHits } from './RulesInfiniteHits'
|
||||
|
||||
const ALGOLIA_APP_ID = process.env.ALGOLIA_APP_ID || ''
|
||||
const ALGOLIA_SEARCH_KEY = process.env.ALGOLIA_SEARCH_KEY || ''
|
||||
const ALGOLIA_INDEX_PREFIX = process.env.ALGOLIA_INDEX_PREFIX || ''
|
||||
|
||||
const searchClient = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_SEARCH_KEY)
|
||||
|
||||
export default function SearchRules() {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<InstantSearch
|
||||
indexName={`${ALGOLIA_INDEX_PREFIX}rules`}
|
||||
searchClient={searchClient}
|
||||
>
|
||||
<SearchBox
|
||||
translations={{
|
||||
submitTitle: t('Valider votre recherche'),
|
||||
resetTitle: t('Réinitialiser votre recherche'),
|
||||
placeholder: t('Cherchez par mot-clef ou acronyme...'),
|
||||
}}
|
||||
/>
|
||||
<h2>
|
||||
<Trans>Règles de calculs</Trans>
|
||||
</h2>
|
||||
<RulesInfiniteHits />
|
||||
</InstantSearch>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
.ais-InfiniteHits {
|
||||
}
|
||||
|
||||
.ais-InfiniteHits-list > .ais-InfiniteHits-item {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ais-InfiniteHits-list > .ais-InfiniteHits-item > .hit-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0.5rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.ais-InfiniteHits-list > .ais-InfiniteHits-item > .hit-content > .hit-ruleName {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.ais-InfiniteHits-list
|
||||
> .ais-InfiniteHits-item
|
||||
> .hit-content
|
||||
> .ais-Highlight {
|
||||
display: block;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.ais-Hits-list {
|
||||
display: grid;
|
||||
grid-column-gap: 0.5rem;
|
||||
grid-row-gap: 0.7rem;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-auto-rows: 1fr;
|
||||
}
|
||||
|
||||
.ais-Hits-list > .box {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.simulator-hit-content {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.simulator-hit-content .ais-Highlight {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.simulator-icon {
|
||||
height: 2em;
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import '../ui/Card.css'
|
||||
import './SearchRulesAndSimulators.css'
|
||||
import 'instantsearch.css/themes/satellite.css'
|
||||
|
||||
import algoliasearch from 'algoliasearch/lite'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
Configure,
|
||||
Index,
|
||||
InstantSearch,
|
||||
SearchBox,
|
||||
} from 'react-instantsearch-dom'
|
||||
import { SimulatorHits } from './SimulatorHits'
|
||||
import { RulesInfiniteHits } from './RulesInfiniteHits'
|
||||
|
||||
const ALGOLIA_APP_ID = process.env.ALGOLIA_APP_ID || ''
|
||||
const ALGOLIA_SEARCH_KEY = process.env.ALGOLIA_SEARCH_KEY || ''
|
||||
const ALGOLIA_INDEX_PREFIX = process.env.ALGOLIA_INDEX_PREFIX || ''
|
||||
|
||||
const searchClient = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_SEARCH_KEY)
|
||||
|
||||
export default function SearchRulesAndSimulators() {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<InstantSearch
|
||||
indexName={`${ALGOLIA_INDEX_PREFIX}rules`}
|
||||
searchClient={searchClient}
|
||||
>
|
||||
<SearchBox
|
||||
translations={{
|
||||
submitTitle: t('Valider votre recherche'),
|
||||
resetTitle: t('Réinitialiser votre recherche'),
|
||||
placeholder: t('Cherchez par mot-clef ou accronyme...'),
|
||||
}}
|
||||
/>
|
||||
|
||||
<Index indexName={`${ALGOLIA_INDEX_PREFIX}simulateurs`}>
|
||||
<Configure hitsPerPage={6} />
|
||||
<SimulatorHits />
|
||||
</Index>
|
||||
|
||||
<Index indexName={`${ALGOLIA_INDEX_PREFIX}rules`}>
|
||||
<RulesInfiniteHits />
|
||||
</Index>
|
||||
</InstantSearch>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
import Emoji from 'Components/utils/Emoji'
|
||||
import { SitePathsContext } from 'Components/utils/SitePathsContext'
|
||||
import { path } from 'ramda'
|
||||
import { useContext } from 'react'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { connectHits, Highlight } from 'react-instantsearch-dom'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
type AlgoliaSimulatorHit = {
|
||||
objectID: string
|
||||
icône: string
|
||||
title: string
|
||||
pathId: string
|
||||
}
|
||||
|
||||
type SimulatorHitProps = {
|
||||
hit: AlgoliaSimulatorHit
|
||||
path?: string
|
||||
}
|
||||
|
||||
const SimulatorHit = ({ hit, path = '' }: SimulatorHitProps) => (
|
||||
<Link
|
||||
className="simulator-hit-content ui__ interactive card box"
|
||||
to={path || ''}
|
||||
>
|
||||
<div className="ui__ box-icon">
|
||||
{hit.icône && <Emoji emoji={hit.icône} />}{' '}
|
||||
</div>
|
||||
<Highlight hit={hit} attribute="title" />
|
||||
</Link>
|
||||
)
|
||||
|
||||
type SimulatorHitsProps = {
|
||||
hits: Array<AlgoliaSimulatorHit>
|
||||
}
|
||||
|
||||
export const SimulatorHits = connectHits(({ hits }: SimulatorHitsProps) => {
|
||||
const sitePaths = useContext(SitePathsContext)
|
||||
return (
|
||||
<>
|
||||
{hits.length > 0 && (
|
||||
<h2>
|
||||
<Trans>Simulateurs</Trans>
|
||||
</h2>
|
||||
)}
|
||||
<div className="ais-Hits-list">
|
||||
{hits.map((hit) => (
|
||||
<SimulatorHit
|
||||
key={hit.objectID}
|
||||
hit={hit}
|
||||
path={path(hit.pathId.split('.'), sitePaths)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
})
|
|
@ -0,0 +1,7 @@
|
|||
type Screen = 'phone' | 'tablet' | 'desktop'
|
||||
|
||||
export const breakpoints: Record<Screen, string> = {
|
||||
phone: '0px',
|
||||
tablet: '850px',
|
||||
desktop: '1200px',
|
||||
}
|
|
@ -139,6 +139,7 @@ Professions libérales: By job
|
|||
Protection sociale: Social security
|
||||
Précédent: Previous
|
||||
Prévisualisation: Preview
|
||||
Que cherchez-vous ?: What are you looking for?
|
||||
Quel module ?: What module?
|
||||
Quelle couleur ?: What color?
|
||||
Quelques exemples de salaires: Some salary exemples
|
||||
|
@ -161,6 +162,7 @@ Revenu du dirigeant par statut: Executive income by status
|
|||
"Revenu net avec chômage partiel :": "Net income with short-time work :"
|
||||
Revenu net mensuel: Monthly net income
|
||||
Revenus étranger: Foreign income
|
||||
Règles de calculs: Rules
|
||||
Réductions: Discounts
|
||||
Rémunération du dirigeant: Director's remuneration
|
||||
Répartition du chiffre d'affaires: Breakdown of turnover
|
||||
|
@ -176,6 +178,7 @@ Salariés et employeurs: Employees and employers
|
|||
Sans responsabilité limitée: Without limited liability
|
||||
Si: If
|
||||
Simulateur de salaire: Employee salary simulation
|
||||
Simulateurs: Simulations
|
||||
Simulations personnalisées: Customized simulations
|
||||
Sinon: Else
|
||||
Situation personnelle: Personal situation
|
||||
|
|
|
@ -62,6 +62,7 @@ Prochaines questions: Prochaines questions
|
|||
Professions libérales: Professions libérales
|
||||
Précédent: Précédent
|
||||
Prévisualisation: Prévisualisation
|
||||
Que cherchez-vous ?: Que cherchez-vous ?
|
||||
Quelques intégrations: Quelques intégrations
|
||||
Rechercher: Rechercher
|
||||
Ressources utiles: Ressources utiles
|
||||
|
@ -70,11 +71,13 @@ Retour à la création: Retour à la création
|
|||
Retour à mon activité: Retour à mon activité
|
||||
Revenu du dirigeant par statut: Revenu du dirigeant par statut
|
||||
Revenus étranger: Revenus étranger
|
||||
Règles de calculs: Règles de calculs
|
||||
Répartition du chiffre d'affaires: Répartition du chiffre d'affaires
|
||||
S'inscrire: S'inscrire
|
||||
Salaire: Salaire
|
||||
Salaire net: Salaire net
|
||||
Salariés et employeurs: Salariés et employeurs
|
||||
Simulateurs: Simulateurs
|
||||
Situation personnelle: Situation personnelle
|
||||
Suivant: Suivant
|
||||
Total des retenues: Total des retenues
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import SearchBar from 'Components/SearchBar'
|
||||
import SearchButton from 'Components/SearchButton'
|
||||
import SearchRules from 'Components/search/SearchRules'
|
||||
import { FromBottom } from 'Components/ui/animate'
|
||||
import { ThemeColorsProvider } from 'Components/utils/colors'
|
||||
import { useEngine } from 'Components/utils/EngineContext'
|
||||
|
@ -12,6 +11,8 @@ import { useSelector } from 'react-redux'
|
|||
import { Redirect, useHistory, useLocation } from 'react-router-dom'
|
||||
import { RootState } from 'Reducers/rootReducer'
|
||||
import { TrackPage } from '../ATInternetTracking'
|
||||
import rules, { DottedName } from 'modele-social'
|
||||
import RuleLink from '../components/RuleLink'
|
||||
|
||||
export default function RulePage() {
|
||||
const currentSimulation = useSelector(
|
||||
|
@ -32,9 +33,15 @@ export default function RulePage() {
|
|||
if (pathname === '/documentation') {
|
||||
return <DocumentationLanding />
|
||||
}
|
||||
|
||||
if (pathname === '/documentation/dev') {
|
||||
return <DocumentationRulesList />
|
||||
}
|
||||
|
||||
if (!documentationSitePaths[pathname]) {
|
||||
return <Redirect to="/404" />
|
||||
}
|
||||
|
||||
return (
|
||||
<FromBottom>
|
||||
<TrackPage
|
||||
|
@ -51,7 +58,6 @@ export default function RulePage() {
|
|||
`}
|
||||
>
|
||||
{currentSimulation ? <BackToSimulation /> : <span />}
|
||||
<SearchButton key={pathname} />
|
||||
</div>
|
||||
<Documentation
|
||||
language={i18n.language as 'fr' | 'en'}
|
||||
|
@ -88,7 +94,21 @@ function DocumentationLanding() {
|
|||
<Trans i18nKey="page.documentation.title">Documentation</Trans>
|
||||
</h1>
|
||||
<p>Explorez toutes les règles de la documentation</p>
|
||||
<SearchBar showListByDefault={true} />
|
||||
<SearchRules />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function DocumentationRulesList() {
|
||||
const ruleEntries = Object.keys(rules) as DottedName[]
|
||||
return (
|
||||
<>
|
||||
<h1>Liste des règles</h1>
|
||||
{ruleEntries.map((name) => (
|
||||
<RuleLink dottedName={name} key={name}>
|
||||
{name}
|
||||
</RuleLink>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,567 @@
|
|||
/**
|
||||
* Contient l'intégralité des données concernant les différents simulateurs
|
||||
* sans dépendance qui compliquerait leur import dans le script de mise à jour
|
||||
* des données pour Algolia.
|
||||
*/
|
||||
module.exports = ({ t = (_, text) => text } = {}) => {
|
||||
return {
|
||||
salarié: {
|
||||
tracking: 'salarie',
|
||||
icône: '🤝',
|
||||
title: t(
|
||||
'pages.simulateurs.salarié.title',
|
||||
'Simulateur de revenus pour salarié'
|
||||
),
|
||||
iframePath: 'simulateur-embauche',
|
||||
meta: {
|
||||
description: t(
|
||||
'pages.simulateurs.salarié.meta.description',
|
||||
"Calcul du salaire net, net après impôt et coût total employeur. Beaucoup d'options disponibles (cadre, stage, apprentissage, heures supplémentaires, etc.)"
|
||||
),
|
||||
ogDescription: t(
|
||||
'pages.simulateurs.salarié.meta.ogDescription',
|
||||
"En tant que salarié, calculez immédiatement votre revenu net après impôt à partir du brut mensuel ou annuel. En tant qu'employé, estimez le coût total d'une embauche à partir du brut. Ce simulateur est développé avec les experts de l'Urssaf, et il adapte les calculs à votre situation (statut cadre, stage, apprentissage, heures supplémentaire, titre-restaurants, mutuelle, temps partiel, convention collective, etc.)"
|
||||
),
|
||||
ogTitle: t(
|
||||
'pages.simulateurs.salarié.meta.ogTitle',
|
||||
'Salaire brut, net, net après impôt, coût total : le simulateur ultime pour salariés et employeurs'
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.salarié.meta.titre',
|
||||
'Salaire brut / net : le convertisseur Urssaf'
|
||||
),
|
||||
},
|
||||
pathId: 'simulateurs.salarié',
|
||||
shortName: t('pages.simulateurs.salarié.shortname', 'Salarié'),
|
||||
nextSteps: ['chômage-partiel', 'aides-embauche'],
|
||||
},
|
||||
'entreprise-individuelle': {
|
||||
tracking: {
|
||||
chapter2: 'statut_entreprise',
|
||||
chapter3: 'EI',
|
||||
},
|
||||
iframePath: 'simulateur-EI',
|
||||
icône: '🚶♀️',
|
||||
meta: {
|
||||
description: t(
|
||||
'pages.simulateurs.ei.meta.description',
|
||||
"Calcul du revenu à partir du chiffre d'affaires, après déduction des cotisations et des impôts"
|
||||
),
|
||||
ogDescription: t(
|
||||
'pages.simulateurs.ei.meta.ogDescription',
|
||||
"Grâce au simulateur de revenu pour entreprise individuelle développé par l'Urssaf, vous pourrez estimer le montant de vos revenus en fonction de votre chiffre d'affaires mensuel ou annuel pour mieux gérer votre trésorerie. Ou dans le sens inverse : savoir quel montant facturer pour atteindre un certain revenu."
|
||||
),
|
||||
ogTitle: t(
|
||||
'pages.simulateurs.ei.meta.ogTitle',
|
||||
'Entreprise individuelle (EI) : calculez rapidement votre revenu net à partir du CA et vice-versa'
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.ei.meta.titre',
|
||||
'Entreprise individuelle (EI) : simulateur de revenus'
|
||||
),
|
||||
},
|
||||
pathId: 'simulateurs.entreprise-individuelle',
|
||||
shortName: t('pages.simulateurs.ei.shortname', 'EI'),
|
||||
title: t(
|
||||
'pages.simulateurs.ei.title',
|
||||
'Simulateur pour entreprise individuelle (EI)'
|
||||
),
|
||||
|
||||
nextSteps: ['comparaison-statuts'],
|
||||
},
|
||||
eirl: {
|
||||
tracking: {
|
||||
chapter2: 'statut_entreprise',
|
||||
chapter3: 'EIRL',
|
||||
},
|
||||
icône: '🚶',
|
||||
iframePath: 'simulateur-EIRL',
|
||||
meta: {
|
||||
description: t(
|
||||
'pages.simulateurs.eirl.meta.description',
|
||||
"Calcul du revenu à partir du chiffre d'affaires, après déduction des cotisations et des impôts"
|
||||
),
|
||||
ogDescription: t(
|
||||
'pages.simulateurs.eirl.meta.ogDescription',
|
||||
"Grâce au simulateur de revenu pour EIRL développé par l'Urssaf, vous pourrez estimer le montant de vos revenus en fonction de votre chiffre d'affaires mensuel ou annuel pour mieux gérer votre trésorerie. Ou dans le sens inverse : savoir quel montant facturer pour atteindre un certain revenu."
|
||||
),
|
||||
ogTitle: t(
|
||||
'pages.simulateurs.eirl.meta.ogTitle',
|
||||
"Dirigeant d'EIRL : calculez rapidement votre revenu net à partir du CA et vice-versa"
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.eirl.meta.titre',
|
||||
'EIRL : simulateur de revenus pour dirigeant'
|
||||
),
|
||||
},
|
||||
pathId: 'simulateurs.eirl',
|
||||
shortName: t('pages.simulateurs.eirl.shortname', 'EIRL'),
|
||||
title: t('pages.simulateurs.eirl.title', "Simulateur d'EIRL"),
|
||||
|
||||
nextSteps: ['comparaison-statuts'],
|
||||
},
|
||||
sasu: {
|
||||
tracking: {
|
||||
chapter2: 'statut_entreprise',
|
||||
chapter3: 'SASU',
|
||||
},
|
||||
icône: '📘',
|
||||
iframePath: 'simulateur-assimilesalarie',
|
||||
meta: {
|
||||
description: t(
|
||||
'pages.simulateurs.sasu.meta.description',
|
||||
'Calcul du salaire net à partir du total alloué à la rémunération et inversement'
|
||||
),
|
||||
ogDescription: t(
|
||||
'pages.simulateurs.sasu.meta.ogDescription',
|
||||
'En tant que dirigeant assimilé-salarié, calculez immédiatement votre revenu net après impôt à partir du total alloué à votre rémunération.'
|
||||
),
|
||||
ogTitle: t(
|
||||
'pages.simulateurs.sasu.meta.ogTitle',
|
||||
'Rémunération du dirigeant de SASU : un simulateur pour connaître votre salaire net'
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.sasu.meta.titre',
|
||||
'SASU : simulateur de revenus pour dirigeant'
|
||||
),
|
||||
},
|
||||
pathId: 'simulateurs.sasu',
|
||||
shortName: t('pages.simulateurs.sasu.shortname', 'SASU'),
|
||||
title: t('pages.simulateurs.sasu.title', 'Simulateur de SASU'),
|
||||
nextSteps: ['is', 'comparaison-statuts'],
|
||||
},
|
||||
eurl: {
|
||||
tracking: {
|
||||
chapter2: 'statut_entreprise',
|
||||
chapter3: 'EURL',
|
||||
},
|
||||
icône: '📕',
|
||||
iframePath: 'simulateur-eurl',
|
||||
meta: {
|
||||
description: t(
|
||||
'pages.simulateurs.eurl.meta.description',
|
||||
'Calcul du salaire net à partir du total alloué à la rémunération et inversement'
|
||||
),
|
||||
ogDescription: t(
|
||||
'pages.simulateurs.eurl.meta.ogDescription',
|
||||
'En tant que dirigeant assimilé-salarié, calculez immédiatement votre revenu net après impôt à partir du total alloué à votre rémunération.'
|
||||
),
|
||||
ogTitle: t(
|
||||
'pages.simulateurs.eurl.meta.ogTitle',
|
||||
"Rémunération du dirigeant d'EURL : un simulateur pour connaître votre salaire net"
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.eurl.meta.titre',
|
||||
'EURL : simulateur de revenus pour dirigeant'
|
||||
),
|
||||
},
|
||||
pathId: 'simulateurs.eurl',
|
||||
shortName: t('pages.simulateurs.sasu.shortname', 'EURL'),
|
||||
title: t('pages.simulateurs.sasu.title', "Simulateur d'EURL"),
|
||||
nextSteps: ['is', 'comparaison-statuts'],
|
||||
},
|
||||
'auto-entrepreneur': {
|
||||
tracking: 'auto_entrepreneur',
|
||||
icône: '🚶♂️',
|
||||
iframePath: 'simulateur-autoentrepreneur',
|
||||
meta: {
|
||||
description: t(
|
||||
'pages.simulateurs.auto-entrepreneur.meta.description',
|
||||
"Calcul du revenu à partir du chiffre d'affaires, après déduction des cotisations et des impôts"
|
||||
),
|
||||
ogDescription: t(
|
||||
'pages.simulateurs.auto-entrepreneur.meta.ogDescription',
|
||||
"Grâce au simulateur de revenu auto-entrepreneur développé par l'Urssaf, vous pourrez estimer le montant de vos revenus en fonction de votre chiffre d'affaires mensuel ou annuel pour mieux gérer votre trésorerie. Ou dans le sens inverse : savoir quel montant facturer pour atteindre un certain revenu."
|
||||
),
|
||||
ogTitle: t(
|
||||
'pages.simulateurs.auto-entrepreneur.meta.ogTitle',
|
||||
'Auto-entrepreneur : calculez rapidement votre revenu net à partir du CA et vice-versa'
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.auto-entrepreneur.meta.titre',
|
||||
'Auto-entrepreneurs : simulateur de revenus'
|
||||
),
|
||||
},
|
||||
pathId: 'simulateurs.auto-entrepreneur',
|
||||
shortName: t(
|
||||
'pages.simulateurs.auto-entrepreneur.shortname',
|
||||
'Auto-entrepreneur'
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.auto-entrepreneur.title',
|
||||
'Simulateur de revenus auto-entrepreneur'
|
||||
),
|
||||
nextSteps: ['indépendant', 'comparaison-statuts'],
|
||||
},
|
||||
indépendant: {
|
||||
tracking: 'independant',
|
||||
icône: '🏃',
|
||||
iframePath: 'simulateur-independant',
|
||||
pathId: 'simulateurs.indépendant',
|
||||
shortName: t('pages.simulateurs.indépendant.shortname', 'Indépendant'),
|
||||
title: t(
|
||||
'pages.simulateurs.indépendant.title',
|
||||
'Simulateur de revenus pour indépendant'
|
||||
),
|
||||
meta: {
|
||||
title: t(
|
||||
'pages.simulateurs.indépendant.meta.title',
|
||||
'Indépendant : simulateur de revenus'
|
||||
),
|
||||
description: t(
|
||||
'pages.simulateurs.indépendant.meta.description',
|
||||
"Calcul du revenu net après impôt et des cotisations à partir du chiffre d'affaires et inversement"
|
||||
),
|
||||
},
|
||||
nextSteps: ['comparaison-statuts', 'is'],
|
||||
},
|
||||
|
||||
'artiste-auteur': {
|
||||
icône: '👩🎨',
|
||||
tracking: 'artiste-auteur',
|
||||
iframePath: 'simulateur-artiste-auteur',
|
||||
meta: {
|
||||
title: t(
|
||||
'pages.simulateurs.artiste-auteur.meta.title',
|
||||
'Artiste-auteur: calcul des cotisations Urssaf'
|
||||
),
|
||||
description: t(
|
||||
'pages.simulateurs.artiste-auteur.meta.description',
|
||||
"Estimez les cotisations sociales sur les droits d'auteur et sur le revenu BNC"
|
||||
),
|
||||
ogTitle: 'Artiste-auteur : estimez vos cotisations Urssaf',
|
||||
ogDescription:
|
||||
"Renseignez vos revenus (droits d'auteur et bnc) et découvrez immédiatement le montant des cotisations que vous aurez à payer sur l'année.",
|
||||
},
|
||||
pathId: 'simulateurs.artiste-auteur',
|
||||
title: t(
|
||||
'pages.simulateurs.artiste-auteur.title',
|
||||
'Estimer mes cotisations d’artiste-auteur'
|
||||
),
|
||||
shortName: t(
|
||||
'pages.simulateurs.artiste-auteur.shortname',
|
||||
'Artiste-auteur'
|
||||
),
|
||||
},
|
||||
'chômage-partiel': {
|
||||
tracking: 'chomage_partiel',
|
||||
pathId: 'simulateurs.chômage-partiel',
|
||||
icône: '😷',
|
||||
iframePath: 'simulateur-chomage-partiel',
|
||||
meta: {
|
||||
description: t(
|
||||
'pages.simulateurs.chômage-partiel.meta.description',
|
||||
"Calcul du revenu net pour l'employé et du reste à charge pour l'employeur après remboursement de l'Etat, en prenant en compte toutes les cotisations sociales."
|
||||
),
|
||||
ogDescription: t(
|
||||
'pages.simulateurs.chômage-partiel.meta.ogDescription',
|
||||
"Accédez à une première estimation en saisissant à partir d'un salaire brut. Vous pourrez ensuite personaliser votre situation (temps partiel, convention, etc). Prends en compte la totalité des cotisations, y compris celles spécifiques à l'indemnité (CSG et CRDS)."
|
||||
),
|
||||
ogTitle: t(
|
||||
'pages.simulateurs.chômage-partiel.meta.ogTitle',
|
||||
"Simulateur chômage partiel : découvrez l'impact sur le revenu net salarié et le coût total employeur."
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.chômage-partiel.meta.titre',
|
||||
"Calcul de l'indemnité chômage partiel : le simulateur Urssaf"
|
||||
),
|
||||
},
|
||||
shortName: t(
|
||||
'pages.simulateurs.chômage-partiel.shortname',
|
||||
'Chômage partiel'
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.chômage-partiel.title',
|
||||
'Covid-19 : Simulateur de chômage partiel'
|
||||
),
|
||||
|
||||
nextSteps: ['salarié', 'aides-embauche'],
|
||||
},
|
||||
'comparaison-statuts': {
|
||||
tracking: 'comparaison_statut',
|
||||
icône: '📊',
|
||||
pathId: 'simulateurs.comparaison',
|
||||
title: t(
|
||||
'pages.simulateurs.comparaison.title',
|
||||
'Indépendant, assimilé salarié ou auto-entrepreneur : quel régime choisir ?'
|
||||
),
|
||||
meta: {
|
||||
description: t(
|
||||
'pages.simulateurs.comparaison.meta.description',
|
||||
'Auto-entrepreneur, indépendant ou dirigeant de SASU ? Avec ce comparatif, trouvez le régime qui vous correspond le mieux'
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.comparaison.meta.title',
|
||||
"Création d'entreprise : le comparatif des régimes sociaux"
|
||||
),
|
||||
},
|
||||
shortName: t(
|
||||
'pages.simulateurs.comparaison.shortname',
|
||||
'Comparaison des statuts'
|
||||
),
|
||||
},
|
||||
'économie-collaborative': {
|
||||
tracking: 'economie_collaborative',
|
||||
meta: {
|
||||
title: t(
|
||||
'pages.économie-collaborative.meta.title',
|
||||
'Déclaration des revenus des plateforme en ligne : guide intéractif'
|
||||
),
|
||||
description: t(
|
||||
'pages.économie-collaborative.meta.description',
|
||||
'Airbnb, Drivy, Blablacar, Leboncoin... Découvrez comment être en règle dans vos déclarations'
|
||||
),
|
||||
},
|
||||
icône: '🙋',
|
||||
pathId: 'simulateurs.économieCollaborative.index',
|
||||
shortName: t(
|
||||
'pages.économie-collaborative.shortname',
|
||||
'Guide économie collaborative'
|
||||
),
|
||||
},
|
||||
'aide-déclaration-indépendant': {
|
||||
tracking: {
|
||||
chapter1: 'gerer',
|
||||
chapter2: 'aide_declaration_independant',
|
||||
},
|
||||
icône: '✍️',
|
||||
meta: {
|
||||
description: t(
|
||||
'pages.gérer.aide-déclaration-indépendant.meta.description',
|
||||
'Calculer facilement les montants des charges sociales à reporter dans votre déclaration de revenu 2020.'
|
||||
),
|
||||
title: t(
|
||||
'pages.gérer.aide-déclaration-indépendant.meta.title',
|
||||
'Déclaration de revenus indépendant : calcul du montant des cotisations'
|
||||
),
|
||||
},
|
||||
pathId: 'simulateurs.déclarationIndépendant',
|
||||
shortName: t(
|
||||
'pages.gérer.aide-déclaration-indépendant.shortname',
|
||||
'Aide à la déclaration de revenu'
|
||||
),
|
||||
title: t(
|
||||
'pages.gérer.aide-déclaration-indépendant.title',
|
||||
"Aide à la déclaration de revenus au titre de l'année 2020"
|
||||
),
|
||||
},
|
||||
'demande-mobilité': {
|
||||
tracking: {
|
||||
chapter1: 'gerer',
|
||||
chapter2: 'demande_mobilite',
|
||||
},
|
||||
icône: '🧳',
|
||||
meta: {
|
||||
title: t(
|
||||
'pages.gérer.demande-mobilité.meta.title',
|
||||
'Travailleur indépendant : demande de mobilité en Europe'
|
||||
),
|
||||
description: t(
|
||||
'pages.gérer.demande-mobilité.meta.description',
|
||||
"Formulaire interactif à compléter pour les indépendants souhaitant exercer leur activité dans d'autres pays d'Europe"
|
||||
),
|
||||
},
|
||||
pathId: 'gérer.formulaireMobilité',
|
||||
shortName: t(
|
||||
'pages.gérer.demande-mobilité.shortname',
|
||||
'Demande de mobilité internationale'
|
||||
),
|
||||
private: true,
|
||||
iframePath: 'demande-mobilite',
|
||||
},
|
||||
médecin: {
|
||||
tracking: {
|
||||
chapter2: 'profession_liberale',
|
||||
chapter3: 'medecin',
|
||||
},
|
||||
icône: '⚕️',
|
||||
iframePath: 'médecin',
|
||||
pathId: 'simulateurs.profession-libérale.médecin',
|
||||
shortName: t('pages.simulateurs.médecin.shortname', 'Médecin'),
|
||||
title: t(
|
||||
'pages.simulateurs.médecin.title',
|
||||
'Simulateur de revenus pour médecin en libéral'
|
||||
),
|
||||
},
|
||||
'chirurgien-dentiste': {
|
||||
icône: '🦷',
|
||||
tracking: {
|
||||
chapter2: 'profession_liberale',
|
||||
chapter3: 'chirurgien_dentiste',
|
||||
},
|
||||
iframePath: 'chirurgien-dentiste',
|
||||
pathId: 'simulateurs.profession-libérale.chirurgien-dentiste',
|
||||
shortName: t(
|
||||
'pages.simulateurs.chirurgien-dentiste.shortname',
|
||||
'Chirurgien-dentiste'
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.chirurgien-dentiste.title',
|
||||
'Simulateur de revenus pour chirurgien-dentiste en libéral'
|
||||
),
|
||||
},
|
||||
'sage-femme': {
|
||||
icône: '👶',
|
||||
tracking: {
|
||||
chapter2: 'profession_liberale',
|
||||
chapter3: 'sage_femme',
|
||||
},
|
||||
iframePath: 'sage-femme',
|
||||
pathId: 'simulateurs.profession-libérale.sage-femme',
|
||||
shortName: t('pages.simulateurs.sage-femme.shortname', 'Sage-femme'),
|
||||
title: t(
|
||||
'pages.simulateurs.sage-femme.title',
|
||||
'Simulateur de revenus pour sage-femme en libéral'
|
||||
),
|
||||
},
|
||||
'auxiliaire-médical': {
|
||||
tracking: {
|
||||
chapter2: 'profession_liberale',
|
||||
chapter3: 'auxiliaire_medical',
|
||||
},
|
||||
tooltip: t(
|
||||
'pages.simulateurs.auxiliaire.tooltip',
|
||||
'Infirmiers, masseurs-kinésithérapeutes, pédicures-podologues, orthophonistes et orthoptistes'
|
||||
),
|
||||
icône: '🩹',
|
||||
iframePath: 'auxiliaire-medical',
|
||||
pathId: 'simulateurs.profession-libérale.auxiliaire',
|
||||
shortName: t('pages.simulateurs.auxiliaire.shortname', 'Auxiliaire méd.'),
|
||||
title: t(
|
||||
'pages.simulateurs.auxiliaire.title',
|
||||
'Simulateur de revenus pour auxiliaire médical en libéral'
|
||||
),
|
||||
},
|
||||
avocat: {
|
||||
tracking: {
|
||||
chapter2: 'profession_liberale',
|
||||
chapter3: 'avocat',
|
||||
},
|
||||
icône: '⚖', // j'ai hesité avec 🥑 mais pas envie de me prendre un procès
|
||||
iframePath: 'avocat',
|
||||
pathId: 'simulateurs.profession-libérale.avocat',
|
||||
shortName: t('pages.simulateurs.avocat.shortname', 'Avocat'),
|
||||
title: t(
|
||||
'pages.simulateurs.avocat.title',
|
||||
'Simulateur de revenus pour avocat en libéral'
|
||||
),
|
||||
},
|
||||
'expert-comptable': {
|
||||
tracking: {
|
||||
chapter2: 'profession_liberale',
|
||||
chapter3: 'expert_comptable',
|
||||
},
|
||||
icône: '🧮',
|
||||
iframePath: 'expert-comptable',
|
||||
pathId: 'simulateurs.profession-libérale.expert-comptable',
|
||||
shortName: t(
|
||||
'pages.simulateurs.expert-comptable.shortname',
|
||||
'Expert-Comptable'
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.expert-comptable.title',
|
||||
'Simulateur de revenus pour expert comptable et commissaire aux comptes en libéral'
|
||||
),
|
||||
},
|
||||
'profession-libérale': {
|
||||
tracking: {
|
||||
chapter2: 'profession_liberale',
|
||||
},
|
||||
icône: '💻',
|
||||
meta: {
|
||||
title: t(
|
||||
'pages.simulateurs.profession-libérale.meta.title',
|
||||
'Professions libérale : le simulateur Urssaf'
|
||||
),
|
||||
description: t(
|
||||
'pages.simulateurs.profession-libérale.meta.description',
|
||||
"Calcul du revenu net pour les indépendants en libéral à l'impôt sur le revenu (IR, BNC)"
|
||||
),
|
||||
},
|
||||
iframePath: 'profession-liberale',
|
||||
pathId: 'simulateurs.profession-libérale.index',
|
||||
shortName: t(
|
||||
'pages.simulateurs.profession-libérale.shortname',
|
||||
'Profession libérale'
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.profession-libérale.title',
|
||||
'Simulateur de revenus pour profession libérale'
|
||||
),
|
||||
},
|
||||
pamc: {
|
||||
private: true,
|
||||
iframePath: 'pamc',
|
||||
tracking: {},
|
||||
title: t(
|
||||
'pages.simulateurs.pamc.title',
|
||||
|
||||
'PAMC : simulateurs de cotisations et de revenu'
|
||||
),
|
||||
pathId: 'simulateurs.pamc',
|
||||
icône: '🏥',
|
||||
meta: {
|
||||
title: t(
|
||||
'pages.simulateurs.pamc.meta.title',
|
||||
'Simulateurs régime PAMC'
|
||||
),
|
||||
description: t(
|
||||
'pages.simulateurs.pamc.meta.description',
|
||||
'Calcul du revenu net pour les professions libérales du régime PAMC (médecin, chirurgien-dentiste, sage-femme et auxiliaire médical)'
|
||||
),
|
||||
},
|
||||
shortName: t('pages.simulateurs.pamc.shortname', 'PAMC'),
|
||||
},
|
||||
'aides-embauche': {
|
||||
icône: '🎁',
|
||||
tracking: 'aides_embauche',
|
||||
meta: {
|
||||
title: t(
|
||||
'pages.simulateurs.aides-embauche.meta.title',
|
||||
'Aides à l’embauche'
|
||||
),
|
||||
description: t(
|
||||
'pages.simulateurs.aides-embauche.meta.description',
|
||||
'Découvrez les principales aides à l’embauche et estimez leur montant en répondant à quelques questions.'
|
||||
),
|
||||
color: '#11965f',
|
||||
},
|
||||
pathId: 'simulateurs.aide-embauche',
|
||||
iframePath: 'aides-embauche',
|
||||
shortName: t(
|
||||
'pages.simulateurs.aides-embauche.meta.title',
|
||||
'Aides à l’embauche'
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.aides-embauche.meta.title',
|
||||
'Aides à l’embauche'
|
||||
),
|
||||
description: t(
|
||||
'pages.simulateurs.aides-embauche.introduction',
|
||||
"Les employeurs peuvent bénéficier d'une aide financière pour l'embauche de certains publics prioritaires. Découvrez les dispositifs existants et estimez le montant de l'aide en répondant aux questions."
|
||||
),
|
||||
nextSteps: ['salarié'],
|
||||
},
|
||||
is: {
|
||||
icône: '🗓',
|
||||
tracking: 'impot-societe',
|
||||
pathId: 'simulateurs.is',
|
||||
iframePath: 'impot-societe',
|
||||
meta: {
|
||||
title: t('pages.simulateurs.is.meta.title', 'Impôt sur les sociétés'),
|
||||
description: t(
|
||||
'pages.simulateurs.is.meta.description',
|
||||
'Calculez votre impôt sur les sociétés'
|
||||
),
|
||||
color: '#E71D66',
|
||||
},
|
||||
shortName: t('pages.simulateurs.is.meta.title', 'Impôt sur les sociétés'),
|
||||
title: t(
|
||||
'pages.simulateurs.is.title',
|
||||
"Simulateur d'impôt sur les sociétés"
|
||||
),
|
||||
|
||||
nextSteps: ['salarié', 'comparaison-statuts'],
|
||||
},
|
||||
}
|
||||
}
|
|
@ -39,6 +39,7 @@ import PAMCHome from './PAMCHome'
|
|||
import SalariéSimulation from './Salarié'
|
||||
import SchemeComparaisonPage from './SchemeComparaison'
|
||||
import ÉconomieCollaborative from './ÉconomieCollaborative'
|
||||
import getData from './metadata-src.js'
|
||||
|
||||
const simulateurs = [
|
||||
'salarié',
|
||||
|
@ -106,40 +107,19 @@ export function getSimulatorsData({
|
|||
t = (_: unknown, text: string) => text,
|
||||
sitePaths = constructLocalizedSitePath('fr'),
|
||||
language = 'fr',
|
||||
}): SimulatorData {
|
||||
} = {}): SimulatorData {
|
||||
const pureSimulatorsData = getData({ t })
|
||||
return {
|
||||
salarié: {
|
||||
tracking: 'salarie',
|
||||
...pureSimulatorsData['salarié'],
|
||||
config: salariéConfig,
|
||||
component: SalariéSimulation,
|
||||
icône: '🤝',
|
||||
title: t(
|
||||
'pages.simulateurs.salarié.title',
|
||||
'Simulateur de revenus pour salarié'
|
||||
),
|
||||
iframePath: 'simulateur-embauche',
|
||||
meta: {
|
||||
description: t(
|
||||
'pages.simulateurs.salarié.meta.description',
|
||||
"Calcul du salaire net, net après impôt et coût total employeur. Beaucoup d'options disponibles (cadre, stage, apprentissage, heures supplémentaires, etc.)"
|
||||
),
|
||||
ogDescription: t(
|
||||
'pages.simulateurs.salarié.meta.ogDescription',
|
||||
"En tant que salarié, calculez immédiatement votre revenu net après impôt à partir du brut mensuel ou annuel. En tant qu'employé, estimez le coût total d'une embauche à partir du brut. Ce simulateur est développé avec les experts de l'Urssaf, et il adapte les calculs à votre situation (statut cadre, stage, apprentissage, heures supplémentaire, titre-restaurants, mutuelle, temps partiel, convention collective, etc.)"
|
||||
),
|
||||
...pureSimulatorsData['salarié'].meta,
|
||||
ogImage:
|
||||
language === 'fr' ? salaireBrutNetPreviewFR : salaireBrutNetPreviewEN,
|
||||
ogTitle: t(
|
||||
'pages.simulateurs.salarié.meta.ogTitle',
|
||||
'Salaire brut, net, net après impôt, coût total : le simulateur ultime pour salariés et employeurs'
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.salarié.meta.titre',
|
||||
'Salaire brut / net : le convertisseur Urssaf'
|
||||
),
|
||||
},
|
||||
path: sitePaths.simulateurs.salarié,
|
||||
shortName: t('pages.simulateurs.salarié.shortname', 'Salarié'),
|
||||
seoExplanations: (
|
||||
<Trans i18nKey="pages.simulateurs.salarié.seo">
|
||||
<h2>Comment calculer le salaire net ?</h2>
|
||||
|
@ -215,10 +195,7 @@ export function getSimulatorsData({
|
|||
nextSteps: ['chômage-partiel', 'aides-embauche'],
|
||||
},
|
||||
'entreprise-individuelle': {
|
||||
tracking: {
|
||||
chapter2: 'statut_entreprise',
|
||||
chapter3: 'EI',
|
||||
},
|
||||
...pureSimulatorsData['entreprise-individuelle'],
|
||||
config: {
|
||||
...indépendantConfig,
|
||||
situation: {
|
||||
|
@ -226,34 +203,12 @@ export function getSimulatorsData({
|
|||
'entreprise . imposition': "'IR'",
|
||||
},
|
||||
},
|
||||
iframePath: 'simulateur-EI',
|
||||
icône: '🚶♀️',
|
||||
meta: {
|
||||
description: t(
|
||||
'pages.simulateurs.ei.meta.description',
|
||||
"Calcul du revenu à partir du chiffre d'affaires, après déduction des cotisations et des impôts"
|
||||
),
|
||||
ogDescription: t(
|
||||
'pages.simulateurs.ei.meta.ogDescription',
|
||||
"Grâce au simulateur de revenu pour entreprise individuelle développé par l'Urssaf, vous pourrez estimer le montant de vos revenus en fonction de votre chiffre d'affaires mensuel ou annuel pour mieux gérer votre trésorerie. Ou dans le sens inverse : savoir quel montant facturer pour atteindre un certain revenu."
|
||||
),
|
||||
...pureSimulatorsData['entreprise-individuelle'].meta,
|
||||
ogImage: AutoEntrepreneurPreview,
|
||||
ogTitle: t(
|
||||
'pages.simulateurs.ei.meta.ogTitle',
|
||||
'Entreprise individuelle (EI) : calculez rapidement votre revenu net à partir du CA et vice-versa'
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.ei.meta.titre',
|
||||
'Entreprise individuelle (EI) : simulateur de revenus'
|
||||
),
|
||||
},
|
||||
component: EntrepriseIndividuelle,
|
||||
path: sitePaths.simulateurs['entreprise-individuelle'],
|
||||
shortName: t('pages.simulateurs.ei.shortname', 'EI'),
|
||||
title: t(
|
||||
'pages.simulateurs.ei.title',
|
||||
'Simulateur pour entreprise individuelle (EI)'
|
||||
),
|
||||
seoExplanations: (
|
||||
<Trans i18nKey="pages.simulateurs.ei.seo explanation">
|
||||
<h2>
|
||||
|
@ -322,69 +277,24 @@ export function getSimulatorsData({
|
|||
nextSteps: ['comparaison-statuts'],
|
||||
},
|
||||
eirl: {
|
||||
tracking: {
|
||||
chapter2: 'statut_entreprise',
|
||||
chapter3: 'EIRL',
|
||||
},
|
||||
...pureSimulatorsData['eirl'],
|
||||
config: indépendantConfig,
|
||||
icône: '🚶',
|
||||
iframePath: 'simulateur-EIRL',
|
||||
meta: {
|
||||
description: t(
|
||||
'pages.simulateurs.eirl.meta.description',
|
||||
"Calcul du revenu à partir du chiffre d'affaires, après déduction des cotisations et des impôts"
|
||||
),
|
||||
ogDescription: t(
|
||||
'pages.simulateurs.eirl.meta.ogDescription',
|
||||
"Grâce au simulateur de revenu pour EIRL développé par l'Urssaf, vous pourrez estimer le montant de vos revenus en fonction de votre chiffre d'affaires mensuel ou annuel pour mieux gérer votre trésorerie. Ou dans le sens inverse : savoir quel montant facturer pour atteindre un certain revenu."
|
||||
),
|
||||
...pureSimulatorsData['eirl'].meta,
|
||||
ogImage: AutoEntrepreneurPreview,
|
||||
ogTitle: t(
|
||||
'pages.simulateurs.eirl.meta.ogTitle',
|
||||
"Dirigeant d'EIRL : calculez rapidement votre revenu net à partir du CA et vice-versa"
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.eirl.meta.titre',
|
||||
'EIRL : simulateur de revenus pour dirigeant'
|
||||
),
|
||||
},
|
||||
component: IndépendantSimulation,
|
||||
path: sitePaths.simulateurs.eirl,
|
||||
shortName: t('pages.simulateurs.eirl.shortname', 'EIRL'),
|
||||
title: t('pages.simulateurs.eirl.title', "Simulateur d'EIRL"),
|
||||
|
||||
nextSteps: ['comparaison-statuts'],
|
||||
},
|
||||
sasu: {
|
||||
...pureSimulatorsData['sasu'],
|
||||
config: sasuConfig,
|
||||
tracking: {
|
||||
chapter2: 'statut_entreprise',
|
||||
chapter3: 'SASU',
|
||||
},
|
||||
icône: '📘',
|
||||
iframePath: 'simulateur-assimilesalarie',
|
||||
meta: {
|
||||
description: t(
|
||||
'pages.simulateurs.sasu.meta.description',
|
||||
'Calcul du salaire net à partir du total alloué à la rémunération et inversement'
|
||||
),
|
||||
ogDescription: t(
|
||||
'pages.simulateurs.sasu.meta.ogDescription',
|
||||
'En tant que dirigeant assimilé-salarié, calculez immédiatement votre revenu net après impôt à partir du total alloué à votre rémunération.'
|
||||
),
|
||||
...pureSimulatorsData['sasu'].meta,
|
||||
ogImage: RémunérationSASUPreview,
|
||||
ogTitle: t(
|
||||
'pages.simulateurs.sasu.meta.ogTitle',
|
||||
'Rémunération du dirigeant de SASU : un simulateur pour connaître votre salaire net'
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.sasu.meta.titre',
|
||||
'SASU : simulateur de revenus pour dirigeant'
|
||||
),
|
||||
},
|
||||
path: sitePaths.simulateurs.sasu,
|
||||
shortName: t('pages.simulateurs.sasu.shortname', 'SASU'),
|
||||
title: t('pages.simulateurs.sasu.title', 'Simulateur de SASU'),
|
||||
component: function SasuSimulation() {
|
||||
return (
|
||||
<>
|
||||
|
@ -437,6 +347,7 @@ export function getSimulatorsData({
|
|||
nextSteps: ['is', 'comparaison-statuts'],
|
||||
},
|
||||
eurl: {
|
||||
...pureSimulatorsData['eurl'],
|
||||
config: {
|
||||
...indépendantConfig,
|
||||
situation: {
|
||||
|
@ -444,43 +355,22 @@ export function getSimulatorsData({
|
|||
'entreprise . imposition': "'IS'",
|
||||
},
|
||||
},
|
||||
tracking: {
|
||||
chapter2: 'statut_entreprise',
|
||||
chapter3: 'EURL',
|
||||
},
|
||||
icône: '📕',
|
||||
iframePath: 'simulateur-eurl',
|
||||
meta: {
|
||||
description: t(
|
||||
'pages.simulateurs.eurl.meta.description',
|
||||
'Calcul du salaire net à partir du total alloué à la rémunération et inversement'
|
||||
),
|
||||
ogDescription: t(
|
||||
'pages.simulateurs.eurl.meta.ogDescription',
|
||||
'En tant que dirigeant assimilé-salarié, calculez immédiatement votre revenu net après impôt à partir du total alloué à votre rémunération.'
|
||||
),
|
||||
...pureSimulatorsData['eurl'].meta,
|
||||
ogImage: RémunérationSASUPreview,
|
||||
ogTitle: t(
|
||||
'pages.simulateurs.eurl.meta.ogTitle',
|
||||
"Rémunération du dirigeant d'EURL : un simulateur pour connaître votre salaire net"
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.eurl.meta.titre',
|
||||
'EURL : simulateur de revenus pour dirigeant'
|
||||
),
|
||||
},
|
||||
path: sitePaths.simulateurs.eurl,
|
||||
shortName: t('pages.simulateurs.sasu.shortname', 'EURL'),
|
||||
title: t('pages.simulateurs.sasu.title', "Simulateur d'EURL"),
|
||||
component: IndépendantSimulation,
|
||||
nextSteps: ['is', 'comparaison-statuts'],
|
||||
},
|
||||
'auto-entrepreneur': {
|
||||
...pureSimulatorsData['auto-entrepreneur'],
|
||||
tracking: 'auto_entrepreneur',
|
||||
config: autoEntrepreneurConfig,
|
||||
icône: '🚶♂️',
|
||||
iframePath: 'simulateur-autoentrepreneur',
|
||||
meta: {
|
||||
...pureSimulatorsData['auto-entrepreneur'].meta,
|
||||
description: t(
|
||||
'pages.simulateurs.auto-entrepreneur.meta.description',
|
||||
"Calcul du revenu à partir du chiffre d'affaires, après déduction des cotisations et des impôts"
|
||||
|
@ -577,92 +467,32 @@ export function getSimulatorsData({
|
|||
nextSteps: ['indépendant', 'comparaison-statuts'],
|
||||
},
|
||||
indépendant: {
|
||||
...pureSimulatorsData['indépendant'],
|
||||
config: indépendantConfig,
|
||||
tracking: 'independant',
|
||||
icône: '🏃',
|
||||
iframePath: 'simulateur-independant',
|
||||
path: sitePaths.simulateurs.indépendant,
|
||||
shortName: t('pages.simulateurs.indépendant.shortname', 'Indépendant'),
|
||||
title: t(
|
||||
'pages.simulateurs.indépendant.title',
|
||||
'Simulateur de revenus pour indépendant'
|
||||
),
|
||||
meta: {
|
||||
title: t(
|
||||
'pages.simulateurs.indépendant.meta.title',
|
||||
'Indépendant : simulateur de revenus'
|
||||
),
|
||||
description: t(
|
||||
'pages.simulateurs.indépendant.meta.description',
|
||||
"Calcul du revenu net après impôt et des cotisations à partir du chiffre d'affaires et inversement"
|
||||
),
|
||||
...pureSimulatorsData['indépendant'].meta,
|
||||
},
|
||||
component: IndépendantSimulation,
|
||||
nextSteps: ['comparaison-statuts', 'is'],
|
||||
},
|
||||
|
||||
'artiste-auteur': {
|
||||
icône: '👩🎨',
|
||||
tracking: 'artiste-auteur',
|
||||
iframePath: 'simulateur-artiste-auteur',
|
||||
...pureSimulatorsData['artiste-auteur'],
|
||||
meta: {
|
||||
title: t(
|
||||
'pages.simulateurs.artiste-auteur.meta.title',
|
||||
'Artiste-auteur: calcul des cotisations Urssaf'
|
||||
),
|
||||
description: t(
|
||||
'pages.simulateurs.artiste-auteur.meta.description',
|
||||
"Estimez les cotisations sociales sur les droits d'auteur et sur le revenu BNC"
|
||||
),
|
||||
ogTitle: 'Artiste-auteur : estimez vos cotisations Urssaf',
|
||||
ogDescription:
|
||||
"Renseignez vos revenus (droits d'auteur et bnc) et découvrez immédiatement le montant des cotisations que vous aurez à payer sur l'année.",
|
||||
...pureSimulatorsData['artiste-auteur'].meta,
|
||||
},
|
||||
path: sitePaths.simulateurs['artiste-auteur'],
|
||||
title: t(
|
||||
'pages.simulateurs.artiste-auteur.title',
|
||||
'Estimer mes cotisations d’artiste-auteur'
|
||||
),
|
||||
shortName: t(
|
||||
'pages.simulateurs.artiste-auteur.shortname',
|
||||
'Artiste-auteur'
|
||||
),
|
||||
component: ArtisteAuteur,
|
||||
},
|
||||
'chômage-partiel': {
|
||||
tracking: 'chomage_partiel',
|
||||
...pureSimulatorsData['chômage-partiel'],
|
||||
component: ChômagePartielComponent,
|
||||
config: chômageParielConfig,
|
||||
path: sitePaths.simulateurs['chômage-partiel'],
|
||||
icône: '😷',
|
||||
iframePath: 'simulateur-chomage-partiel',
|
||||
meta: {
|
||||
description: t(
|
||||
'pages.simulateurs.chômage-partiel.meta.description',
|
||||
"Calcul du revenu net pour l'employé et du reste à charge pour l'employeur après remboursement de l'Etat, en prenant en compte toutes les cotisations sociales."
|
||||
),
|
||||
ogDescription: t(
|
||||
'pages.simulateurs.chômage-partiel.meta.ogDescription',
|
||||
"Accédez à une première estimation en saisissant à partir d'un salaire brut. Vous pourrez ensuite personaliser votre situation (temps partiel, convention, etc). Prends en compte la totalité des cotisations, y compris celles spécifiques à l'indemnité (CSG et CRDS)."
|
||||
),
|
||||
...pureSimulatorsData['chômage-partiel'].meta,
|
||||
ogImage: ChômagePartielPreview,
|
||||
ogTitle: t(
|
||||
'pages.simulateurs.chômage-partiel.meta.ogTitle',
|
||||
"Simulateur chômage partiel : découvrez l'impact sur le revenu net salarié et le coût total employeur."
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.chômage-partiel.meta.titre',
|
||||
"Calcul de l'indemnité chômage partiel : le simulateur Urssaf"
|
||||
),
|
||||
},
|
||||
shortName: t(
|
||||
'pages.simulateurs.chômage-partiel.shortname',
|
||||
'Chômage partiel'
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.chômage-partiel.title',
|
||||
'Covid-19 : Simulateur de chômage partiel'
|
||||
),
|
||||
seoExplanations: (
|
||||
<Trans i18nKey="pages.simulateurs.chômage-partiel.seo">
|
||||
<h2>Comment calculer l'indemnité d'activité partielle ?</h2>
|
||||
|
@ -738,284 +568,137 @@ export function getSimulatorsData({
|
|||
nextSteps: ['salarié', 'aides-embauche'],
|
||||
},
|
||||
'comparaison-statuts': {
|
||||
...pureSimulatorsData['comparaison-statuts'],
|
||||
component: SchemeComparaisonPage,
|
||||
tracking: 'comparaison_statut',
|
||||
icône: '📊',
|
||||
path: sitePaths.simulateurs.comparaison,
|
||||
title: t(
|
||||
'pages.simulateurs.comparaison.title',
|
||||
'Indépendant, assimilé salarié ou auto-entrepreneur : quel régime choisir ?'
|
||||
),
|
||||
meta: {
|
||||
description: t(
|
||||
'pages.simulateurs.comparaison.meta.description',
|
||||
'Auto-entrepreneur, indépendant ou dirigeant de SASU ? Avec ce comparatif, trouvez le régime qui vous correspond le mieux'
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.comparaison.meta.title',
|
||||
"Création d'entreprise : le comparatif des régimes sociaux"
|
||||
),
|
||||
...pureSimulatorsData['comparaison-statuts'].meta,
|
||||
},
|
||||
shortName: t(
|
||||
'pages.simulateurs.comparaison.shortname',
|
||||
'Comparaison des statuts'
|
||||
),
|
||||
},
|
||||
'économie-collaborative': {
|
||||
tracking: 'economie_collaborative',
|
||||
...pureSimulatorsData['économie-collaborative'],
|
||||
component: ÉconomieCollaborative,
|
||||
meta: {
|
||||
title: t(
|
||||
'pages.économie-collaborative.meta.title',
|
||||
'Déclaration des revenus des plateforme en ligne : guide intéractif'
|
||||
),
|
||||
description: t(
|
||||
'pages.économie-collaborative.meta.description',
|
||||
'Airbnb, Drivy, Blablacar, Leboncoin... Découvrez comment être en règle dans vos déclarations'
|
||||
),
|
||||
...pureSimulatorsData['économie-collaborative'].meta,
|
||||
},
|
||||
icône: '🙋',
|
||||
path: sitePaths.simulateurs.économieCollaborative.index,
|
||||
shortName: t(
|
||||
'pages.économie-collaborative.shortname',
|
||||
'Guide économie collaborative'
|
||||
),
|
||||
},
|
||||
'aide-déclaration-indépendant': {
|
||||
...pureSimulatorsData['aide-déclaration-indépendant'],
|
||||
component: AideDéclarationIndépendant,
|
||||
tracking: {
|
||||
chapter1: 'gerer',
|
||||
chapter2: 'aide_declaration_independant',
|
||||
},
|
||||
icône: '✍️',
|
||||
meta: {
|
||||
description: t(
|
||||
'pages.gérer.aide-déclaration-indépendant.meta.description',
|
||||
'Calculer facilement les montants des charges sociales à reporter dans votre déclaration de revenu 2020.'
|
||||
),
|
||||
title: t(
|
||||
'pages.gérer.aide-déclaration-indépendant.meta.title',
|
||||
'Déclaration de revenus indépendant : calcul du montant des cotisations'
|
||||
),
|
||||
...pureSimulatorsData['aide-déclaration-indépendant'].meta,
|
||||
},
|
||||
path: sitePaths.gérer.déclarationIndépendant,
|
||||
shortName: t(
|
||||
'pages.gérer.aide-déclaration-indépendant.shortname',
|
||||
'Aide à la déclaration de revenu'
|
||||
),
|
||||
title: t(
|
||||
'pages.gérer.aide-déclaration-indépendant.title',
|
||||
"Aide à la déclaration de revenus au titre de l'année 2020"
|
||||
),
|
||||
},
|
||||
'demande-mobilité': {
|
||||
...pureSimulatorsData['demande-mobilité'],
|
||||
component: FormulaireMobilitéIndépendant,
|
||||
tracking: {
|
||||
chapter1: 'gerer',
|
||||
chapter2: 'demande_mobilite',
|
||||
},
|
||||
icône: '🧳',
|
||||
meta: {
|
||||
title: t(
|
||||
'pages.gérer.demande-mobilité.meta.title',
|
||||
'Travailleur indépendant : demande de mobilité en Europe'
|
||||
),
|
||||
description: t(
|
||||
'pages.gérer.demande-mobilité.meta.description',
|
||||
"Formulaire interactif à compléter pour les indépendants souhaitant exercer leur activité dans d'autres pays d'Europe"
|
||||
),
|
||||
...pureSimulatorsData['demande-mobilité'].meta,
|
||||
},
|
||||
path: sitePaths.gérer.formulaireMobilité,
|
||||
shortName: t(
|
||||
'pages.gérer.demande-mobilité.shortname',
|
||||
'Demande de mobilité internationale'
|
||||
),
|
||||
private: true,
|
||||
iframePath: 'demande-mobilite',
|
||||
},
|
||||
médecin: {
|
||||
...pureSimulatorsData['médecin'],
|
||||
config: médecinConfig,
|
||||
tracking: {
|
||||
chapter2: 'profession_liberale',
|
||||
chapter3: 'medecin',
|
||||
},
|
||||
icône: '⚕️',
|
||||
iframePath: 'médecin',
|
||||
path: sitePaths.simulateurs['profession-libérale'].médecin,
|
||||
shortName: t('pages.simulateurs.médecin.shortname', 'Médecin'),
|
||||
title: t(
|
||||
'pages.simulateurs.médecin.title',
|
||||
'Simulateur de revenus pour médecin en libéral'
|
||||
),
|
||||
component: IndépendantPLSimulation,
|
||||
},
|
||||
'chirurgien-dentiste': {
|
||||
...pureSimulatorsData['chirurgien-dentiste'],
|
||||
config: dentisteConfig,
|
||||
icône: '🦷',
|
||||
tracking: {
|
||||
chapter2: 'profession_liberale',
|
||||
chapter3: 'chirurgien_dentiste',
|
||||
},
|
||||
iframePath: 'chirurgien-dentiste',
|
||||
path: sitePaths.simulateurs['profession-libérale']['chirurgien-dentiste'],
|
||||
shortName: t(
|
||||
'pages.simulateurs.chirurgien-dentiste.shortname',
|
||||
'Chirurgien-dentiste'
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.chirurgien-dentiste.title',
|
||||
'Simulateur de revenus pour chirurgien-dentiste en libéral'
|
||||
),
|
||||
component: IndépendantPLSimulation,
|
||||
},
|
||||
'sage-femme': {
|
||||
...pureSimulatorsData['sage-femme'],
|
||||
config: sageFemmeConfig,
|
||||
icône: '👶',
|
||||
tracking: {
|
||||
chapter2: 'profession_liberale',
|
||||
chapter3: 'sage_femme',
|
||||
},
|
||||
iframePath: 'sage-femme',
|
||||
path: sitePaths.simulateurs['profession-libérale']['sage-femme'],
|
||||
shortName: t('pages.simulateurs.sage-femme.shortname', 'Sage-femme'),
|
||||
title: t(
|
||||
'pages.simulateurs.sage-femme.title',
|
||||
'Simulateur de revenus pour sage-femme en libéral'
|
||||
),
|
||||
component: IndépendantPLSimulation,
|
||||
},
|
||||
'auxiliaire-médical': {
|
||||
...pureSimulatorsData['auxiliaire-médical'],
|
||||
config: auxiliaireConfig,
|
||||
tracking: {
|
||||
chapter2: 'profession_liberale',
|
||||
chapter3: 'auxiliaire_medical',
|
||||
},
|
||||
tooltip: t(
|
||||
'pages.simulateurs.auxiliaire.tooltip',
|
||||
'Infirmiers, masseurs-kinésithérapeutes, pédicures-podologues, orthophonistes et orthoptistes'
|
||||
),
|
||||
icône: '🩹',
|
||||
iframePath: 'auxiliaire-medical',
|
||||
path: sitePaths.simulateurs['profession-libérale'].auxiliaire,
|
||||
shortName: t('pages.simulateurs.auxiliaire.shortname', 'Auxiliaire méd.'),
|
||||
title: t(
|
||||
'pages.simulateurs.auxiliaire.title',
|
||||
'Simulateur de revenus pour auxiliaire médical en libéral'
|
||||
),
|
||||
component: IndépendantPLSimulation,
|
||||
},
|
||||
avocat: {
|
||||
...pureSimulatorsData['avocat'],
|
||||
config: avocatConfig,
|
||||
tracking: {
|
||||
chapter2: 'profession_liberale',
|
||||
chapter3: 'avocat',
|
||||
},
|
||||
icône: '⚖', // j'ai hesité avec 🥑 mais pas envie de me prendre un procès
|
||||
iframePath: 'avocat',
|
||||
path: sitePaths.simulateurs['profession-libérale'].avocat,
|
||||
shortName: t('pages.simulateurs.avocat.shortname', 'Avocat'),
|
||||
title: t(
|
||||
'pages.simulateurs.avocat.title',
|
||||
'Simulateur de revenus pour avocat en libéral'
|
||||
),
|
||||
component: IndépendantPLSimulation,
|
||||
},
|
||||
'expert-comptable': {
|
||||
...pureSimulatorsData['expert-comptable'],
|
||||
config: expertComptableConfig,
|
||||
tracking: {
|
||||
chapter2: 'profession_liberale',
|
||||
chapter3: 'expert_comptable',
|
||||
},
|
||||
icône: '🧮',
|
||||
iframePath: 'expert-comptable',
|
||||
path: sitePaths.simulateurs['profession-libérale']['expert-comptable'],
|
||||
shortName: t(
|
||||
'pages.simulateurs.expert-comptable.shortname',
|
||||
'Expert-Comptable'
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.expert-comptable.title',
|
||||
'Simulateur de revenus pour expert comptable et commissaire aux comptes en libéral'
|
||||
),
|
||||
component: IndépendantPLSimulation,
|
||||
},
|
||||
'profession-libérale': {
|
||||
...pureSimulatorsData['profession-libérale'],
|
||||
config: professionLibéraleConfig,
|
||||
tracking: {
|
||||
chapter2: 'profession_liberale',
|
||||
},
|
||||
icône: '💻',
|
||||
meta: {
|
||||
title: t(
|
||||
'pages.simulateurs.profession-libérale.meta.title',
|
||||
'Professions libérale : le simulateur Urssaf'
|
||||
),
|
||||
description: t(
|
||||
'pages.simulateurs.profession-libérale.meta.description',
|
||||
"Calcul du revenu net pour les indépendants en libéral à l'impôt sur le revenu (IR, BNC)"
|
||||
),
|
||||
...pureSimulatorsData['profession-libérale'].meta,
|
||||
},
|
||||
iframePath: 'profession-liberale',
|
||||
path: sitePaths.simulateurs['profession-libérale'].index,
|
||||
shortName: t(
|
||||
'pages.simulateurs.profession-libérale.shortname',
|
||||
'Profession libérale'
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.profession-libérale.title',
|
||||
'Simulateur de revenus pour profession libérale'
|
||||
),
|
||||
component: IndépendantPLSimulation,
|
||||
},
|
||||
pamc: {
|
||||
...pureSimulatorsData['pamc'],
|
||||
private: true,
|
||||
iframePath: 'pamc',
|
||||
tracking: {},
|
||||
title: t(
|
||||
'pages.simulateurs.pamc.title',
|
||||
|
||||
'PAMC : simulateurs de cotisations et de revenu'
|
||||
),
|
||||
path: sitePaths.simulateurs.pamc,
|
||||
config: professionLibéraleConfig,
|
||||
icône: '🏥',
|
||||
meta: {
|
||||
title: t(
|
||||
'pages.simulateurs.pamc.meta.title',
|
||||
'Simulateurs régime PAMC'
|
||||
),
|
||||
description: t(
|
||||
'pages.simulateurs.pamc.meta.description',
|
||||
'Calcul du revenu net pour les professions libérales du régime PAMC (médecin, chirurgien-dentiste, sage-femme et auxiliaire médical)'
|
||||
),
|
||||
...pureSimulatorsData['pamc'].meta,
|
||||
},
|
||||
shortName: t('pages.simulateurs.pamc.shortname', 'PAMC'),
|
||||
component: PAMCHome,
|
||||
},
|
||||
'aides-embauche': {
|
||||
icône: '🎁',
|
||||
...pureSimulatorsData['aides-embauche'],
|
||||
tracking: 'aides_embauche',
|
||||
meta: {
|
||||
title: t(
|
||||
'pages.simulateurs.aides-embauche.meta.title',
|
||||
'Aides à l’embauche'
|
||||
),
|
||||
description: t(
|
||||
'pages.simulateurs.aides-embauche.meta.description',
|
||||
'Découvrez les principales aides à l’embauche et estimez leur montant en répondant à quelques questions.'
|
||||
),
|
||||
color: '#11965f',
|
||||
...pureSimulatorsData['aides-embauche'].meta,
|
||||
},
|
||||
path: sitePaths.simulateurs['aides-embauche'],
|
||||
iframePath: 'aides-embauche',
|
||||
shortName: t(
|
||||
'pages.simulateurs.aides-embauche.meta.title',
|
||||
'Aides à l’embauche'
|
||||
),
|
||||
title: t(
|
||||
'pages.simulateurs.aides-embauche.meta.title',
|
||||
'Aides à l’embauche'
|
||||
),
|
||||
// Cette description est surchargé car elle contient ici du JSX
|
||||
description: (
|
||||
<Trans i18nKey="pages.simulateurs.aides-embauche.introduction">
|
||||
<p>
|
||||
|
@ -1046,23 +729,12 @@ export function getSimulatorsData({
|
|||
nextSteps: ['salarié'],
|
||||
},
|
||||
is: {
|
||||
icône: '🗓',
|
||||
...pureSimulatorsData['is'],
|
||||
tracking: 'impot-societe',
|
||||
path: sitePaths.simulateurs.is,
|
||||
iframePath: 'impot-societe',
|
||||
meta: {
|
||||
title: t('pages.simulateurs.is.meta.title', 'Impôt sur les sociétés'),
|
||||
description: t(
|
||||
'pages.simulateurs.is.meta.description',
|
||||
'Calculez votre impôt sur les sociétés'
|
||||
),
|
||||
color: '#E71D66',
|
||||
...pureSimulatorsData['is'].meta,
|
||||
},
|
||||
shortName: t('pages.simulateurs.is.meta.title', 'Impôt sur les sociétés'),
|
||||
title: t(
|
||||
'pages.simulateurs.is.title',
|
||||
"Simulateur d'impôt sur les sociétés"
|
||||
),
|
||||
component: ISSimulation,
|
||||
seoExplanations: (
|
||||
<Trans i18nKey="pages.simulateurs.is.seo">
|
||||
|
|
|
@ -136,6 +136,11 @@ module.exports.default = {
|
|||
FR_BASE_URL: 'http://localhost:8080/mon-entreprise',
|
||||
AT_INTERNET_SITE_ID: '',
|
||||
}),
|
||||
new EnvironmentPlugin({
|
||||
ALGOLIA_APP_ID: '',
|
||||
ALGOLIA_SEARCH_KEY: '',
|
||||
ALGOLIA_INDEX_PREFIX: '',
|
||||
}),
|
||||
new EnvironmentPlugin({
|
||||
GITHUB_REF: '',
|
||||
GITHUB_HEAD_REF: '',
|
||||
|
|
201
yarn.lock
201
yarn.lock
|
@ -2,6 +2,110 @@
|
|||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@algolia/cache-browser-local-storage@4.10.2":
|
||||
version "4.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.10.2.tgz#9925c7c0ce94257564b8948b60fc427c4a98124c"
|
||||
integrity sha512-B3NInwobEAim4J4Y0mgZermoi0DCXdTT/Q+4ehLamqUqxLw8To5zc9izjg7B8JaFSQsqflRdCeRmYEv2gYDY7g==
|
||||
dependencies:
|
||||
"@algolia/cache-common" "4.10.2"
|
||||
|
||||
"@algolia/cache-common@4.10.2":
|
||||
version "4.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@algolia/cache-common/-/cache-common-4.10.2.tgz#0113419518419895118d132bed4115345a865ce3"
|
||||
integrity sha512-xcGbV0+6gLu2C7XoJdD+Pp6wWjROle6PNDsa6O21vS7fw1a03xb2bEnFdl1U31bs69P1z8IRy3h+8RVBouvhhw==
|
||||
|
||||
"@algolia/cache-in-memory@4.10.2":
|
||||
version "4.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@algolia/cache-in-memory/-/cache-in-memory-4.10.2.tgz#2d34d4155425b385d19ff197a8943a4b5084c790"
|
||||
integrity sha512-zPIcxHQEJXy+M35A+v9Y5u5BAQOKR2aFK0kYpAdW/OrgxYcrFHtVCxwIWB/ZhGbkDtzCW8/8tJeddcD5YsHX9Q==
|
||||
dependencies:
|
||||
"@algolia/cache-common" "4.10.2"
|
||||
|
||||
"@algolia/client-account@4.10.2":
|
||||
version "4.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@algolia/client-account/-/client-account-4.10.2.tgz#c53d18d4f57ab5343c21e0ed795421964ba0cbb9"
|
||||
integrity sha512-iuIU+xUtjgR9p4Hpujlr8mePDPSrVIk3peg+RAUhxniLBDaI+OhgHyhP6Lmh9flWk+JfRg91Rhk46xuxMLqwfA==
|
||||
dependencies:
|
||||
"@algolia/client-common" "4.10.2"
|
||||
"@algolia/client-search" "4.10.2"
|
||||
"@algolia/transporter" "4.10.2"
|
||||
|
||||
"@algolia/client-analytics@4.10.2":
|
||||
version "4.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-4.10.2.tgz#93c881cfb9e5df389725d821327fa801f1baa2c6"
|
||||
integrity sha512-u47J65NHs0fMryDrMeuLMGjXDOKt/muF9WlfbMslT2Cvdd7PZwl9KYnT7xMhnmBB8TDiDMmEQkDykhnCOnwVNw==
|
||||
dependencies:
|
||||
"@algolia/client-common" "4.10.2"
|
||||
"@algolia/client-search" "4.10.2"
|
||||
"@algolia/requester-common" "4.10.2"
|
||||
"@algolia/transporter" "4.10.2"
|
||||
|
||||
"@algolia/client-common@4.10.2":
|
||||
version "4.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-4.10.2.tgz#a715e8feb2a2b6ea38765f53e8ae6ffc4ed80aba"
|
||||
integrity sha512-sfgZCv9ha9aHbe3ErAYb1blg2qx4XTLvQqP1jq8asU75rrH9XBTtSzQQO43GlArwhtwCHLgcWquN3WgPlLzkiQ==
|
||||
dependencies:
|
||||
"@algolia/requester-common" "4.10.2"
|
||||
"@algolia/transporter" "4.10.2"
|
||||
|
||||
"@algolia/client-personalization@4.10.2":
|
||||
version "4.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-4.10.2.tgz#89d761bcf60ce13b8565c2ae8ab644c3a3d114c8"
|
||||
integrity sha512-2UhUNo/czfA/keOC3+vFyMnFGV/E1Zkm+ek9Fsk/9miS39UMhx2CmH5vKSIJ7jxLSin7zBaCwKt65phfYty1pg==
|
||||
dependencies:
|
||||
"@algolia/client-common" "4.10.2"
|
||||
"@algolia/requester-common" "4.10.2"
|
||||
"@algolia/transporter" "4.10.2"
|
||||
|
||||
"@algolia/client-search@4.10.2":
|
||||
version "4.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-4.10.2.tgz#ad281b04ec4e6eaff68fb5be330f0bdf965ce011"
|
||||
integrity sha512-ZdOh6XS6Y9bcekfG4y0VhdoIYfsTounsgXX4Bt3X2RCcmY3uotgaq2EVY58E6q6nvfgBfPHW18+AZCHKTWHAAw==
|
||||
dependencies:
|
||||
"@algolia/client-common" "4.10.2"
|
||||
"@algolia/requester-common" "4.10.2"
|
||||
"@algolia/transporter" "4.10.2"
|
||||
|
||||
"@algolia/logger-common@4.10.2":
|
||||
version "4.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@algolia/logger-common/-/logger-common-4.10.2.tgz#f28e966a6b878af2917ed2e1518f46650a6fb8ad"
|
||||
integrity sha512-UJaU6arzmW+FT5fCv5NIbxNMtEoGcf+UENmZxxu7k7UWPARR2XL4ljJ45Jv14Z5dlz32LXWtR1PRmNfkDMk22Q==
|
||||
|
||||
"@algolia/logger-console@4.10.2":
|
||||
version "4.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@algolia/logger-console/-/logger-console-4.10.2.tgz#9d3dcbb077242db92f0f0a1795ec95c3bc839599"
|
||||
integrity sha512-JrCrZ7CGs/TsyNR2AWe9Vdd6rsuxfvfcpqbu+CY7LBUYEnV8GERkf7FnDNaKVNsFJqClILCGh3U8CzQ1G5L+kA==
|
||||
dependencies:
|
||||
"@algolia/logger-common" "4.10.2"
|
||||
|
||||
"@algolia/requester-browser-xhr@4.10.2":
|
||||
version "4.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.10.2.tgz#2286e2f10fff3651f719b8d7d3defc8c032fcce0"
|
||||
integrity sha512-LveaAp7/oCBotv1aZ4VHz8fCcJA7v/28ayh+Ljlm+hYXsxgs6NAYKz7iBpxGN7q5MV8GM+MThRYNFoT0cHTMxQ==
|
||||
dependencies:
|
||||
"@algolia/requester-common" "4.10.2"
|
||||
|
||||
"@algolia/requester-common@4.10.2":
|
||||
version "4.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@algolia/requester-common/-/requester-common-4.10.2.tgz#8b62f0848454ec5b07bd3599f5fb2b87ec7c4de8"
|
||||
integrity sha512-3J2W0fAaURLGK0lEGeNb8eWJnQcsu+oIcfJTCIYkYT5T9w21M65kUUyD9QSf/K137qQts3tzGniUR3LxfovlXA==
|
||||
|
||||
"@algolia/requester-node-http@4.10.2":
|
||||
version "4.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-4.10.2.tgz#edb691e34e18aacc15107193319e1a712e024649"
|
||||
integrity sha512-IBqsalCGgn0CrOP1PKRB5rufEOvHlrSQUFEGXZ8mxmE/zU8CLX2LKqdHbEFeNDLFl+l+8HW5BGVDGD2rvG+hSg==
|
||||
dependencies:
|
||||
"@algolia/requester-common" "4.10.2"
|
||||
|
||||
"@algolia/transporter@4.10.2":
|
||||
version "4.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@algolia/transporter/-/transporter-4.10.2.tgz#ae0fa7c99b9bf8efa5ac83843558be1074e7c045"
|
||||
integrity sha512-I3QDRSookQtPSUEnxT2XCShhipCT4beJBpWhteNwMrWQF/SqTsveqSR6bX0G49lDh9MOmYrOlCegteuKuT/tEw==
|
||||
dependencies:
|
||||
"@algolia/cache-common" "4.10.2"
|
||||
"@algolia/logger-common" "4.10.2"
|
||||
"@algolia/requester-common" "4.10.2"
|
||||
|
||||
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4":
|
||||
version "7.10.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a"
|
||||
|
@ -3476,6 +3580,23 @@
|
|||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-instantsearch-core@*":
|
||||
version "6.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-instantsearch-core/-/react-instantsearch-core-6.10.2.tgz#634a887233ce76cc0f37a37f30909fe77a24d73b"
|
||||
integrity sha512-dG/XHdrPWjVvQTTOg4Q5somVfE6xePOEFJXVeVsRNB+Pj8tzfFR6niFOStf791wGM9BKVBmmy2rCAMhcbfROnw==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
algoliasearch ">=4"
|
||||
algoliasearch-helper ">=3"
|
||||
|
||||
"@types/react-instantsearch-dom@^6.10.1":
|
||||
version "6.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-instantsearch-dom/-/react-instantsearch-dom-6.10.1.tgz#9a0aa032c18e38c429f0d2a7c432959beb45f5a7"
|
||||
integrity sha512-LISZFa3NHTB8455e+5q/igQVt11ElnUQcYp1/O+ju46Suck83EjyS4YXacUx+zTJGlagIxJadGMV+V7amJAzTQ==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
"@types/react-instantsearch-core" "*"
|
||||
|
||||
"@types/react-native@*":
|
||||
version "0.63.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.63.8.tgz#73ec087122c64c309eeaf150b565b8d755f0fb1f"
|
||||
|
@ -4340,6 +4461,33 @@ ajv@^6.12.3, ajv@^6.12.5:
|
|||
json-schema-traverse "^0.4.1"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
algoliasearch-helper@>=3, algoliasearch-helper@^3.4.3:
|
||||
version "3.5.3"
|
||||
resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.5.3.tgz#fbf8b328bc103efdefde59a7d25eaffe85b2490f"
|
||||
integrity sha512-DtSlOKAJ6TGkQD6u58g6/ABdMmHf3pAj6xVL5hJF+D4z9ldDRf/f5v6puNIxGOlJRwGVvFGyz34beYNqhLDUbQ==
|
||||
dependencies:
|
||||
events "^1.1.1"
|
||||
|
||||
"algoliasearch@>= 3.27.1 < 5", algoliasearch@>=4, algoliasearch@^4.10.2:
|
||||
version "4.10.2"
|
||||
resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.10.2.tgz#23e88c71cb381d5b59430baa5d417186cc8ff683"
|
||||
integrity sha512-BAYCe97XRfO15irJKBRjBnrp9tSqN0jppklLIXKdtUcXlibcPQtuAeGUP2cPiz6bJd3ISuoYzLFNt4/fQYtLMw==
|
||||
dependencies:
|
||||
"@algolia/cache-browser-local-storage" "4.10.2"
|
||||
"@algolia/cache-common" "4.10.2"
|
||||
"@algolia/cache-in-memory" "4.10.2"
|
||||
"@algolia/client-account" "4.10.2"
|
||||
"@algolia/client-analytics" "4.10.2"
|
||||
"@algolia/client-common" "4.10.2"
|
||||
"@algolia/client-personalization" "4.10.2"
|
||||
"@algolia/client-search" "4.10.2"
|
||||
"@algolia/logger-common" "4.10.2"
|
||||
"@algolia/logger-console" "4.10.2"
|
||||
"@algolia/requester-browser-xhr" "4.10.2"
|
||||
"@algolia/requester-common" "4.10.2"
|
||||
"@algolia/requester-node-http" "4.10.2"
|
||||
"@algolia/transporter" "4.10.2"
|
||||
|
||||
ally.js@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/ally.js/-/ally.js-1.4.1.tgz#9fb7e6ba58efac4ee9131cb29aa9ee3b540bcf1e"
|
||||
|
@ -7809,6 +7957,11 @@ eventemitter3@^4.0.0:
|
|||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.5.tgz#51d81e4f1ccc8311a04f0c20121ea824377ea6d9"
|
||||
integrity sha512-QR0rh0YiPuxuDQ6+T9GAO/xWTExXpxIes1Nl9RykNGTnE1HJmkuEfxJH9cubjIOQZ/GH4qNBR4u8VSHaKiWs4g==
|
||||
|
||||
events@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
|
||||
integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=
|
||||
|
||||
events@^3.0.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379"
|
||||
|
@ -9518,6 +9671,11 @@ inquirer@^7.0.0:
|
|||
strip-ansi "^6.0.0"
|
||||
through "^2.3.6"
|
||||
|
||||
instantsearch.css@^7.4.5:
|
||||
version "7.4.5"
|
||||
resolved "https://registry.yarnpkg.com/instantsearch.css/-/instantsearch.css-7.4.5.tgz#2a521aa634329bf1680f79adf87c79d67669ec8d"
|
||||
integrity sha512-iIGBYjCokU93DDB8kbeztKtlu4qVEyTg1xvS6iSO1YvqRwkIZgf0tmsl/GytsLdZhuw8j4wEaeYsCzNbeJ/zEQ==
|
||||
|
||||
internal-slot@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.2.tgz#9c2e9fb3cd8e5e4256c6f45fe310067fcfa378a3"
|
||||
|
@ -13345,7 +13503,7 @@ react-easy-emoji@^1.2.0, react-easy-emoji@^1.4.0:
|
|||
lodash.assign "^4.0.8"
|
||||
string-replace-to-array "^1.0.1"
|
||||
|
||||
react-fast-compare@^3.1.1:
|
||||
react-fast-compare@^3.0.0, react-fast-compare@^3.1.1:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
|
||||
integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==
|
||||
|
@ -13382,6 +13540,47 @@ react-i18next@^11.0.0:
|
|||
"@babel/runtime" "^7.3.1"
|
||||
html-parse-stringify2 "2.0.1"
|
||||
|
||||
react-instantsearch-core@^6.11.2:
|
||||
version "6.11.2"
|
||||
resolved "https://registry.yarnpkg.com/react-instantsearch-core/-/react-instantsearch-core-6.11.2.tgz#5d70b04b02a91f2729e664156e6cd5203fae2c26"
|
||||
integrity sha512-DSvS8XRESmhuBp9q+lhhsGqEKupWJioe95CCelUH0RoB8RtdC2vXRvBMDBEqTf7vG5K7b/dbbObBj5PnMqv5Sw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.1.2"
|
||||
algoliasearch-helper "^3.4.3"
|
||||
prop-types "^15.6.2"
|
||||
react-fast-compare "^3.0.0"
|
||||
|
||||
react-instantsearch-dom@^6.11.2:
|
||||
version "6.11.2"
|
||||
resolved "https://registry.yarnpkg.com/react-instantsearch-dom/-/react-instantsearch-dom-6.11.2.tgz#048e8934dfac472eb59a16fa0125fda0669334c6"
|
||||
integrity sha512-n6d0E9rreGHIo88OuWHagabAwS9/6NQ/vxRivi0n1Zfqhkaota6QTh83Ay2HIBnio1kRuJgqJhLpO+85R4vzvQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.1.2"
|
||||
algoliasearch-helper "^3.4.3"
|
||||
classnames "^2.2.5"
|
||||
prop-types "^15.6.2"
|
||||
react-fast-compare "^3.0.0"
|
||||
react-instantsearch-core "^6.11.2"
|
||||
|
||||
react-instantsearch-native@^6.11.2:
|
||||
version "6.11.2"
|
||||
resolved "https://registry.yarnpkg.com/react-instantsearch-native/-/react-instantsearch-native-6.11.2.tgz#4493e05f7efbd1cb6bd394eac568075d86bb200c"
|
||||
integrity sha512-T4fXONNRXx5HkUVN2qIcZifIivuIG7vMNDsgGtKL3OM5+eUvu+jZYRdBOX0c2WY3S036jTRoFiweVgxYI1xIig==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.1.2"
|
||||
algoliasearch ">= 3.27.1 < 5"
|
||||
react-instantsearch-core "^6.11.2"
|
||||
|
||||
react-instantsearch@^6.11.2:
|
||||
version "6.11.2"
|
||||
resolved "https://registry.yarnpkg.com/react-instantsearch/-/react-instantsearch-6.11.2.tgz#5df882a9bbfd65552c50ebc3f2874df21662e1b6"
|
||||
integrity sha512-SlB4MdT4kJx9upGh6enf0XJJJ6byGLt/09rCbdJDRTapTqfn85PCZ7LXBFapLU0BEnJHtGOWfdZMHnRQ0FET9w==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.1.2"
|
||||
react-instantsearch-core "^6.11.2"
|
||||
react-instantsearch-dom "^6.11.2"
|
||||
react-instantsearch-native "^6.11.2"
|
||||
|
||||
"react-is@^16.12.0 || ^17.0.0", react-is@^17.0.1:
|
||||
version "17.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339"
|
||||
|
|
Loading…
Reference in New Issue