simulateurs.yaml -> simulateurs à générer; refonte de l'introduction au [cdd]

pull/6/head
mama 2017-04-18 19:37:38 +02:00
parent 7e1cb62385
commit a87c98fad3
29 changed files with 521 additions and 127 deletions

View File

@ -20,11 +20,11 @@
"nearley": "^2.7.14",
"npm": "^4.4.1",
"ramda": "^0.23.0",
"react": "^15.5.3",
"react-dom": "^15.5.3",
"react": "^15.5.4",
"react-dom": "^15.5.4",
"react-json-tree": "^0.10.0",
"react-redux": "^5.0.3",
"react-router": "^3.0.2",
"react-router-dom": "^4.1.1",
"reduce-reducers": "^0.1.2",
"redux": "^3.6.0",
"redux-form": "^6.4.3",

View File

@ -3,9 +3,19 @@
- Variable: CDD
attache: Salariat
nom complet: CDD
description: Contrat de travail pour lequel un employeur (société, entreprise) peut recruter directement un salarié pour une durée déterminée, car la cause de cette détermination, de la date ou échéance de fin de contrat est prévue explicitement par le Code du travail.
description: Contrat de travail pour lequel un employeur peut recruter un salarié pour une durée déterminée, car la cause de cette détermination, de la date ou échéance de fin de contrat est prévue explicitement par le Code du travail.
référence: https://fr.wikipedia.org/wiki/Contrat_de_travail_%C3%A0_dur%C3%A9e_d%C3%A9termin%C3%A9e_en_France
# TODO: règle de type : il faut q'un motif et une durée soient sélectionnés pour qu'un contrat soit un CDD. Cela revient à dire que les variables CDD et motif sont obligatoires *dans le contexte* de leur attache
# implique:
# - emploi temporaire
# - motif de recours
# - Variable: emploi temporaire
# attache: Salariat . CDD
# description: Le contrat n'a ni pour objet ni pour effet de pourvoir durablement un emploi lié à l'activité normale et permanente de l'entreprise.
# références:
# Code du travail - Article L1242-1 : https://www.legifrance.gouv.fr/affichCodeArticle.do;jsessionid=BF6DDD5AADF2010EFD3ED78B7CC00691.tpdila20v_1?idArticle=LEGIARTI000006901194&cidTexte=LEGITEXT000006072050&dateTexte=20100508
- Variable: durée contrat
attache: Salariat . CDD
@ -22,6 +32,7 @@
6 mois: 6
3 mois: 3
- Variable: motif # alias utilisé dans l'article L. 1242-1
nom complet: motif de recours
attache: Salariat . CDD
@ -94,6 +105,10 @@
références:
service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F33693
# concerne: emploi temporaire par nature
notes: |
- exprimer la justification du CDD d'usage au delà des secteurs.
" l'usage exclut le recours au CDI en raison de la nature de l'activité et du caractère temporaire de ces emplois."
+ interdictions explicites (grève et travaux dangereux)
formule-futur: #TODO intégrer ça dans le formulaire. Comment ?
contrainte:
@ -125,8 +140,8 @@
titre: Événement de contrat
question: Pensez-vous être confronté à l'un de ces événements au cours du contrat ?
description: |
Certains de ces événements impactent fortement les objectifs de la simulation.
Par exemple, si le CDD est poursuivi en CDI, aucune majoration ou indemnité ne sera à verser !
Certains de ces événements impactent fortement les résultats de la simulation, et peuvent donc la racourcir.
Par exemple, dans l'hypothèse d'un CDD poursuivi en CDI, aucune majoration ou indemnité ne sera à verser !
# au lieu de lister tous les cas, l'alternative est de simplement indiquer qu'ils sont exclusifs,
# et les identifier dynamiquement par leur attribut "attache" :
langue au chat possible: oui

30
règles/simulateurs.yaml Normal file
View File

@ -0,0 +1,30 @@
# Les simulateurs sont définis ici. Ils ont une variable comme objectif de calcul. Le moteur va analyser cette variable et son arbre pour produire un formulaire. Il sera exposé sur /simulateurs/:id
- titre: Simulateur CDD
sous-titre: Découvrir le surcoût employeur du CDD par rapport au CDI
id: cdd
introduction:
- icône: fa-handshake-o
texte: Vous avez embauché ou vous réfléchissez à l'embauche d'un salarié en CDD.
titre: Votre situation
- icône: fa-balance-scale
texte: Votre contrat ne peut donc avoir ni pour objet ni pour effet de pourvoir durablement un emploi lié à l'activité normale et permanente de l'entreprise.
titre: Votre obligation
action:
texte: Répondez à quelques questions pour estimer le montant des 4 obligations du CDD.
bouton: C'est parti !
objectif: surcoût CDD
conclusion: |
Nous n'avons plus de questions : votre simulation est terminée.
Cliquez sur les obligations en bas pour comprendre vos résultats.
Une remarque ? [Écrivez-nous](mailto:contact@embauche.beta.gouv.fr) !
# <i
# style={{cursor: 'pointer'}}
# className="fa fa-envelope-o"
# />
# - titre: Du brut au net

View File

@ -16,7 +16,7 @@ p {
#conversation {
margin: 1em auto;
margin: 3em auto;
font-size: 110%;
line-height: normal;
display: flex;
@ -24,6 +24,7 @@ p {
align-items: flex-start;
min-height: 10em;
max-width: 90%;
max-height: 85%;
}
@ -34,25 +35,11 @@ p {
text-align: center;
font-style: italic;
}
#fin p:first-of-type {
font-weight: bold
}
#questions-answers {
min-width: 60%;
min-width: 55%;
transition: width 1s;
}
#sim #results {
padding: .1em;
background: #333350;
box-shadow: 1px -7px 20px 2px #ccc;
position: fixed;
bottom: 0;
left: 0;
width: 100%;
font-size: 80%;
color: white;
height: 12em;
text-align: center;
}

View File

@ -1,4 +1,4 @@
#CDDIntro {
#simulateurIntro {
background: radial-gradient(ellipse at center, #4A89DC -160%,#333350 100%);
color: white;
position: fixed;
@ -8,24 +8,24 @@
height: 100%;
}
#CDDIntro h1 {
#simulateurIntro h1 {
margin-top: 3%;
text-align: center;
font-size: 350%;
font-weight: 800;
}
#CDDIntro .subtitle {
#simulateurIntro .subtitle {
text-align: center;
font-size: 110%;
font-weight: 300;
}
#CDDIntro #introduction {
#simulateurIntro {
margin-top: 4%;
}
#CDDIntro p {
#simulateurIntro p {
font-size: 120%;
color: white;
opacity: .9;
@ -33,19 +33,19 @@
margin: 2em auto;
}
#CDDIntro #introduction .insist {
#simulateurIntro #introduction .insist {
font-weight: 600;
/*font-style: italic;*/
}
#CDDIntro #action {
#simulateurIntro #action {
color: white;
margin-top: 3%;
display: block;
text-align: center;
}
#CDDIntro #action button {
#simulateurIntro #action button {
color: inherit;
background: #4A89DC;
padding: .6em 1.2em;
@ -55,7 +55,7 @@
border: none;
box-shadow: 0px 9px 14px 0px rgba(0, 0, 0, 0.1)
}
#CDDIntro #action button:hover {
#simulateurIntro #action button:hover {
box-shadow: none;
opacity: .95;
}

View File

@ -2,14 +2,14 @@ import React from 'react'
import colours from './themeColours'
import PageTypeIcon from './PageTypeIcon'
import './CDDIntro.css'
import {Link} from 'react-router'
import {Link} from 'react-router-dom'
export default () => (
<div id="CDDIntro">
<PageTypeIcon type="simulation" />
<h1>Simulateur CDD</h1>
<div className="subtitle">
Découvrir le surcoût pour l'employeur du CDD par rapport au CDI
Découvrir le surcoût employeur du CDD par rapport au CDI
</div>
<section id="introduction">
<p>

View File

@ -0,0 +1,10 @@
import React from 'react'
export default () =>
<div style={{color: '#333350', margin: '15% auto', width: '20em', textAlign: 'center'}}>
<p style={{fontWeight: 'bold'}}>
Pour nous écrire : contact@embauche.beta.gouv.fr
</p>
{/* TODO: credits for the image to add: https://thenounproject.com/search/?q=post+card&i=715677 */}
<img style={{margin: '3%'}} width="200px" src={require('../images/contact.png')} />
</div>

View File

@ -1,4 +1,23 @@
#results {
padding: .1em;
background: #333350;
font-size: 80%;
color: white;
padding-top: 2em;
text-align: center;
width: 100%;
position: fixed;
bottom: 0;
left: 0;
border-top: 3px solid white;
}
#results.started {
border: none;
box-shadow: 1px -7px 20px 2px #ccc;
}
#results #results-titles {
color: white;
width: 18%;
@ -42,6 +61,7 @@
border-radius: 3px;
white-space: nowrap;
color: #333350;
min-width: 15em;
}
#results li.irrelevant .rule-box {
background: rgba(255, 255, 255, 0.65);

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react'
import classNames from 'classnames'
import {Link} from 'react-router'
import {Link} from 'react-router-dom'
import {connect} from 'react-redux'
import R from 'ramda'
import './Results.css'
@ -10,21 +10,24 @@ let humanFigure = decimalDigits => value => fmt(value.toFixed(decimalDigits))
@connect(
state => ({
pointedOutObjectives: state.pointedOutObjectives
pointedOutObjectives: state.pointedOutObjectives,
analysedSituation: state.analysedSituation,
conversationStarted: !R.isEmpty(state.form)
})
)
export default class Results extends Component {
render() {
let {analysedSituation, pointedOutObjectives} = this.props
let {analysedSituation, pointedOutObjectives, conversationStarted} = this.props
// On travaille pour l'instant sur un objectif qui est une somme de plusieurs variables, et c'est ces variables que nous affichons comme résultats. D'où ce chemin :
let explanation = R.path(['formule', 'explanation', 'explanation'])(analysedSituation)
if (!explanation) return null
return (
<section id="results">
<section id="results" className={classNames({started: conversationStarted})}>
<div id="results-titles">
<h2>Vos obligations</h2>
<div>Cliquez pour comprendre chaque calcul &nbsp;<i className="fa fa-hand-o-right" aria-hidden="true"></i></div>
{conversationStarted &&
<div>Cliquez pour comprendre chaque calcul &nbsp;<i className="fa fa-hand-o-right" aria-hidden="true"></i></div>}
</div>
<ul>
{explanation.map(
@ -47,12 +50,13 @@ export default class Results extends Component {
{name}
</div>
<p>
{irrelevant ?
"Vous n'êtes pas concerné"
: unsatisfied ?
'En attente de vos réponses...'
: <span className="figure">{humanFigure(2)(computedValue) + '€'}</span>
}
{conversationStarted && (
irrelevant ?
"Vous n'êtes pas concerné"
: unsatisfied ?
'En attente de vos réponses...'
: <span className="figure">{humanFigure(2)(computedValue) + '€'}</span>
)}
</p>
</div>
</Link>

View File

@ -0,0 +1,14 @@
import React from 'react'
import {Link} from 'react-router-dom'
export default () =>
<div style={{color: '#333350', margin: '15% auto', width: '15em', textAlign: 'center'}}>
<h2>
On peut facilement se perdre dans la législation...
</h2>
<Link to="/">
{/* TODO: credits for the image to add: https://thenounproject.com/term/treasure-map/96666/ */}
<img style={{margin: '3%'}} width="250px" src={require('../images/map-directions.png')} />
Revenir en lieu sûr
</Link>
</div>

View File

@ -1,5 +1,7 @@
#rule {
font-size: 110%;
height: 100%;
padding-bottom: 10%;
}
#rule pre {
@ -125,16 +127,23 @@
margin-top: 3em;
width: 100%;
}
.dictionaryWrapper {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
.dictionaryPanelWrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.86);
z-index: 1;
}
.dictionaryPanel {
background: white;
position: fixed;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
width: 33%;
border: 1px dashed #aaa;
border: 2px solid #333350;
margin: 1em;
padding: 0em 1.5em;
min-height: 6em;

View File

@ -36,7 +36,7 @@ export default class Rule extends Component {
}
render() {
let {
params: {name},
match: {params: {name}},
analysedSituation,
form
} = this.props,
@ -131,8 +131,13 @@ let AttachDictionary = dictionary => Decorated =>
<div className="dictionaryWrapper">
<Decorated ref={decorated => this.decorated = decorated} {...this.props} explain={this.explain}/>
{explanation &&
<div className="dictionaryPanel" dangerouslySetInnerHTML={{__html: this.renderExplanationMarkdown(explanation, term)}}>
<div className="dictionaryPanelWrapper" onClick={() => this.setState({term: null, explanation: null})}>
<div className="dictionaryPanel"
onClick={e => {e.preventDefault(); e.stopPropagation()}}
dangerouslySetInnerHTML={{__html: this.renderExplanationMarkdown(explanation, term)}}>
</div>
</div>
}
</div>
)

View File

@ -0,0 +1,97 @@
#sim {
padding: 3em 0; /* For the warning message */
background-color: #333350;
/*background-image: radial-gradient(ellipse at center, white -160%, rgba(255,255,255,0) 100%);*/
background-image: radial-gradient(ellipse at center, #4A89DC -160%,#333350 70%);
color: white;
transition: background-color .5s;
height: 100%;
padding-bottom: 10%;
}
#sim p {
color: inherit;
}
#sim.started {
background: none;
background-color: white;
color: #333350;
}
#sim > h1 {
color: inherit;
margin-top: 0;
text-align: center;
font-size: 350%;
font-weight: 800;
}
#simSubtitle {
margin-top: -.5em;
text-align: center;
font-size: 110%;
font-weight: 300;
margin-bottom: 2%;
}
#sim .centered {
width: 40%;
margin: 0 auto;
}
#sim .intro {
font-style: italic;
font-size: 100%;
margin-bottom: 4%;
}
#sim .intro > div {
margin: 1em 0;
display: flex;
align-items: center;
}
#sim .intro i {
opacity: .9;
font-size: 250%;
margin-right: .6em;
}
#sim .intro span {
display: inline-block;
width: 80%;
}
#sim .remarks p {
opacity: .9;
padding-left: 1em;
border-left: 10px solid rgba(255, 255, 255, 0.2);
font-style: italic;
}
#sim .action {
margin-top: 5%;
margin-bottom: 3%;
}
#sim .action p {
text-align: center;
}
#sim .action button {
color: white;
display: block;
text-align: center;
color: inherit;
background: #4A89DC;
padding: .6em 1.2em;
font-size: 140%;
margin: 1em auto;
width: 12em;
border: none;
box-shadow: 0px 9px 14px 0px rgba(0, 0, 0, 0.1)
}
#sim .action:hover {
box-shadow: none;
opacity: .95;
}

View File

@ -0,0 +1,136 @@
import React, {Component} from 'react'
import './CDD.css'
import {reduxForm, formValueSelector} from 'redux-form'
import {connect} from 'react-redux'
import './conversation/conversation.css'
import {START_CONVERSATION} from '../actions'
import Aide from './Aide'
import PageTypeIcon from './PageTypeIcon'
import simulateurs from '../../règles/simulateurs.yaml'
import R from 'ramda'
import {Redirect, Link} from 'react-router-dom'
import {createMarkdownDiv} from '../engine/marked'
import './Simulateur.css'
import classNames from 'classnames'
let situationSelector = formValueSelector('conversation')
@reduxForm({form: 'conversation', destroyOnUnmount: false})
@connect(
state => ({
situation: variableName => situationSelector(state, variableName),
foldedSteps: state.foldedSteps,
unfoldedSteps: state.unfoldedSteps,
themeColours: state.themeColours,
analysedSituation: state.analysedSituation,
}),
dispatch => ({
startConversation: rootVariable => dispatch({type: START_CONVERSATION, rootVariable}),
})
)
export default class extends React.Component {
componentWillMount() {
let {
match: {
params: {
simulateurId
}
}
} = this.props
this.simulateur = R.find(R.propEq('id', simulateurId))(simulateurs)
// C'est ici que la génération du formulaire, et donc la traversée des variables commence
if (this.simulateur)
this.props.startConversation(this.simulateur.objectif)
}
state = {
started: null
}
render(){
if (!this.simulateur) return <Redirect to="/404"/>
let
{foldedSteps, unfoldedSteps, situation} = this.props,
sim = path =>
R.path(R.unless(R.is(Array), R.of)(path))(this.simulateur)
,
{started} = this.state
return (
<div id="sim" className={classNames({started})}>
<PageTypeIcon type="simulation" />
<h1>{sim('titre')}</h1>
<div id="simSubtitle">{sim('sous-titre')}</div>
<div className="intro centered">
{sim('introduction').map( ({icône, texte, titre}) =>
<div key={titre}>
<i title={titre} className={"fa "+icône} aria-hidden="true"></i>
<span>
{texte}
</span>
</div>
)}
</div>
{
// Tant que le bouton 'C'est parti' n'est pas cliqué, on affiche l'intro
!this.state.started ?
<div>
<div className="action centered">
<p>{sim(['action', 'texte'])}</p>
<button onClick={() => this.setState({started: true})}>
{sim(['action', 'bouton'])}
</button>
</div>
<div className="remarks centered">
<p>
Pour simplifier, les résultats sont calculés par mois de contrat.
</p>
<p>
N'hésitez pas à <Link to="/contact">nous écrire</Link> ! La loi française est complexe, car très ciblée. Nous pouvons la rendre plus transparente.
</p>
</div>
</div>
: (
<div>
<div id="conversation">
<div id="questions-answers">
{ !R.isEmpty(foldedSteps) &&
<div id="foldedSteps">
{foldedSteps
.map(step => (
<step.component
key={step.name}
{...step}
step={step}
answer={situation(step.name)}
/>
))}
</div>
}
<div id="unfoldedSteps">
{ !R.isEmpty(unfoldedSteps) && do {
let step = R.head(unfoldedSteps)
;<step.component
key={step.name}
{...step}
step={step}
unfolded={true}
answer={situation(step.name)}
/>
}}
</div>
{unfoldedSteps.length == 0 &&
<div id="fin">
<img src={require('../images/fin.png')} />
{createMarkdownDiv(sim('conclusion'))}
</div>}
</div>
<Aide />
</div>
</div>
)}
</div>
)
}
}

View File

@ -8,8 +8,6 @@ import {START_CONVERSATION} from '../actions'
import Aide from './Aide'
import PageTypeIcon from './PageTypeIcon'
//TODO fusionner SimulationCDD & SimulationNet
let situationSelector = formValueSelector('conversation')
@reduxForm({form: 'conversation', destroyOnUnmount: false})
@ -66,7 +64,11 @@ export default class CDD extends Component {
<img src={require('../images/fin.png')} />
<p>
Nous n'avons plus de questions : votre simulation est terminée.
</p><p>
</p>
<p>
Cliquez sur les obligations en bas pour comprendre vos résultats.
</p>
<p>
Une remarque ? &nbsp;
<a href="mailto:contact@embauche.beta.gouv.fr">
Écrivez-nous

View File

@ -5,9 +5,11 @@
border-radius: .2em;
display: inline-block;
margin-left: .8em;
opacity: .4;
opacity: .35;
border: 1px solid transparent;
cursor: pointer;
vertical-align: baseline;
font-size: 85%;
}
.explicable:hover .icon {
@ -15,7 +17,7 @@
border-color: white;
}
.explicable.dark:hover .icon {
border-color: #222;
border-color: #333350;
}
.explicable .icon:hover {
@ -24,7 +26,7 @@
color: #4A89DC;
}
.explicable.dark .icon:hover {
background: #222;
background: #333350;
color: white;
}
@ -34,6 +36,6 @@
color: white;
}
.explicable.dark.explained .icon {
background: #222;
background: #333350;
color: white;
}

View File

@ -35,7 +35,8 @@ export default class Explicable extends React.Component {
<span
className="icon"
onClick={e => {e.preventDefault(); e.stopPropagation(); explain(name)}}>
{ hover ? '' : '•' }</span>
<i className="fa fa-info" aria-hidden="true"></i>
</span>
</span>
)
}

View File

@ -81,7 +81,7 @@
}
.step-question {
margin-bottom: 1em;
margin-bottom: 2em;
}
.step h1 {

View File

@ -2,9 +2,7 @@ import React, {Component} from 'react'
import { Provider } from 'react-redux'
import DevTools from '../DevTools'
import routes from '../routes'
import {Router, browserHistory} from 'react-router'
import Layout from './Layout'
export default class App extends Component {
render() {
@ -12,7 +10,7 @@ export default class App extends Component {
return (
<Provider store={store}>
<div>
<Router routes={routes} history={browserHistory} />
<Layout />
<DevTools />
</div>
</Provider>

View File

@ -1,15 +1,18 @@
// App.dev without the redux DevTools
import React, {Component} from 'react'
import { Provider } from 'react-redux'
import routes from '../routes'
import {Router} from 'react-router'
import DevTools from '../DevTools'
import Layout from './Layout'
export default class App extends Component {
render() {
const { store } = this.props
return (
<Provider store={store}>
<Router routes={routes} history={history} />
<div>
<Layout />
</div>
</Provider>
)
}

View File

@ -1,7 +1,7 @@
import React, {Component} from 'react'
import './Home.css'
import {searchRules} from '../engine/rules.js'
import {Link} from 'react-router'
import {Link} from 'react-router-dom'
import '../components/Rule.css'
import R from 'ramda'
@ -60,11 +60,11 @@ export default class Home extends Component {
<ul>
<li key="cdd">
<span className="simulateur">Surcoût du CDD</span>
<Link to="/cdd-intro"><button>Essayer</button></Link>
<Link to="/simulateurs/cdd"><button>Essayer</button></Link>
</li>
<li key="embauche">
<span className="simulateur">Prix global de l'embauche</span>
<Link><button className="disabled">Bientôt disponible</button></Link>
<a href="#"><button className="disabled">Bientôt disponible</button></a>
</li>
</ul>
</section>

View File

@ -1,5 +1,7 @@
body {
font-family: 'Open Sans';
margin: 0;
height: 100%;
}
#content {
@ -13,6 +15,10 @@ body {
top: 4.5%;
}
a {
color: inherit;
}
h1 {
margin: 6% 3% 0;
font-size: 250%;
@ -31,6 +37,18 @@ h1 {
text-align: center;
z-index: 1000000;
}
#main {
min-height: 100vh;
width: 100%;
display: flex;
flex-direction: column;
}
#ninetyPercent {
width: 100%;
flex-grow: 1;
overflow: auto;
}
#page-type {
display: inline-block;

View File

@ -1,27 +1,42 @@
import React, { Component } from 'react'
import './Layout.css'
import './reset.css'
import {Link} from 'react-router'
import {Link, Route, BrowserRouter as Router, Switch} from 'react-router-dom'
import Home from './Home'
import Rule from '../components/Rule'
import Route404 from '../components/Route404'
import Contact from '../components/Contact'
import Simulateur from '../components/Simulateur'
import Results from '../components/Results'
export default class Layout extends Component {
render() {
return (<div>
<div id="header">
<div id="warning">
Attention ! Tout le contenu de ce site est hautement expérimental.
return (
<Router>
<div id="main">
<div id="ninetyPercent">
<div id="header">
<div id="warning">
Attention ! Ce site est expérimental.
</div>
{
// this.props.location.pathname != '/' &&
// <Link to="/">
// <img id="site-logo" src={require('../images/logo.png')} />
// </Link>
}
</div>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/contact" component={Contact} />
<Route path="/regle/:name" component={Rule} />
<Route path="/simulateurs/:simulateurId" component={Simulateur} />
<Route component={Route404} />
</Switch>
</div>
<Results />
</div>
{
this.props.location.pathname != '/' &&
<Link to="/">
<img id="site-logo" src={require('../images/logo.png')} />
</Link>
}
</div>
<div id="content">
{this.props.children}
</div>
</div>
</Router>
)
}
}

View File

@ -32,7 +32,7 @@ taux:
multiplication:
description: |
C'est une multiplication un peu améliorée, très utile pour exprimer les cotisations, souvent linéaires.
C'est une multiplication un peu améliorée, très utile pour exprimer les cotisations.
Sa propriété `assiette` est multipliée par un pourcentage, `taux`, ou par un `facteur` quand ce nom est plus approprié.

View File

@ -1,4 +1,5 @@
import marked from 'marked'
import React from 'react'
let customMarked = new marked.Renderer()
customMarked.link = ( href, title, text ) =>
@ -7,4 +8,8 @@ marked.setOptions({
renderer: customMarked
})
export let createMarkdownDiv = markdown =>
<div dangerouslySetInnerHTML={{__html: marked(markdown)}}></div>
export default marked

View File

@ -2,7 +2,7 @@ import React from 'react'
import R from 'ramda'
import classNames from 'classnames'
let treatValue = data => console.log('data', data) ||
let treatValue = data =>
data == null
? '?'
: !isNaN(data) ? Math.round(+data) : data ? 'oui' : 'non'

BIN
source/images/contact.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

56
source/images/contact.svg Normal file
View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 100 125"
enable-background="new 0 0 100 100"
xml:space="preserve"
id="svg12"
sodipodi:docname="contact.svg"
inkscape:version="0.92.1 r15371"><metadata
id="metadata18"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs16" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1046"
id="namedview14"
showgrid="false"
inkscape:zoom="1.888"
inkscape:cx="-1.9067797"
inkscape:cy="62.5"
inkscape:window-x="122"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg12" /><path
d="M93.1,35.5l-9.3-7v-9.6c0-2.5-2-4.5-4.5-4.5H65l-6.2-4.6c-5.2-3.9-12.4-3.9-17.6,0L35,14.4H20.7c-2.5,0-4.5,2-4.5,4.5v9.6 l-9.3,7c-1.2,0.9-2,2.4-2,3.9v48.9c0,2.7,2.2,4.9,4.9,4.9h80.3c2.7,0,4.9-2.2,4.9-4.9V39.4C95,37.8,94.3,36.4,93.1,35.5z M79.3,36.9 v6.2L50,65.1L20.7,43.1v-6.2v-18h58.5V36.9z"
id="path2"
style="fill:#333350;fill-opacity:1" /><rect
x="27.5"
y="24.6"
width="45"
height="6.8"
id="rect4"
style="fill:#4a89dc;fill-opacity:1" /><rect
x="27.5"
y="35.8"
width="36"
height="6.8"
id="rect6"
style="fill:#4a89dc;fill-opacity:1" /></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1,33 +0,0 @@
import React from 'react'
import { Route, IndexRoute, Link } from 'react-router'
import Layout from './containers/Layout'
import Home from './containers/Home'
import Rule from './components/Rule'
import SimulationCDD from './components/SimulationCDD'
import SimulationNet from './components/SimulationNet'
import CDDIntro from './components/CDDIntro'
let RouteNotFound = () =>
<div style={{color: '#333350', margin: '15% auto', width: '15em', textAlign: 'center'}}>
<h2>
On peut facilement se perdre dans la législation...
</h2>
<Link to="/">
{/* TODO: credits for the image to add: https://thenounproject.com/term/treasure-map/96666/ */}
<img style={{margin: '3%'}} width="250px" src={require('./images/map-directions.png')} />
Revenir en lieu sûr
</Link>
</div>
export default (
<Route path="/" component={Layout}>
<Route path="regle/:name" component={Rule} />
<Route path="cdd-intro" component={CDDIntro} />
<Route path="cdd" component={SimulationCDD} />
<Route path="net" component={SimulationNet} />
<IndexRoute component={Home} />
<Route path="*" component={RouteNotFound} />
</Route>
)