Merge pull request #1008 from betagouv/split-codebase
Sépare le moteur et le code applicatif dans des package dédiéspull/1039/head
commit
2b04fbbc1f
|
@ -1,11 +1,9 @@
|
|||
.tags*
|
||||
.tmp
|
||||
.env
|
||||
source/data
|
||||
node_modules/
|
||||
dist/
|
||||
.DS_Store
|
||||
package-lock.json
|
||||
yarn-error.log
|
||||
cypress/videos
|
||||
cypress/screenshots
|
||||
package-lock.json
|
||||
node_modules/
|
||||
|
||||
# Local Netlify folder
|
||||
.netlify
|
|
@ -39,12 +39,21 @@ git clone --depth 100 git@github.com:betagouv/mon-entreprise.git && cd mon-entre
|
|||
# Install the Javascript dependencies through Yarn
|
||||
yarn install
|
||||
|
||||
# Run the server
|
||||
# Run the server for mon-entreprise
|
||||
cd mon-entreprise
|
||||
yarn start
|
||||
```
|
||||
|
||||
L'application est exécuté sur https://localhost:8080/mon-entreprise pour la version française et http://localhost:8080/infrance pour la version anglaise.
|
||||
|
||||
Si vous souhaitez travailler sur le package publicode, on peut créer un lien
|
||||
symbolique depuis mon-entreprise en executant la commande suivante à la racine
|
||||
du projet :
|
||||
|
||||
```
|
||||
yarn run link:publicodes
|
||||
```
|
||||
|
||||
### Messages de commit
|
||||
|
||||
A mettre sans retenue dans les messages de commit :
|
||||
|
@ -90,7 +99,7 @@ Si vous souhaitez mettre à jour les snapshots vous pouvez utiliser le paramètr
|
|||
Enfin pour les tests d'intégration :
|
||||
|
||||
```sh
|
||||
$ yarn run test-cypress
|
||||
$ yarn run cypress run
|
||||
```
|
||||
|
||||
### Traduction 👽
|
||||
|
@ -121,7 +130,7 @@ $ yarn run i18n:rules:translate
|
|||
$ yarn run i18n:ui:translate
|
||||
```
|
||||
|
||||
N'oubliez pas de vérifier le diff que rien n'est choquant.
|
||||
N'oubliez pas de vérifier sur le diff que rien n'est choquant.
|
||||
|
||||
### CI/CD
|
||||
|
||||
|
@ -132,7 +141,7 @@ N'oubliez pas de vérifier le diff que rien n'est choquant.
|
|||
|
||||
### Analyse des bundles
|
||||
|
||||
La commande `yarn run analyze-bundle` gènere une visualisation interactive du
|
||||
La commande `yarn run compile:analyse-bundle` gènere une visualisation interactive du
|
||||
contenu packagé, cf.
|
||||
[webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer)
|
||||
|
||||
|
@ -148,7 +157,7 @@ raisonnement ayant abouti à ce langage sont dispos sur le repository
|
|||
ailleurs inutilisé.
|
||||
|
||||
Pour se familiariser avec les règles, vous pouvez jeter un œil aux fichiers
|
||||
contenant les règles elles-mêmes (dans le dossier `source/rules`) mais cela peut
|
||||
contenant les règles elles-mêmes (dans le dossier `rules`) mais cela peut
|
||||
s'avérer assez abrupt.
|
||||
|
||||
Essayez plutôt de jeter un oeil [aux tests](./test/mécanismes/expressions.yaml)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
This repository powers [mycompanyinfrance.fr](https://mycompanyinfrance.fr) and [mon-entreprise.fr](https://mon-entreprise.fr) and [publi.codes](https://publi.codes).
|
||||
|
||||
The hiring simulator, available on both websites, embeds a [model](https://github.com/betagouv/mon-entreprise/blob/master/source/rules) of the french tax system as a YAML domain specific language. It enables displaying the computing rules on the Web and having a single source of logic for both the computation engine (a JS library) and the generated end-user conversation-like form.
|
||||
The hiring simulator, available on both websites, embeds a [model](https://github.com/betagouv/mon-entreprise/blob/master/mon-entreprise/source/rules) of the french tax system as a YAML domain specific language. It enables displaying the computing rules on the Web and having a single source of logic for both the computation engine (a JS library) and the generated end-user conversation-like form.
|
||||
|
||||
The engine with the French tax law is available as a NPM module and explained [on the wiki](https://github.com/betagouv/mon-entreprise/wiki/Librairie-de-calcul).
|
||||
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
module.exports = {
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
targets: {
|
||||
node: 'current'
|
||||
}
|
||||
}
|
||||
],
|
||||
'@babel/react',
|
||||
'@babel/preset-typescript'
|
||||
],
|
||||
plugins: [
|
||||
'@babel/plugin-proposal-class-properties',
|
||||
'@babel/plugin-proposal-optional-chaining',
|
||||
'@babel/plugin-proposal-nullish-coalescing-operator',
|
||||
'@babel/plugin-proposal-object-rest-spread',
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
'react-hot-loader/babel',
|
||||
['webpack-alias', { config: './source/webpack.dev.js' }],
|
||||
[
|
||||
'ramda',
|
||||
{
|
||||
useES: true
|
||||
}
|
||||
],
|
||||
'babel-plugin-styled-components'
|
||||
]
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"node": "current"
|
||||
}
|
||||
}
|
||||
],
|
||||
"@babel/preset-react",
|
||||
"@babel/preset-typescript"
|
||||
],
|
||||
"plugins": [
|
||||
"babel-plugin-styled-components",
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"@babel/plugin-proposal-optional-chaining",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator",
|
||||
"@babel/plugin-proposal-object-rest-spread",
|
||||
"@babel/plugin-syntax-dynamic-import",
|
||||
["ramda", { "useES": true }]
|
||||
]
|
||||
}
|
50
circle.yml
50
circle.yml
|
@ -5,12 +5,12 @@ commands:
|
|||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-deps-{{ .Branch }}-{{ checksum "package.json" }}
|
||||
- v1-deps-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||
- v1-deps-{{ .Branch }}
|
||||
- v1-deps
|
||||
- run: yarn install --frozen-lockfile
|
||||
- save_cache:
|
||||
key: v1-deps-{{ .Branch }}-{{ checksum "package.json" }}
|
||||
key: v1-deps-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||
paths:
|
||||
- ~/.cache
|
||||
cypress:
|
||||
|
@ -27,7 +27,9 @@ commands:
|
|||
type: string
|
||||
default: https://mon-entreprise.fr
|
||||
steps:
|
||||
- run: CYPRESS_baseUrl=<< parameters.base_url >> yarn run cypress run --record --key 21660df5-36a5-4c49-b23d-801799b0c759 --env language=<< parameters.language >> --config integrationFolder=cypress/integration/<< parameters.integration_folder >>
|
||||
- run: |
|
||||
cd mon-entreprise
|
||||
CYPRESS_baseUrl=<< parameters.base_url >> yarn run cypress run --record --key 21660df5-36a5-4c49-b23d-801799b0c759 --env language=<< parameters.language >> --config integrationFolder=cypress/integration/<< parameters.integration_folder >>
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
|
@ -36,42 +38,45 @@ jobs:
|
|||
steps:
|
||||
- install
|
||||
- run: |
|
||||
yarn eslintrc-check
|
||||
yarn eslint-check --quiet
|
||||
yarn prettier-check
|
||||
yarn lint:eslintrc
|
||||
yarn lint:eslint --quiet
|
||||
yarn lint:prettier
|
||||
type-check:
|
||||
docker:
|
||||
- image: node:12.16.1-buster
|
||||
steps:
|
||||
- install
|
||||
- run: |
|
||||
yarn type-check
|
||||
- run: yarn test:type
|
||||
i18n-check:
|
||||
docker:
|
||||
- image: node:12.16.1-buster
|
||||
steps:
|
||||
- install
|
||||
- run: yarn run i18n:rules:check
|
||||
- run: yarn run i18n:ui:check
|
||||
- run: |
|
||||
cd mon-entreprise
|
||||
yarn run i18n:rules:check
|
||||
yarn run i18n:ui:check
|
||||
|
||||
unit-test:
|
||||
docker:
|
||||
- image: node:12.16.1-buster
|
||||
steps:
|
||||
- install
|
||||
- run: |
|
||||
git config --global core.quotepath false
|
||||
yarn test
|
||||
yarn test-regressions
|
||||
- run: git config --global core.quotepath false
|
||||
- run: yarn test
|
||||
- run: yarn test:regressions
|
||||
|
||||
end-to-end-test:
|
||||
docker:
|
||||
- image: cypress/base:12.16.1
|
||||
environment:
|
||||
TERM: xterm
|
||||
resource_class: medium+
|
||||
steps:
|
||||
- install
|
||||
- run: yarn run compile-dev
|
||||
- run: yarn workspace mon-entreprise compile:dev
|
||||
- run:
|
||||
command: yarn run serve-dev
|
||||
command: yarn workspace mon-entreprise serve:dev
|
||||
background: true
|
||||
- cypress:
|
||||
base_url: http://localhost:5000
|
||||
|
@ -81,21 +86,13 @@ jobs:
|
|||
- cypress:
|
||||
base_url: http://localhost:5002
|
||||
integration_folder: publi.codes
|
||||
bundlesize-test:
|
||||
docker:
|
||||
- image: cypress/base:12.16.1
|
||||
environment:
|
||||
TERM: xterm
|
||||
steps:
|
||||
- install
|
||||
- run: |
|
||||
yarn run simple-compile
|
||||
yarn test-bundlesize
|
||||
|
||||
production-end-to-end-test:
|
||||
docker:
|
||||
- image: cypress/base:12.16.1
|
||||
environment:
|
||||
TERM: xterm
|
||||
resource_class: medium+
|
||||
parallelism: 3
|
||||
steps:
|
||||
- install
|
||||
|
@ -124,7 +121,6 @@ workflows:
|
|||
- i18n-check
|
||||
- unit-test
|
||||
- end-to-end-test
|
||||
- bundlesize-test
|
||||
- production-end-to-end-test:
|
||||
filters:
|
||||
branches:
|
||||
|
|
|
@ -67,18 +67,11 @@ module.exports = {
|
|||
moduleDirectories: ['node_modules', 'sources'],
|
||||
|
||||
// An array of file extensions your modules use
|
||||
// moduleFileExtensions: [
|
||||
// "js",
|
||||
// "json",
|
||||
// "jsx",
|
||||
// "ts",
|
||||
// "tsx",
|
||||
// "node"
|
||||
// ],
|
||||
moduleFileExtensions: ['js', 'json', 'jsx', 'ts', 'tsx', 'node'],
|
||||
|
||||
// A map from regular expressions to module names that allow to stub out resources with a single module
|
||||
moduleNameMapper: {
|
||||
'\\.css$': '<rootDir>/test/regressions/styleMock.js'
|
||||
'\\.css$': 'mon-entreprise/test/regressions/styleMock.js'
|
||||
},
|
||||
|
||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||
|
@ -115,9 +108,7 @@ module.exports = {
|
|||
// rootDir: null,
|
||||
|
||||
// A list of paths to directories that Jest should use to search for files in
|
||||
// roots: [
|
||||
// "<rootDir>"
|
||||
// ],
|
||||
// roots: ['<rootDir>'],
|
||||
|
||||
// Allows you to use a custom runner instead of Jest's default test runner
|
||||
// runner: "jest-runner",
|
||||
|
@ -141,13 +132,11 @@ module.exports = {
|
|||
// testLocationInResults: false,
|
||||
|
||||
// The glob patterns Jest uses to detect test files
|
||||
testMatch: [
|
||||
// We have 2 test runners (Mocha and Jest), so we create a custom extension without `.test.js`
|
||||
// to dissociate the two.
|
||||
'**/*.jest.js'
|
||||
// "**/__tests__/**/*.[jt]s?(x)",
|
||||
// "**/?(*.)+(spec|test).[tj]s?(x)"
|
||||
],
|
||||
testMatch: ['**/*.jest.js'],
|
||||
// [
|
||||
// "**/__tests__/**/*.[jt]s?(x)",
|
||||
// "**/?(*.)+(spec|test).[tj]s?(x)"
|
||||
// ],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
|
||||
// testPathIgnorePatterns: [
|
||||
|
@ -173,13 +162,14 @@ module.exports = {
|
|||
transform: {
|
||||
// It's not possible to have 2 piped transformers like in webpack
|
||||
// ie ['jest-transform-nearley', 'babel-jest'], so we removed ES6 module from nearley output.
|
||||
'\\.ne$': 'jest-transform-nearley',
|
||||
'\\.yaml$': 'yaml-jest',
|
||||
'\\.(js|tsx?)$': 'babel-jest'
|
||||
'\\.ne$': require.resolve('jest-transform-nearley'),
|
||||
'\\.yaml$': require.resolve('yaml-jest'),
|
||||
'\\.(js|tsx?)$': require.resolve('babel-jest')
|
||||
},
|
||||
|
||||
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||
transformIgnorePatterns: ['/node_modules/(?!ramda).+\\.js$']
|
||||
// An array of regexp pattern strings that are matched against all source file
|
||||
// paths, matched files will skip transformation
|
||||
transformIgnorePatterns: ['node_modules/(?!ramda|publicodes)/']
|
||||
|
||||
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
||||
// unmockedModulePathPatterns: undefined,
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"plugins": [
|
||||
"react-hot-loader/babel",
|
||||
["webpack-alias", { "config": "./webpack.dev.js" }]
|
||||
]
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
extends: '../.eslintrc'
|
||||
overrides:
|
||||
- files: ['*.test.js', 'cypress/integration/**/*.js']
|
||||
env:
|
||||
mocha: true
|
|
@ -0,0 +1,8 @@
|
|||
.env
|
||||
node_modules/
|
||||
package-lock.json
|
||||
yarn-error.log
|
||||
source/data
|
||||
dist/
|
||||
cypress/videos
|
||||
cypress/screenshots
|
|
@ -14,7 +14,7 @@ describe('Simulateurs', function() {
|
|||
})
|
||||
|
||||
it('should display a result when entering a value in any of the currency input', () => {
|
||||
cy.contains('€ / an').click()
|
||||
cy.contains('€/an').click()
|
||||
if (['indépendant', 'assimilé-salarié'].includes(simulateur)) {
|
||||
cy.get(chargeInputSelector).type(1000)
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ describe('Simulateurs', function() {
|
|||
})
|
||||
|
||||
it('should allow to change period', function() {
|
||||
cy.contains('€ / an').click()
|
||||
cy.contains('€/an').click()
|
||||
cy.wait(200)
|
||||
cy.get(inputSelector)
|
||||
.first()
|
||||
|
@ -42,7 +42,7 @@ describe('Simulateurs', function() {
|
|||
cy.get(chargeInputSelector).type('{selectall}6000')
|
||||
}
|
||||
cy.wait(800)
|
||||
cy.contains('€ / mois').click()
|
||||
cy.contains('€/mois').click()
|
||||
cy.get(inputSelector)
|
||||
.first()
|
||||
.invoke('val')
|
||||
|
@ -82,11 +82,12 @@ describe('Simulateurs', function() {
|
|||
cy.contains('Passer').click()
|
||||
cy.contains('Passer').click()
|
||||
cy.contains('Début 2020').click()
|
||||
cy.wait(200)
|
||||
cy.contains('Suivant').click()
|
||||
cy.contains('ACRE')
|
||||
})
|
||||
it('should not have negative value', () => {
|
||||
cy.contains('€ / mois').click()
|
||||
cy.contains('€/mois').click()
|
||||
cy.get(inputSelector)
|
||||
.first()
|
||||
.type('{selectall}5000')
|
||||
|
@ -101,7 +102,7 @@ describe('Simulateurs', function() {
|
|||
)
|
||||
})
|
||||
|
||||
describe.only('Simulateur salarié', () => {
|
||||
describe('Simulateur salarié', () => {
|
||||
if (!fr) {
|
||||
return
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
{
|
||||
"name": "mon-entreprise",
|
||||
"license": "MIT",
|
||||
"version": "1.2.8",
|
||||
"description": "Library to compute the french social security contributions. Also a website that explains the calculations, and a generic engine to build cool forms that asks the question needed to compute an objective.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/betagouv/mon-entreprise.git",
|
||||
"directory": "mon-entreprise"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.16.1"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1% in FR",
|
||||
"not ie < 11"
|
||||
],
|
||||
"devDependencies": {
|
||||
"i18next-parser": "https://github.com/i18next/i18next-parser#master"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.3.4",
|
||||
"@rehooks/local-storage": "^2.1.1",
|
||||
"@sentry/browser": "5.15.5",
|
||||
"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",
|
||||
"js-yaml": "^3.13.1",
|
||||
"moo": "^0.5.0",
|
||||
"nearley": "^2.19.0",
|
||||
"publicodes": "file:../publicodes",
|
||||
"puppeteer": "^2.1.1",
|
||||
"ramda": "^0.27.0",
|
||||
"react": "^16.13.1",
|
||||
"react-color": "^2.14.0",
|
||||
"react-dom": "npm:@hot-loader/react-dom",
|
||||
"react-easy-emoji": "^1.2.0",
|
||||
"react-helmet": "6.0.0-beta",
|
||||
"react-i18next": "^11.0.0",
|
||||
"react-loading-skeleton": "^2.0.1",
|
||||
"react-markdown": "^4.1.0",
|
||||
"react-monaco-editor": "^0.36.0",
|
||||
"react-number-format": "^4.3.1",
|
||||
"react-redux": "^7.0.3",
|
||||
"react-router-dom": "^5.1.1",
|
||||
"react-router-hash-link": "^1.2.2",
|
||||
"react-spring": "=8.0.27",
|
||||
"react-syntax-highlighter": "^10.1.1",
|
||||
"react-to-print": "^2.5.1",
|
||||
"react-transition-group": "^2.2.1",
|
||||
"recharts": "^1.8.5",
|
||||
"reduce-reducers": "^1.0.4",
|
||||
"redux": "^4.0.4",
|
||||
"redux-sentry-middleware": "^0.1.8",
|
||||
"redux-thunk": "^2.3.0",
|
||||
"regenerator-runtime": "^0.13.3",
|
||||
"reselect": "^4.0.0",
|
||||
"styled-components": "^5.1.0",
|
||||
"swr": "^0.1.16",
|
||||
"whatwg-fetch": "^3.0.0",
|
||||
"yaml": "^1.9.2"
|
||||
},
|
||||
"scripts": {
|
||||
"prepare": "node scripts/prepare.js",
|
||||
"compile": "yarn run compile:prod && yarn run compile:legacy",
|
||||
"compile:prod": "yarn run webpack --config webpack.prod.js",
|
||||
"compile:legacy": "yarn run webpack --config webpack.prod.legacyBrowser.js",
|
||||
"compile:stats": "webpack --config webpack.prod.js --profile --json > stats.json",
|
||||
"compile:analyze-bundle": "ANALYZE_BUNDLE=1 yarn run compile",
|
||||
"compile:dev": "FR_SITE='http://localhost:5000${path}' EN_SITE='http://localhost:5001${path}' yarn run compile",
|
||||
"test": "yarn test:file \"./{,!(node_modules)/**/}!(webpack).test.js\"",
|
||||
"test:file": "yarn mocha-webpack --webpack-config ../webpack.test.js --require source-map-support/register --include test/componentTestSetup.js --require mock-local-storage --require test/helpers/browser.js",
|
||||
"test:bundlesize": "bundlesize",
|
||||
"test:dev-e2e:publicode": "cypress open --browser chromium --config baseUrl=http://localhost:8080/publicodes,integrationFolder=cypress/integration/publi.codes",
|
||||
"test:dev-e2e:mon-entreprise": "cypress open --browser chromium",
|
||||
"test:dev-e2e:mycompanyinfrance": "cypress open --browser chromium --config baseUrl=http://localhost:8080/infrance",
|
||||
"i18n:rules:check": "node scripts/i18n/check-missing-rule-translation.js",
|
||||
"i18n:rules:translate": "node scripts/i18n/translate-rules.js",
|
||||
"i18n:ui:check": "yarn run i18next -c scripts/i18n/parser.config.js && node scripts/i18n/check-missing-UI-translation",
|
||||
"i18n:ui:translate": "rm -rf source/locales/static-analysis-fr.json && yarn run i18next -c script/i18n/parser.config.js && node scripts/i18n/translate-ui.js",
|
||||
"start": "node source/server.js",
|
||||
"serve:dev": "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"
|
||||
}
|
||||
}
|
|
@ -10,9 +10,9 @@ const fs = require('fs')
|
|||
const path = require('path')
|
||||
const { readRules } = require('./rules')
|
||||
|
||||
const sourceDirPath = path.resolve(__dirname, '../rules')
|
||||
const sourceDirPath = path.resolve(__dirname, '../source/rules')
|
||||
// Note: we can't put the output file in the fs.watched directory
|
||||
const outPath = path.resolve(__dirname, '../types/dottednames.json')
|
||||
const outPath = path.resolve(__dirname, '../source/types/dottednames.json')
|
||||
|
||||
function persistJsonFileFromYaml() {
|
||||
const rules = readRules()
|
|
@ -50,12 +50,12 @@ module.exports = {
|
|||
// Namespace separator used in your translation keys
|
||||
// If you want to use plain english keys, separators such as `.` and `:` will conflict. You might want to set `keySeparator: false` and `namespaceSeparator: false`. That way, `t('Status: Loading...')` will not think that there are a namespace and three separator dots for instance.
|
||||
|
||||
output: 'source/locales/static-analysis-$LOCALE.json',
|
||||
output: '../../source/locales/static-analysis-$LOCALE.json',
|
||||
// Supports $LOCALE and $NAMESPACE injection
|
||||
// Supports JSON (.json) and YAML (.yml) file formats
|
||||
// Where to write the locale files relative to process.cwd()
|
||||
|
||||
input: './source/**/*.{jsx,tsx,js,ts}',
|
||||
input: '../../source/**/*.{jsx,tsx,js,ts}',
|
||||
// An array of globs that describe where to look for source files
|
||||
// relative to the location of the configuration file
|
||||
|
|
@ -117,7 +117,7 @@ function getRulesMissingTranslations() {
|
|||
|
||||
const getUiMissingTranslations = () => {
|
||||
const staticKeys = require(path.resolve(
|
||||
'source/locales/static-analysis-fr.json'
|
||||
'../../source/locales/static-analysis-fr.json'
|
||||
))
|
||||
const translatedKeys = parse(fs.readFileSync(UiTranslationPath, 'utf-8'))
|
||||
|
|
@ -5,7 +5,7 @@ const fs = require('fs')
|
|||
const path = require('path')
|
||||
const yaml = require('yaml')
|
||||
|
||||
const publicodesDir = path.resolve(__dirname, '../rules')
|
||||
const publicodesDir = path.resolve(__dirname, '../source/rules')
|
||||
|
||||
function concatenateFilesInDir(dirPath = publicodesDir) {
|
||||
return fs
|
|
@ -1,6 +1,6 @@
|
|||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const dataDir = path.resolve(__dirname, '../data/')
|
||||
const dataDir = path.resolve(__dirname, '../source/data/')
|
||||
|
||||
exports.createDataDir = () => {
|
||||
if (!fs.existsSync(dataDir)) {
|
|
@ -2,16 +2,15 @@ import { ThemeColorsProvider } from 'Components/utils/colors'
|
|||
import { SitePathProvider, SitePaths } from 'Components/utils/SitePathsContext'
|
||||
import { TrackerProvider } from 'Components/utils/withTracker'
|
||||
import { createBrowserHistory } from 'history'
|
||||
import { AvailableLangs } from 'i18n'
|
||||
import i18next from 'i18next'
|
||||
import React, { createContext, useEffect, useMemo } from 'react'
|
||||
import { I18nextProvider, useTranslation } from 'react-i18next'
|
||||
import { I18nextProvider } from 'react-i18next'
|
||||
import { Provider as ReduxProvider } from 'react-redux'
|
||||
import { Router } from 'react-router-dom'
|
||||
import reducers, { RootState } from 'Reducers/rootReducer'
|
||||
import { applyMiddleware, compose, createStore, Middleware, Store } from 'redux'
|
||||
import thunk from 'redux-thunk'
|
||||
import Tracker from 'Tracker'
|
||||
import Tracker from './Tracker'
|
||||
import { inIframe } from './utils'
|
||||
|
||||
declare global {
|
||||
|
@ -56,13 +55,13 @@ export type ProviderProps = {
|
|||
}
|
||||
|
||||
export default function Provider({
|
||||
tracker,
|
||||
tracker = new Tracker(),
|
||||
basename,
|
||||
sitePaths,
|
||||
reduxMiddlewares,
|
||||
initialStore,
|
||||
onStoreCreated,
|
||||
children
|
||||
children,
|
||||
sitePaths = {} as SitePaths
|
||||
}: ProviderProps) {
|
||||
const history = useMemo(
|
||||
() =>
|
||||
|
@ -118,9 +117,9 @@ export default function Provider({
|
|||
<ThemeColorsProvider
|
||||
color={iframeCouleur && decodeURIComponent(iframeCouleur)}
|
||||
>
|
||||
<TrackerProvider value={tracker!}>
|
||||
<TrackerProvider value={tracker}>
|
||||
<SiteNameContext.Provider value={basename}>
|
||||
<SitePathProvider value={sitePaths as any}>
|
||||
<SitePathProvider value={sitePaths}>
|
||||
<I18nextProvider i18n={i18next}>
|
||||
<Router history={history}>
|
||||
<>{children}</>
|
|
@ -2,7 +2,7 @@ import { SitePaths } from 'Components/utils/SitePathsContext'
|
|||
import { History } from 'history'
|
||||
import { RootState, SimulationConfig } from 'Reducers/rootReducer'
|
||||
import { ThunkAction } from 'redux-thunk'
|
||||
import { DottedName, Situation } from 'Rules'
|
||||
import { DottedName } from 'Rules'
|
||||
import { deletePersistedSimulation } from '../storage/persistSimulation'
|
||||
import { CompanyStatusAction } from './companyStatusActions'
|
||||
|
|
@ -2,7 +2,7 @@ import React from 'react'
|
|||
import emoji from 'react-easy-emoji'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { firstStepCompletedSelector } from 'Selectors/simulationSelectors'
|
||||
import Animate from 'Ui/animate'
|
||||
import Animate from 'Components/ui/animate'
|
||||
import './Banner.css'
|
||||
|
||||
type BannerProps = {
|
|
@ -3,7 +3,7 @@ import emoji from 'react-easy-emoji'
|
|||
import { animated, config, useSpring } from 'react-spring'
|
||||
import useDisplayOnIntersecting from 'Components/utils/useDisplayOnIntersecting'
|
||||
import { ThemeColorsContext } from 'Components/utils/colors'
|
||||
import { formatValue } from 'Engine/format'
|
||||
import { formatValue } from 'publicodes'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const ANIMATION_SPRING = config.gentle
|
|
@ -1,16 +1,15 @@
|
|||
import { goToQuestion, hideControl } from 'Actions/actions'
|
||||
import animate from 'Components/ui/animate'
|
||||
import { useControls, useInversionFail } from 'Components/utils/EngineContext'
|
||||
import { makeJsx } from 'Engine/evaluation'
|
||||
import React from 'react'
|
||||
import emoji from 'react-easy-emoji'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { RootState } from 'Reducers/rootReducer'
|
||||
import animate from 'Ui/animate'
|
||||
import { answeredQuestionsSelector } from 'Selectors/simulationSelectors'
|
||||
import './Controls.css'
|
||||
import { Markdown } from './utils/markdown'
|
||||
import { ScrollToElement } from './utils/Scroll'
|
||||
import { answeredQuestionsSelector } from 'Selectors/simulationSelectors'
|
||||
|
||||
export default function Controls() {
|
||||
const { t } = useTranslation()
|
||||
|
@ -48,12 +47,7 @@ export default function Controls() {
|
|||
<div className="control">
|
||||
{emoji(level == 'avertissement' ? '⚠️' : 'ℹ️')}
|
||||
<div className="controlText ui__ card">
|
||||
{message ? (
|
||||
<Markdown source={message} />
|
||||
) : (
|
||||
<span id="controlExplanation">{makeJsx(evaluated)}</span>
|
||||
)}
|
||||
|
||||
<Markdown source={message} />
|
||||
{solution && !answeredQuestions?.includes(solution.cible) && (
|
||||
<div>
|
||||
<button
|
|
@ -1,8 +1,7 @@
|
|||
import classnames from 'classnames'
|
||||
import { currencyFormat } from 'Engine/format'
|
||||
import React, { useMemo, useRef, useState } from 'react'
|
||||
import NumberFormat, { NumberFormatProps } from 'react-number-format'
|
||||
import { debounce } from '../../utils'
|
||||
import { debounce, currencyFormat } from '../../utils'
|
||||
import './CurrencyInput.css'
|
||||
|
||||
type CurrencyInputProps = NumberFormatProps & {
|
||||
|
@ -10,7 +9,7 @@ type CurrencyInputProps = NumberFormatProps & {
|
|||
debounce?: number
|
||||
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void
|
||||
currencySymbol?: string
|
||||
language?: Parameters<typeof currencyFormat>[0]
|
||||
language: string
|
||||
}
|
||||
|
||||
export default function CurrencyInput({
|
|
@ -64,7 +64,7 @@ export function DistributionBranch({
|
|||
<BarChartBranch
|
||||
value={value}
|
||||
maximum={maximum}
|
||||
title={<RuleLink {...branche} />}
|
||||
title={<RuleLink dottedName={dottedName} />}
|
||||
icon={icon ?? branche.icons}
|
||||
description={branche.summary}
|
||||
unit="€"
|
|
@ -1,6 +1,4 @@
|
|||
import Engine from 'Engine'
|
||||
import { formatValue } from 'Engine/format'
|
||||
import { EvaluatedNode } from 'Engine/types'
|
||||
import Engine, { EvaluatedNode, formatValue } from 'publicodes'
|
||||
import React, { useContext } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { DottedName } from 'Rules'
|
||||
|
@ -44,7 +42,7 @@ export default function Value({
|
|||
})
|
||||
if ('dottedName' in evaluation && linkToRule) {
|
||||
return (
|
||||
<RuleLink {...evaluation}>
|
||||
<RuleLink dottedName={evaluation.dottedName}>
|
||||
<span {...props}>{value}</span>
|
||||
</RuleLink>
|
||||
)
|
|
@ -1,7 +1,7 @@
|
|||
import { TrackerContext } from 'Components/utils/withTracker'
|
||||
import React, { useCallback, useContext, useState } from 'react'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { useLocation } from 'react-router'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import safeLocalStorage from '../../storage/safeLocalStorage'
|
||||
import './Feedback.css'
|
||||
import Form from './FeedbackForm'
|
|
@ -1,7 +1,7 @@
|
|||
import * as animate from 'Components/ui/animate'
|
||||
import { LinkButton } from 'Components/ui/Button'
|
||||
import FocusTrap from 'focus-trap-react'
|
||||
import React, { useEffect } from 'react'
|
||||
import * as animate from 'Ui/animate'
|
||||
import { LinkButton } from 'Ui/Button'
|
||||
import './Overlay.css'
|
||||
|
||||
type OverlayProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
@ -1,13 +1,12 @@
|
|||
import { useEvaluation, EngineContext } from 'Components/utils/EngineContext'
|
||||
import Value from 'Components/EngineValue'
|
||||
import { formatValue } from 'Engine/format'
|
||||
import RuleLink from 'Components/RuleLink'
|
||||
import { EngineContext, useEvaluation } from 'Components/utils/EngineContext'
|
||||
import { formatValue, ParsedRule, ParsedRules } from 'publicodes'
|
||||
import React, { Fragment, useContext } from 'react'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { DottedName } from 'Rules'
|
||||
import './PaySlip.css'
|
||||
import { Line, SalaireBrutSection, SalaireNetSection } from './PaySlipSections'
|
||||
import RuleLink from './RuleLink'
|
||||
import { ParsedRules, ParsedRule } from 'Rules'
|
||||
|
||||
export const SECTION_ORDER = [
|
||||
'protection sociale . santé',
|
||||
|
@ -105,7 +104,7 @@ export default function PaySlip() {
|
|||
return (
|
||||
<Fragment key={section.dottedName}>
|
||||
<h5 className="payslip__cotisationTitle">
|
||||
<RuleLink {...section} />
|
||||
<RuleLink dottedName={section.dottedName} />
|
||||
</h5>
|
||||
{cotisations.map(cotisation => (
|
||||
<Cotisation key={cotisation} dottedName={cotisation} />
|
||||
|
@ -150,8 +149,6 @@ export default function PaySlip() {
|
|||
}
|
||||
|
||||
function Cotisation({ dottedName }: { dottedName: DottedName }) {
|
||||
const parsedRules = useContext(EngineContext).getParsedRules()
|
||||
|
||||
const partSalariale = useEvaluation(
|
||||
'contrat salarié . cotisations . salariales'
|
||||
)?.formule.explanation.explanation.find(
|
||||
|
@ -168,7 +165,7 @@ function Cotisation({ dottedName }: { dottedName: DottedName }) {
|
|||
return (
|
||||
<>
|
||||
<RuleLink
|
||||
{...parsedRules[dottedName]}
|
||||
dottedName={dottedName}
|
||||
style={{ backgroundColor: 'var(--lightestColor)' }}
|
||||
/>
|
||||
<span style={{ backgroundColor: 'var(--lightestColor)' }}>
|
|
@ -1,10 +1,9 @@
|
|||
import Value, { Condition, ValueProps } from 'Components/EngineValue'
|
||||
import RuleLink from 'Components/RuleLink'
|
||||
import { EngineContext } from 'Components/utils/EngineContext'
|
||||
import Value, { ValueProps, Condition } from 'Components/EngineValue'
|
||||
import React, { useContext } from 'react'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { DottedName } from 'Rules'
|
||||
import { coerceArray } from '../utils'
|
||||
import RuleLink from './RuleLink'
|
||||
|
||||
export const SalaireBrutSection = () => {
|
||||
return (
|
|
@ -1,4 +1,4 @@
|
|||
import { formatValue } from 'Engine/format'
|
||||
import { formatValue } from 'publicodes'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { debounce as debounceFn } from '../utils'
|
|
@ -1,7 +1,6 @@
|
|||
import { updateUnit } from 'Actions/actions'
|
||||
import { parseUnit, serializeUnit } from 'Engine/units'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { targetUnitSelector } from 'Selectors/simulationSelectors'
|
||||
import './PeriodSwitch.css'
|
||||
|
@ -24,7 +23,9 @@ export default function PeriodSwitch() {
|
|||
onChange={() => dispatch(updateUnit(unit))}
|
||||
checked={currentUnit === unit}
|
||||
/>
|
||||
<span>{serializeUnit(parseUnit(unit), 1, language)}</span>
|
||||
<span>
|
||||
<Trans>{unit}</Trans>
|
||||
</span>
|
||||
</label>
|
||||
))}
|
||||
</span>
|
|
@ -3,7 +3,7 @@ import React from 'react'
|
|||
import { Trans } from 'react-i18next'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { RootState } from 'Reducers/rootReducer'
|
||||
import { LinkButton } from 'Ui/Button'
|
||||
import { LinkButton } from 'Components/ui/Button'
|
||||
import Banner from './Banner'
|
||||
import { firstStepCompletedSelector } from 'Selectors/simulationSelectors'
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import Engine, { RuleLink as EngineRuleLink } from 'publicodes'
|
||||
import React, { useContext } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { DottedName } from 'Rules'
|
||||
import { EngineContext } from './utils/EngineContext'
|
||||
import { SitePathsContext } from './utils/SitePathsContext'
|
||||
|
||||
export default function RuleLink(
|
||||
props: {
|
||||
dottedName: DottedName
|
||||
useDefaultValues?: boolean
|
||||
displayIcon?: boolean
|
||||
} & Omit<React.ComponentProps<Link>, 'to'>
|
||||
) {
|
||||
const sitePaths = useContext(SitePathsContext)
|
||||
const engine = useContext(EngineContext)
|
||||
return (
|
||||
<EngineRuleLink
|
||||
{...props}
|
||||
engine={engine}
|
||||
useDefaultValues={props.useDefaultValues ?? true}
|
||||
documentationPath={sitePaths.documentation.index}
|
||||
/>
|
||||
)
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
import { ParsedRule } from 'publicodes'
|
||||
import yaml from 'yaml'
|
||||
import React from 'react'
|
||||
import rules, { ParsedRule } from 'Rules'
|
||||
import PublicodeHighlighter from '../ui/PublicodeHighlighter'
|
||||
import rules from 'Rules'
|
||||
import PublicodeHighlighter from './ui/PublicodeHighlighter'
|
||||
|
||||
type RuleSourceProps = Pick<ParsedRule, 'dottedName'>
|
||||
|
|
@ -8,7 +8,7 @@ import emoji from 'react-easy-emoji'
|
|||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { RootState } from 'Reducers/rootReducer'
|
||||
import * as Animate from 'Ui/animate'
|
||||
import * as Animate from 'Components/ui/animate'
|
||||
import { answeredQuestionsSelector } from 'Selectors/simulationSelectors'
|
||||
|
||||
export default function SalaryExplanation() {
|
|
@ -8,7 +8,7 @@ import Conversation from 'Components/conversation/Conversation'
|
|||
import SeeAnswersButton from 'Components/conversation/SeeAnswersButton'
|
||||
import Value from 'Components/EngineValue'
|
||||
import dirigeantComparaison from 'Components/simulationConfigs/rémunération-dirigeant.yaml'
|
||||
import Engine from 'Engine'
|
||||
import Engine from 'publicodes'
|
||||
import revenusSVG from 'Images/revenus.svg'
|
||||
import {
|
||||
default as React,
|
||||
|
@ -22,7 +22,7 @@ import emoji from 'react-easy-emoji'
|
|||
import { Trans } from 'react-i18next'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { situationSelector } from 'Selectors/simulationSelectors'
|
||||
import InfoBulle from 'Ui/InfoBulle'
|
||||
import InfoBulle from 'Components/ui/InfoBulle'
|
||||
import './SchemeComparaison.css'
|
||||
import { EngineContext } from './utils/EngineContext'
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
import { ParsedRules } from 'publicodes'
|
||||
import React, { useContext, useEffect, useMemo, useState } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { DottedName } from 'Rules'
|
||||
import Worker from 'worker-loader!./SearchBar.worker.js'
|
||||
import RuleLink from './RuleLink'
|
||||
import './SearchBar.css'
|
||||
|
||||
const worker = new Worker()
|
||||
|
||||
type SearchBarProps = {
|
||||
rules: ParsedRules<DottedName>
|
||||
showDefaultList: 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({ rules, showDefaultList }: SearchBarProps) {
|
||||
const [input, setInput] = useState('')
|
||||
const [results, setResults] = useState<
|
||||
Array<{
|
||||
item: SearchItem
|
||||
matches: Matches
|
||||
}>
|
||||
>([])
|
||||
const { i18n } = useTranslation()
|
||||
|
||||
const searchIndex: Array<SearchItem> = useMemo(
|
||||
() =>
|
||||
Object.values(rules).map(rule => ({
|
||||
title:
|
||||
rule.title ??
|
||||
rule.name + (rule.acronyme ? ` (${rule.acronyme})` : ''),
|
||||
dottedName: rule.dottedName,
|
||||
espace: rule.dottedName.split(' . ').reverse()
|
||||
})),
|
||||
[rules]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
worker.postMessage({
|
||||
rules: searchIndex
|
||||
})
|
||||
|
||||
worker.onmessage = ({ data: results }) => setResults(results)
|
||||
return () => {
|
||||
worker.onmessage = null
|
||||
}
|
||||
}, [searchIndex, setResults])
|
||||
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type="search"
|
||||
css={`
|
||||
padding: 0.4rem;
|
||||
margin: 0.2rem 0;
|
||||
width: 100%;
|
||||
border: 1px solid var(--lighterTextColor);
|
||||
border-radius: 0.3rem;
|
||||
color: inherit;
|
||||
font-size: inherit;
|
||||
transition: border-color 0.1s;
|
||||
position: relative;
|
||||
|
||||
:focus {
|
||||
border-color: var(--color);
|
||||
}
|
||||
`}
|
||||
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 && !showDefaultList && !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;
|
||||
`}
|
||||
>
|
||||
{(showDefaultList && !results.length
|
||||
? searchIndex.map(item => ({ item, matches: [] }))
|
||||
: results
|
||||
)
|
||||
.slice(0, 6)
|
||||
.map(({ item, matches }) => (
|
||||
<li key={item.dottedName}>
|
||||
<RuleLink
|
||||
dottedName={item.dottedName}
|
||||
style={{ width: '100%', textDecoration: 'none' }}
|
||||
>
|
||||
<small>
|
||||
{item.espace
|
||||
.slice(1)
|
||||
.reverse()
|
||||
.map(name => (
|
||||
<span key={name}>
|
||||
{highlightMatches(
|
||||
name,
|
||||
matches.filter(
|
||||
m => m.key === 'espace' && m.value === name
|
||||
)
|
||||
)}{' '}
|
||||
›{' '}
|
||||
</span>
|
||||
))}
|
||||
</small>{' '}
|
||||
<br />
|
||||
{highlightMatches(
|
||||
item.title,
|
||||
matches.filter(m => m.key === 'title')
|
||||
)}
|
||||
</RuleLink>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
import Fuse from 'fuse.js'
|
||||
|
||||
let 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, '|')
|
||||
)
|
||||
]
|
||||
postMessage(results)
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ import { useSelector } from 'react-redux'
|
|||
import Overlay from './Overlay'
|
||||
import { EngineContext } from 'Components/utils/EngineContext'
|
||||
import SearchBar from './SearchBar'
|
||||
import { useLocation } from 'react-router'
|
||||
|
||||
type SearchButtonProps = {
|
||||
invisibleButton?: boolean
|
||||
|
@ -13,6 +14,7 @@ type SearchButtonProps = {
|
|||
export default function SearchButton({ invisibleButton }: SearchButtonProps) {
|
||||
const rules = useContext(EngineContext).getParsedRules()
|
||||
const [visible, setVisible] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (!(e.ctrlKey && e.key === 'k')) return
|
||||
|
@ -34,7 +36,7 @@ export default function SearchButton({ invisibleButton }: SearchButtonProps) {
|
|||
<h1>
|
||||
<Trans>Chercher dans la documentation</Trans>
|
||||
</h1>
|
||||
<SearchBar showDefaultList={false} finally={close} rules={rules} />
|
||||
<SearchBar showDefaultList={false} rules={rules} />
|
||||
</Overlay>
|
||||
) : invisibleButton ? null : (
|
||||
<button
|
|
@ -1,3 +1,4 @@
|
|||
import { setSimulationConfig } from 'Actions/actions'
|
||||
import Controls from 'Components/Controls'
|
||||
import Conversation, {
|
||||
ConversationProps
|
||||
|
@ -6,16 +7,15 @@ import SeeAnswersButton from 'Components/conversation/SeeAnswersButton'
|
|||
import PageFeedback from 'Components/Feedback/PageFeedback'
|
||||
import SearchButton from 'Components/SearchButton'
|
||||
import TargetSelection from 'Components/TargetSelection'
|
||||
import { useSimulationProgress } from 'Components/utils/useNextQuestion'
|
||||
import React, { useEffect } from 'react'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
import { firstStepCompletedSelector } from 'Selectors/simulationSelectors'
|
||||
import { useSimulationProgress } from 'Components/utils/useNextQuestion'
|
||||
import * as Animate from 'Ui/animate'
|
||||
import Progress from 'Ui/Progress'
|
||||
import { setSimulationConfig } from 'Actions/actions'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { useLocation } from 'react-router'
|
||||
import { SimulationConfig } from 'Reducers/rootReducer'
|
||||
import { firstStepCompletedSelector } from 'Selectors/simulationSelectors'
|
||||
import * as Animate from 'Components/ui/animate'
|
||||
import Progress from 'Components/ui/Progress'
|
||||
|
||||
type SimulationProps = {
|
||||
config: SimulationConfig
|
|
@ -1,6 +1,6 @@
|
|||
import RuleLink from 'Components/RuleLink'
|
||||
import useDisplayOnIntersecting from 'Components/utils/useDisplayOnIntersecting'
|
||||
import { EvaluatedRule, Evaluation, Types } from 'Engine/types'
|
||||
import { EvaluatedRule, Evaluation, Types } from 'publicodes'
|
||||
import React from 'react'
|
||||
import { animated, useSpring } from 'react-spring'
|
||||
import { DottedName } from 'Rules'
|
||||
|
@ -143,7 +143,7 @@ export default function StackedRulesChart({ data }: StackedRulesChartProps) {
|
|||
...rule,
|
||||
key: rule.dottedName,
|
||||
value: rule.nodeValue,
|
||||
legend: <RuleLink {...rule}>{capitalise0(rule.title)}</RuleLink>
|
||||
legend: <RuleLink dottedName={rule.dottedName} />
|
||||
}))}
|
||||
/>
|
||||
)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue