🐛 Les saisies numériques ont maintenant le bon format

Solution pratique mais pas élégantes aux formats d'entrée
pull/6/head
mama 2017-05-02 16:53:56 +02:00
parent 465438ee1c
commit 4886f0ca1c
8 changed files with 29 additions and 360 deletions

View File

@ -26,7 +26,7 @@
[Cliquez ici](https://www.service-public.fr/professionnels-entreprises/vosdroits/F31211) pour connaître la durée maximale d'un CDD.
références:
Durée maximale d'un CDD (service-public.fr): https://www.service-public.fr/professionnels-entreprises/vosdroits/F31211
format: période
format: mois
suggestions:
18 mois: 18
1 an: 12
@ -38,7 +38,7 @@
titre: Congés non pris
question: Combien de jours de congés ne seront pas pris ?
description: Combien de jours de congés ne pourront être pris par l'employé, du fait de la durée de son CDD. En jours ouvrés, par rapport aux 25 jours de congés légaux annuels.
format: nombre positif
format: jours
suggestions:
3 / 25: 3
10 / 25: 10

View File

@ -25,7 +25,7 @@
nom: salaire de base
question: Quel est le salaire de base ?
description: Le salaire de base est le salaire brut régulier inscrit dans le contrat. C'est le salaire de négociation entre le salarié et l'employeur. Des primes viendront éventuellement le compléter, on parlera alors de salaire brut.
format: nombre positif
format: euros
suggestions:
salaire médian: 2300
SMIC: 1480
@ -34,13 +34,13 @@
nom: salaire brut
titre: Salaire brut
question: Quel est le salaire brut ?
description: |
description: |
C'est le salaire de négociation du contrat de travail en France.
Il peut être vu comme :
- la somme du salaire net et des cotisations sociales salariales retenues sur le bulletin de paie d'un salarié
- ou comme les sommes perçues par le salarié au titre de son contrat de travail, avant retenues sociales et fiscales.
format: nombre positif
format: euros
# TODO En attendant que l'UI devienne plus intelligente, c'est confondu avec le salaire de base.
# intelligente : il faudrait demander : `salaire brut`, puis un bouton `qu'est-ce que c'est` pour nous guider et décortiquer la formule
# formule:

View File

@ -1,100 +0,0 @@
import React, {Component} from 'react'
import './CDD.css'
import Results from './Results'
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'
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 CDD extends Component {
componentDidMount() {
// C'est ici que la génération du formulaire, et donc la traversée des variables commence
this.props.startConversation('surcoût CDD')
}
render() {
let {foldedSteps, unfoldedSteps, situation} = this.props
return (
<div id="sim">
<PageTypeIcon type="simulation" />
<h1>Simulateur CDD</h1>
<div id="conversation">
<div id="questions-answers">
<div id="foldedSteps">
{foldedSteps
.map(step => (
<step.component
key={step.name}
{...step}
step={step}
answer={situation(step.name)}
/>
))}
</div>
<div id="unfoldedSteps">
{unfoldedSteps.map(step => (
<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')} />
<p>
Nous n'avons plus de questions : votre simulation est terminée.
</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
</a>
{' '}
<i
style={{cursor: 'pointer'}}
className="fa fa-envelope-o"
/>
{' '}
!
</p>
</div>}
</div>
<Aide />
</div>
<Results {...this.props} />
</div>
)
}
}
/* TODO Problèmes à résoudre :
- 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)
*/

View File

@ -1,90 +0,0 @@
import React, {Component} from 'react'
import './CDD.css'
import Results from './Results'
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'
//TODO fusionner SimulationCDD & SimulationNet
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 SimulationNet extends Component {
componentDidMount() {
// C'est ici que la génération du formulaire, et donc la traversée des variables commence
this.props.startConversation('salaire net')
}
render() {
let {foldedSteps, unfoldedSteps, situation} = this.props
return (
<div id="sim">
<PageTypeIcon type="simulation" />
<h1>Simulateur salaire net</h1>
<div id="conversation">
<div id="questions-answers">
<div id="foldedSteps">
{foldedSteps
.map(step => (
<step.component
key={step.name}
{...step}
step={step}
answer={situation(step.name)}
/>
))}
</div>
<div id="unfoldedSteps">
{unfoldedSteps.map(step => (
<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')} />
<p>
Nous n'avons plus de questions : votre simulation est terminée.
</p><p>
Une remarque ? &nbsp;
<a href="mailto:contact@embauche.beta.gouv.fr">
Écrivez-nous
</a>
{' '}
<i
style={{cursor: 'pointer'}}
className="fa fa-envelope-o"
/>
{' '}
!
</p>
</div>}
</div>
<Aide />
</div>
<Results {...this.props} />
</div>
)
}
}

View File

@ -1,124 +0,0 @@
import React, {Component} from 'react'
import { connect } from 'react-redux'
import Question from '../components/Forms/Question'
import Input from '../components/Forms/Input'
import SelectCommune from '../components/Forms/SelectCommune'
import SelectTauxRisque from '../components/Forms/SelectTauxRisque'
import RhetoricalQuestion from '../components/Forms/RhetoricalQuestion'
import TextArea from '../components/Forms/TextArea'
import Group from '../components/Group'
import ResultATMP from '../components/ResultATMP'
import {reduxForm, formValueSelector} from 'redux-form'
import { percentage } from '../formValueTypes.js'
import validate from '../conversation-validate'
let advancedInputSelector = formValueSelector('advancedQuestions'),
basicInputSelector = formValueSelector('basicInput')
@reduxForm({
form: 'advancedQuestions',
validate,
})
@connect(state => ({
formValue: (field, simple) => simple ? basicInputSelector(state, field): advancedInputSelector(state, field),
steps: state.steps,
themeColours: state.themeColours
}))
class Conversation extends Component {
render() {
let { formValue, steps, themeColours: {colour, textColour}} = this.props
let effectifEntreprise = formValue('effectifEntreprise', 'basicInput')
/* C'est ici qu'est définie la suite de questions à poser. */
return (
<div id="conversation">
<SelectCommune
visible={effectifEntreprise >= 10}
title="Commune"
question="Quelle est la commune de l'embauche ?"
name="codeINSEE" />
<Input
title="Complémentaire santé"
question="Quel est le montant total par salarié de la complémentaire santé obligatoire de l'entreprise ?"
visible={effectifEntreprise < 10 || steps.get('codeINSEE')}
name="mutuelle" />
<Group
text="Risques professionnels"
visible={steps.get('mutuelle')}
foldTrigger="tauxRisque"
valueType={percentage}
>
<Question
visible={true}
title="Taux de risque connu"
question="Connaissez-vous votre taux de risque AT/MP ?"
name="tauxRisqueConnu" />
<Input
title="Taux de risque"
question="Entrez votre taux de risque"
visible={formValue('tauxRisqueConnu') == 'Oui'}
name="tauxRisque" />
<Group name="tauxInconnu" visible={formValue('tauxRisqueConnu')== 'Non'}>
<SelectTauxRisque
visible={true}
title="Code de risque sélectionné"
question="Quelle est la catégorie de risque de votre entreprise ?"
name="selectTauxRisque" />
<ResultATMP
name="resultATMP"
selectedTauxRisque={formValue('selectTauxRisque')}
formValue={formValue}
{...{steps}}
effectif={formValue('effectifEntreprise', 'basicInput')} />
</Group>
</Group>
<Input
title="Pourcentage d'alternants"
question="Quel est le pourcentage d'alternants dans votre entreprise ?"
visible={effectifEntreprise >= 249 && steps.get('tauxRisque')}
name="pourcentage_alternants" />
<Question
visible={
(effectifEntreprise < 249 && steps.get('tauxRisque'))
|| steps.get('pourcentage_alternants')}
title="Régime Alsace-Moselle"
question="Le salarié est-il affilié au régime d'Alsace-Moselle ?"
name="alsaceMoselle" />
<Question
title="Pénibilité du travail"
question="Le salarié est-il exposé à des facteurs de pénibilité au-delà des seuils d'exposition ?"
visible={steps.get('alsaceMoselle')}
name="penibilite" />
<Question
title="Exonération Jeune Entreprise Innovante"
question="Profitez-vous du statut Jeune Entreprise Innovante pour cette embauche ?"
visible={steps.get('penibilite')}
name="jei" />
<Question
title="Votre avis"
question="Votre estimation est terminée. En êtes-vous satisfait ?"
visible={steps.get('jei')}
name="serviceUtile" />
<RhetoricalQuestion
visible={formValue('serviceUtile') === ':-)'}
name="partage"
question={<span>
Merci. N'hésitez pas à partager le simulateur !
</span>
} />
<TextArea
visible={formValue('serviceUtile') === ':-|'}
name="remarque"
title="Votre remarque"
question={'Que pouvons-nous faire pour l\'améliorer ?'}
/>
</div>)
}
}
export default Conversation

View File

@ -1,23 +1,36 @@
import { number } from './validators'
import { number, int } from './validators'
/*
Here are common formats that can be attached to Form components
*/
export let percentage = {
let pourcentage = {
suffix: '%',
human: value => value + ' ' + '%',
validator: number
}
export let euro = {
let euros = {
suffix: '€',
human: value => value + ' ' + '€',
validator: number
}
export let months = {
let mois = {
suffix: 'mois',
human: value => value + ' ' + 'mois',
validator: number
validator: int
}
let jours = {
suffix: 'jours',
human: value => value + ' ' + 'jours',
validator: int
}
export default {
pourcentage,
euros,
mois,
jours
}

View File

@ -3,7 +3,7 @@ import Explicable from '../components/conversation/Explicable'
import R from 'ramda'
import Question from '../components/conversation/Question'
import Input from '../components/conversation/Input'
import {euro, months} from '../components/conversation/formValueTypes'
import formValueTypes from '../components/conversation/formValueTypes'
import {analyseSituation} from './traverse'
import {formValueSelector} from 'redux-form'
import { STEP_ACTION, START_CONVERSATION} from '../actions'
@ -189,6 +189,7 @@ export let generateGridQuestions = missingVariables => R.pipe(
// )(variant['une possibilité'])
// }
)
export let generateSimpleQuestions = missingVariables => R.pipe(
R.values, //TODO exploiter ici les groupes de questions de type 'record' (R.keys): elles pourraient potentiellement êtres regroupées visuellement dans le formulaire
R.unnest,
@ -197,10 +198,10 @@ export let generateSimpleQuestions = missingVariables => R.pipe(
if (rule == null) console.log(dottedName)
return Object.assign(
constructStepMeta(rule),
rule.format == 'nombre positif' || rule.format == 'période'
rule.format != null
? {
component: Input,
valueType: rule.format == 'nombre positif' ? euro : months,
valueType: formValueTypes[rule.format],
attributes: {
inputMode: 'numeric',
placeholder: 'votre réponse',
@ -220,34 +221,3 @@ export let generateSimpleQuestions = missingVariables => R.pipe(
)
})
)
/*
// Pas de groupe trouvé : ce sont des variables individuelles
[R.isNil, () => variables.map(dottedName => {
let rule = findRuleByDottedName(dottedName)
return Object.assign(constructStepMeta(rule),
rule.format == 'nombre positif' ||
rule.format == 'période' ?
{
component: Input,
valueType: rule.format == 'nombre positif' ? euro : months,
attributes: {
inputMode: 'numeric',
placeholder: 'votre réponse'
},
suggestions: rule.suggestions
} : {
component: Question,
choices: [
{value: 'non', label: 'Non'},
{value: 'oui', label: 'Oui'}
]
},
{
objectives: missingVariables[dottedName]
}
)})],
*/

View File

@ -23,7 +23,7 @@ let evaluateBottomUp = situationGate => startingFragments => {
export let evaluateVariable = (situationGate, variableName, format) => {
// test rec
let value = situationGate(variableName)
return R.contains(format)(['nombre positif', 'période'])
? (value == undefined ? null : value)
return format != null ?
(value == undefined ? null : value)
: evaluateBottomUp(situationGate)(splitName(variableName))
}