Studio : Support des exemples et menu déroulant

pull/913/head
Maxime Quandalle 2020-03-02 13:37:50 +01:00
parent 205b441634
commit 9278244e7e
8 changed files with 96 additions and 64 deletions

View File

@ -21,8 +21,8 @@
"dependencies": {
"@babel/polyfill": "^7.4.0",
"@babel/runtime": "^7.3.4",
"@monaco-editor/react": "^3.1.1",
"@rehooks/local-storage": "^2.1.1",
"@monaco-editor/react": "^2.3.0",
"classnames": "^2.2.5",
"color-convert": "^1.9.2",
"core-js": "^3.2.1",
@ -31,6 +31,7 @@
"i18next": "^18.0.1",
"iframe-resizer": "^4.1.1",
"js-yaml": "^3.13.1",
"monaco-editor": "^0.20.0",
"moo": "^0.5.0",
"nearley": "^2.19.0",
"ramda": "^0.25.0",
@ -63,6 +64,9 @@
"swr": "^0.1.16",
"whatwg-fetch": "^3.0.0"
},
"peerDependencies": {
"monaco-editor": "^0.20.0"
},
"scripts": {
"prepare": "node source/scripts/prepare.js",
"compile": "yarn run webpack --config source/webpack.prod.js && yarn run webpack --config source/webpack.prod.legacyBrowser.js",

View File

@ -42,7 +42,7 @@ if (
}
type ProviderProps = {
tracker: Tracker
tracker?: Tracker
basename: string
sitePaths: SitePaths
language: AvailableLangs
@ -92,7 +92,7 @@ export default class Provider extends PureComponent<ProviderProps> {
document.body.appendChild(css)
}
componentWillUnmount() {
this.props.tracker.disconnectFromHistory()
this.props.tracker?.disconnectFromHistory()
}
render() {
const iframeCouleur =
@ -105,7 +105,7 @@ export default class Provider extends PureComponent<ProviderProps> {
<ThemeColorsProvider
color={iframeCouleur && decodeURIComponent(iframeCouleur)}
>
<TrackerProvider value={this.props.tracker}>
<TrackerProvider value={this.props.tracker as Tracker}>
<SitePathProvider value={this.props.sitePaths}>
<I18nextProvider i18n={i18next}>
<Router history={this.history}>

View File

@ -1,5 +1,5 @@
import exemple1 from '!!raw-loader!./exemple1.yaml'
import exemple2 from '!!raw-loader!./exemple2.yaml'
import exemple1 from '!!raw-loader!./exemples/bareme-ir.yaml'
import exemple2 from '!!raw-loader!./exemples/douche.yaml'
import ColoredYaml from 'Components/rule/ColoredYaml'
import React, { useEffect } from 'react'
import emoji from 'react-easy-emoji'
@ -61,7 +61,9 @@ export default function Landing() {
<div className="ui__ card">
<ColoredYaml source={exemple1} />
</div>
<button className="ui__ button small">Lancer le calcul </button>
<Link to="/studio?exemple=bareme-ir" className="ui__ button small">
Lancer le calcul
</Link>
<p>
En plus du site Web, Mon-entreprise est disponible comme une{' '}
<a href="https://mon-entreprise.fr/intégration/bibliothèque-de-calcul">
@ -76,7 +78,9 @@ export default function Landing() {
<div className="ui__ card">
<ColoredYaml source={exemple2} />
</div>
<button className="ui__ button small ">Lancer le calcul </button>
<Link to="/studio?exemple=douche" className="ui__ button small">
Lancer le calcul
</Link>
<br />
</div>
)

View File

@ -1,18 +1,23 @@
import baremeIr from '!!raw-loader!./exemples/bareme-ir.yaml'
import douche from '!!raw-loader!./exemples/douche.yaml'
import { ControlledEditor } from '@monaco-editor/react'
import Engine from 'Engine/index'
import { buildFlatRules } from 'Engine/rules'
import { serializeUnit } from 'Engine/units'
import { safeLoad } from 'js-yaml'
import React, { useEffect, useState } from 'react'
import React, { useRef, useState } from 'react'
import emoji from 'react-easy-emoji'
import { useDispatch, useSelector } from 'react-redux'
import { analysisWithDefaultsSelector } from 'Selectors/analyseSelectors'
import { setSimulationConfig } from '../../actions/actions'
import { useLocation } from 'react-router'
import { Header } from './Header'
let initialInput = `
a:
let examples = {
'bareme-ir': baremeIr,
douche
}
let initialInput = `a:
formule: 10
b:
b:
formule: a + 18
c:
formule:
@ -34,34 +39,29 @@ export default function SafeStudio() {
)
}
export function Studio() {
const defaultTarget = 'b'
const [ready, setReady] = useState(false)
const [value, setValue] = useState(initialInput)
const [target, setTarget] = useState(defaultTarget)
const { search } = useLocation()
const currentExample = new URLSearchParams(search ?? '').get('exemple')
const [editorValue, setEditorValue] = useState(
currentExample && Object.keys(examples).includes(currentExample)
? examples[currentExample]
: initialInput
)
const [targets, setTargets] = useState<string[]>([])
const [currentTarget, setCurrentTarget] = useState('')
const [analysis, setAnalysis] = useState()
const engine = useRef<any>(null)
const dispatch = useDispatch()
try {
setTargets(Object.keys(safeLoad(editorValue) ?? {}))
} catch {}
const setRules = rulesString =>
dispatch({
type: 'SET_RULES',
rules: buildFlatRules(safeLoad(rulesString))
})
const setTargets = targets =>
dispatch(setSimulationConfig({ objectifs: targets }))
useEffect(() => {
setRules(initialInput)
setTargets([defaultTarget])
setReady(true)
}, [])
function onChange(ev, newValue) {
setValue(newValue)
}
function updateRules() {
setTargets([target])
setRules(value)
function updateResult() {
engine.current = new Engine.Engine(buildFlatRules(safeLoad(editorValue)))
engine.current.evaluate(
[targets.includes(currentTarget) ? currentTarget : targets[0]],
{ defaultUnits: [], situation: {} }
)
setAnalysis(engine.current.getLastEvaluationExplanations()?.targets?.[0])
}
return (
@ -70,34 +70,37 @@ export function Studio() {
<ControlledEditor
height="40vh"
language="yaml"
value={value}
onChange={onChange}
value={editorValue}
onChange={(ev, newValue) => setEditorValue(newValue ?? '')}
options={{ minimap: { enabled: false } }}
/>
<div>
<label htmlFor="objectif">Que voulez-vous calculer ? </label>
<input
id="objectif"
value={target}
onChange={e => setTarget(e.target.value)}
/>
<select
onChange={e => {
setCurrentTarget(e.target.value)
}}
>
{targets.map(target => (
<option key={target} value={target}>
{target}
</option>
))}
</select>
</div>
<button
css="display: block; margin-top: 1rem"
className="ui__ button small"
onClick={() => updateRules()}
onClick={() => updateResult()}
>
{emoji('▶️')} Mettre à jour
</button>
{ready && <Results />}
<Results analysis={analysis} />
</div>
)
}
export const Results = () => {
const analysis = useSelector(state => analysisWithDefaultsSelector(state))
?.targets[0]
console.log(analysis)
return (
export const Results = ({ analysis }) => {
return analysis ? (
<div>
<h2>Résultats</h2>
<ul>
@ -108,12 +111,12 @@ export const Results = () => {
<li>Applicable : {analysis.isApplicable ? '✅' : '❌'}</li>
</ul>
</div>
)
) : null
}
class ErrorBoundary extends React.Component {
state = {
error: false
error: false as false | { message: string }
}
static getDerivedStateFromError(error) {
// Mettez à jour l'état, de façon à montrer l'UI de repli au prochain rendu.
@ -123,7 +126,7 @@ class ErrorBoundary extends React.Component {
render() {
return (
<>
{this.state.error && (
{this.state.error ? (
<p css="max-height: 4rem; overflow: hidden; border: 3px solid red;">
Erreur :{' '}
{
@ -132,8 +135,9 @@ class ErrorBoundary extends React.Component {
)[0]
}
</p>
) : (
this.props.children
)}
{this.props.children}
</>
)
}

View File

@ -1,6 +1,8 @@
douche:
icônes: 🚿
douche . impact par douche:
titre: Une douche
icônes: 🚿
formule: impact par litre * litres d'eau
douche . impact par litre:
@ -75,6 +77,9 @@ chauffage . impact par litre:
titre: impact par litre chauffé
formule: impact par kWh * énergie consommée par litre
eau:
icônes: 🌊
eau . impact par litre froid:
unité: kgCO₂e/l
formule: eau potable + traitement eau usée

View File

@ -98,3 +98,13 @@ Multiplication complète:
# valeur attendue: 0
# variables manquantes: []
a:
formule:
multiplication:
assiette: 1000
taux: 3%
b:
formule: a
exemples:
- valeur attendue: 30

View File

@ -1039,10 +1039,10 @@
"@types/istanbul-reports" "^1.1.1"
"@types/yargs" "^13.0.0"
"@monaco-editor/react@^2.3.0":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@monaco-editor/react/-/react-2.3.0.tgz#ef1e09c408bb3119e2dc0d369b132e82b2d3ade7"
integrity sha512-jmmZCQ4iSMfwelWGRV4HWUOJchkN4MOx9vhx1DWvC6ERpvK5DXrcwJBBzuayVtiK7cyCVOGKB7mgjT7KOdUkJw==
"@monaco-editor/react@^3.1.1":
version "3.1.1"
resolved "https://registry.yarnpkg.com/@monaco-editor/react/-/react-3.1.1.tgz#35338643f8350d37f508596b02970bcc2ecf8ea9"
integrity sha512-c2XMTEu5SxOahcmvZ6AorDZLWrl3Q6Cax4G2fr5rC55jCYN2b8hpjXu8BlH/Q3blKHEk8e/bahF/i2GeQukLzA==
"@prerenderer/prerenderer@^0.7.2":
version "0.7.2"
@ -7366,6 +7366,11 @@ moment@2.24.0:
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
monaco-editor@^0.20.0:
version "0.20.0"
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.20.0.tgz#5d5009343a550124426cb4d965a4d27a348b4dea"
integrity sha512-hkvf4EtPJRMQlPC3UbMoRs0vTAFAYdzFQ+gpMb8A+9znae1c43q8Mab9iVsgTcg/4PNiLGGn3SlDIa8uvK1FIQ==
moo@^0.4.3:
version "0.4.3"
resolved "https://registry.yarnpkg.com/moo/-/moo-0.4.3.tgz#3f847a26f31cf625a956a87f2b10fbc013bfd10e"