🎨 améliore l'UI / UX sur les questions posée

pull/530/head
Johan Girod 2019-06-06 16:33:06 +02:00
parent f3bc7e0579
commit 7876bfed9f
No known key found for this signature in database
GPG Key ID: 9E27B57DA2E8AE12
23 changed files with 231 additions and 324 deletions

View File

@ -151,29 +151,6 @@ const PaySlip = ({
<RuleLink {...salaireNetAprèsImpôt} />
<Montant>{salaireNetAprèsImpôt.montant}</Montant>
</div>
<br />
<p className="ui__ notice">
<Trans i18nKey="payslip.notice">
Le simulateur vous aide à comprendre votre bulletin de paie, sans lui
être opposable. Pour plus d&apos;informations, rendez vous sur&nbsp;
<a
alt="service-public.fr"
href="https://www.service-public.fr/particuliers/vosdroits/F559">
service-public.fr
</a>
.
</Trans>
</p>
<p className="ui__ notice">
<Trans i18nKey="payslip.disclaimer">
Il ne prend pour l'instant pas en compte les accords et conventions
collectives, ni la myriade d'aides aux entreprises. Trouvez votre
convention collective{' '}
<a href="https://socialgouv.github.io/conventions-collectives">ici</a>
, et explorez les aides sur&nbsp;
<a href="https://www.aides-entreprises.fr">aides-entreprises.fr</a>.
</Trans>
</p>
</div>
)
}

View File

@ -28,9 +28,8 @@ const QuickLinks = ({ goToQuestion, quickLinks, quickLinksToHide }: Props) => {
reject(dottedName => contains(dottedName, quickLinksToHide))
)(quickLinks)
return (
<p>
<small>Répondre à une question précise : </small>
<br />
<span>
<small>Autres questions : </small>
{links.map(([label, dottedName]) => (
<>
<button
@ -43,7 +42,7 @@ const QuickLinks = ({ goToQuestion, quickLinks, quickLinksToHide }: Props) => {
</>
))}{' '}
{/* <button className="ui__ link-button">Voir la liste</button> */}
</p>
</span>
)
}

View File

@ -1,31 +0,0 @@
.result-view__tabs {
display: flex;
align-items: center;
box-shadow: 0 -1px 0 0 #d4d4d5, 1px 0 0 0 #d4d4d5, -1px 0 0 0 #d4d4d5;
border-top-right-radius: 0.6em;
border: none;
border-top-left-radius: 0.6em;
margin-bottom: -1px;
border-bottom: 1px solid transparent;
}
.result-view__tabs button {
padding: 1em !important;
border-right: 1px solid #d4d4d5 !important;
text-decoration: none !important;
}
.result-view__tabs button.selected {
border-bottom: 1px solid white;
}
.result-view__tabs button:last-of-type {
border-right: none !important;
}
.result-view__header {
margin-top: 2em;
display: flex;
justify-content: space-between;
}
.result-view__body {
border-top-left-radius: 0 !important;
margin-bottom: 2em;
}

View File

@ -1,59 +0,0 @@
/* @flow */
import Distribution from 'Components/Distribution'
import PaySlip from 'Components/PaySlip'
import SearchButton from 'Components/SearchButton'
import React, { Component } from 'react'
import { Trans } from 'react-i18next'
import './SalaryCompactExplanation.css'
import type { Tracker } from 'Components/utils/withTracker'
type ResultView = 'distribution' | 'payslip'
type State = {
resultView: ResultView
}
type Props = {
tracker: Tracker,
displayResults: boolean
}
const resultViewTitle = {
distribution: 'Cotisations',
payslip: 'Fiche de paie'
}
export default class SalaryCompactExplanation extends Component<Props, State> {
state = {
resultView: 'payslip'
}
handleClickOnTab = (resultView: ResultView) => () => {
this.setState({ resultView })
this.props.tracker.push(['trackEvent', 'results', 'selectView', resultView])
}
render() {
return (
<>
<div className="result-view__header">
<div className="result-view__tabs">
{['payslip', 'distribution'].map(resultView => (
<button
key={resultView}
className={
'ui__ link-button ' +
(this.state.resultView === resultView ? 'selected' : '')
}
onClick={this.handleClickOnTab(resultView)}>
<Trans>{resultViewTitle[resultView]}</Trans>
</button>
))}
</div>
<SearchButton />
</div>
<div className="ui__ card result-view__body">
{this.state.resultView === 'payslip' ? <PaySlip /> : <Distribution />}
</div>
</>
)
}
}

View File

@ -1,51 +1,78 @@
import { React, T } from 'Components'
import PageFeedback from 'Components/Feedback/PageFeedback'
import Distribution from 'Components/Distribution'
import PaySlip from 'Components/PaySlip'
import withTracker from 'Components/utils/withTracker'
import { compose } from 'ramda'
import React from 'react'
import { Trans } from 'react-i18next'
import { connect } from 'react-redux'
import { formValueSelector } from 'redux-form'
import * as Animate from 'Ui/animate'
import SalaryCompactExplanation from './SalaryCompactExplanation'
import SalaryFirstExplanation from './SalaryFirstExplanation'
export default compose(
withTracker,
connect(state => ({
showCompactView: !!state.conversationSteps.foldedSteps.length
showDistributionFirst: !state.conversationSteps.foldedSteps.length
}))
)(
class SalaryExplanation extends React.Component {
render() {
return (
<Animate.fromTop>
{!this.props.showCompactView ? (
<>
{/* <PageFeedback
customMessage={
<T k="feedback.simulator">
Êtes-vous satisfait de ce simulateur ?
</T>
}
customEventName="rate simulator"
/> */}
<SalaryFirstExplanation {...this.props} />
{this.props.protectionText}
</>
) : (
<>
<SalaryCompactExplanation {...this.props} />
<PageFeedback
customMessage={
<T k="feedback.simulator">
Êtes-vous satisfait de ce simulateur ?
</T>
}
customEventName="rate simulator"
/>
</>
)}
<div style={{ textAlign: 'center' }} />
</Animate.fromTop>
)
}
}
)(function SalaryExplanation({ showDistributionFirst }) {
return (
<Animate.fromTop key={showDistributionFirst}>
{showDistributionFirst ? (
<>
<DistributionSection />
<PaySlipSection />
</>
) : (
<>
<PaySlipSection />
<DistributionSection />
</>
)}
<p className="ui__ notice">
<Trans i18nKey="payslip.notice">
Le simulateur vous aide à comprendre votre bulletin de paie, sans lui
être opposable. Pour plus d&apos;informations, rendez vous sur&nbsp;
<a
alt="service-public.fr"
href="https://www.service-public.fr/particuliers/vosdroits/F559">
service-public.fr
</a>
.
</Trans>
</p>
<p className="ui__ notice">
<Trans i18nKey="payslip.disclaimer">
Il ne prend pour l'instant pas en compte les accords et conventions
collectives, ni la myriade d'aides aux entreprises. Trouvez votre
convention collective{' '}
<a href="https://socialgouv.github.io/conventions-collectives">ici</a>
, et explorez les aides sur&nbsp;
<a href="https://www.aides-entreprises.fr">aides-entreprises.fr</a>.
</Trans>
</p>
</Animate.fromTop>
)
})
const PaySlipSection = connect(state => ({
period: formValueSelector('conversation')(state, 'période')
}))(({ period }) => (
<section>
<h2>
<Trans>
{period === 'mois'
? 'Fiche de paie mensuelle'
: 'Détail annuel des cotisations'}
</Trans>
</h2>
<PaySlip />
</section>
))
const DistributionSection = () => (
<section>
<h2>
<Trans>À quoi servent mes cotisations ?</Trans>
</h2>
<Distribution />
</section>
)

View File

@ -1,40 +0,0 @@
/* @flow */
import Distribution from 'Components/Distribution'
import PaySlip from 'Components/PaySlip'
import React, { Component } from 'react'
import { Trans } from 'react-i18next'
import { connect } from 'react-redux'
// $FlowFixMe
import { formValueSelector } from 'redux-form'
type OwnProps = {}
type Props = OwnProps & {
period: 'mois' | 'année'
}
export default (connect(state => ({
period: formValueSelector('conversation')(state, 'période')
}))(
class SalaryFirstExplanation extends Component<Props> {
render() {
return (
<>
<h2>
<Trans>À quoi servent mes cotisations ?</Trans>
</h2>
<Distribution />
<h2>
<Trans>
{this.props.period === 'mois'
? 'Fiche de paie mensuelle'
: 'Détail annuel des cotisations'}
</Trans>
</h2>
<PaySlip />
</>
)
}
}
): React$ComponentType<OwnProps>)

View File

@ -7,6 +7,7 @@ import {
import classnames from 'classnames'
import { T } from 'Components'
import Conversation from 'Components/conversation/Conversation'
import SeeAnswersButton from 'Components/conversation/SeeAnswersButton'
import PeriodSwitch from 'Components/PeriodSwitch'
// $FlowFixMe
import ComparaisonConfig from 'Components/simulationConfigs/rémunération-dirigeant.yaml'
@ -206,6 +207,7 @@ const SchemeComparaison = ({
</T>
) : (
<div className="ui__ container">
<SeeAnswersButton />
<Conversation />
</div>
)}

View File

@ -1,29 +1,52 @@
import Controls from 'Components/Controls'
import Conversation from 'Components/conversation/Conversation'
import ResultReliability from 'Components/conversation/ResultReliability'
import SeeAnswersButton from 'Components/conversation/SeeAnswersButton'
import TargetSelection from 'Components/TargetSelection'
import React from 'react'
import emoji from 'react-easy-emoji'
import { connect } from 'react-redux'
import { firstStepCompletedSelector } from 'Selectors/analyseSelectors'
import { simulationProgressSelector } from 'Selectors/progressSelectors'
import * as Animate from 'Ui/animate'
import Progress from 'Ui/Progress'
export default connect(state => ({
firstStepCompleted: firstStepCompletedSelector(state)
firstStepCompleted: firstStepCompletedSelector(state),
progress: simulationProgressSelector(state)
}))(function Simulation({
firstStepCompleted,
explanations,
customEndMessages
customEndMessages,
progress
}) {
return (
<>
<TargetSelection />
{firstStepCompleted && (
<>
<Controls />
<h2>{emoji('📝 ')}Votre simulation</h2>
<>
<Conversation customEndMessages={customEndMessages} />
<Animate.fromTop>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
marginTop: '1.2rem',
marginBottom: '0.6rem',
alignItems: 'flex-end'
}}>
<ResultReliability progress={progress} />
<SeeAnswersButton />
</div>
<div className="ui__ full-width choice-group">
<div className="ui__ container">
<Controls />
<Conversation customEndMessages={customEndMessages} />
</div>
</div>
{progress < 1 && (
<Progress progress={progress} className="ui__ full-width" />
)}
{explanations}
</>
</Animate.fromTop>
</>
)}
</>

View File

@ -6,8 +6,8 @@ import { compose } from 'ramda'
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { flatRulesSelector } from 'Selectors/analyseSelectors'
import References from '../rule/References'
import './Aide.css'
import References from './rule/References'
export default compose(
connect(

View File

@ -11,7 +11,7 @@ import { connect } from 'react-redux'
import { createSelector } from 'reselect'
import { règleAvecValeurSelector } from 'Selectors/regleSelectors'
import Montant from 'Ui/Montant'
import { softCatch } from '../utils'
import { softCatch } from '../../utils'
import './AnswerList.css'
const formatAnswer = (answer, language) => {

View File

@ -1,15 +1,11 @@
import { goToQuestion, resetSimulation, skipQuestion } from 'Actions/actions'
import { T } from 'Components'
import Aide from 'Components/Aide'
import Answers from 'Components/AnswerList'
import QuickLinks from 'Components/QuickLinks'
import Scroll from 'Components/utils/Scroll'
import withColours from 'Components/utils/withColours'
import { getInputComponent } from 'Engine/generateQuestions'
import { compose } from 'ramda'
import React, { useState } from 'react'
import React from 'react'
import emoji from 'react-easy-emoji'
import { withTranslation } from 'react-i18next'
import { connect } from 'react-redux'
import { reduxForm } from 'redux-form'
import {
@ -17,31 +13,27 @@ import {
flatRulesSelector,
nextStepsSelector
} from 'Selectors/analyseSelectors'
import { simulationProgressSelector } from 'Selectors/progressSelectors'
import * as Animate from 'Ui/animate'
import Progress from 'Ui/Progress'
import Aide from './Aide'
import './conversation.css'
export default compose(
withColours,
reduxForm({
form: 'conversation',
destroyOnUnmount: false
}),
withTranslation(),
connect(
state => ({
flatRules: flatRulesSelector(state),
currentQuestion: currentQuestionSelector(state),
previousAnswers: state.conversationSteps.foldedSteps,
nextSteps: nextStepsSelector(state),
progress: simulationProgressSelector(state)
nextSteps: nextStepsSelector(state)
}),
{ resetSimulation, skipQuestion, goToQuestion }
)
)(function Conversation({
nextSteps,
previousAnswers,
currentQuestion,
customEndMessages,
@ -51,98 +43,63 @@ export default compose(
skipQuestion,
goToQuestion
}) {
const arePreviousAnswers = previousAnswers.length > 0
const [showAnswerModal, setShowAnswerModal] = useState(false)
return (
<Animate.fromTop>
<div
style={{
display: 'flex',
justifyContent: 'flex-start',
alignItems: 'baseline'
}}>
<p
style={{
flex: 1,
maxWidth: '50%'
}}>
<small>Précision du résultat :</small>
<Progress progress={progress} />
</p>
<div style={{ flex: 1 }} />
{arePreviousAnswers ? (
<Scroll.toElement onlyIfNotVisible>
{nextSteps.length ? (
<>
<Aide />
<div id="currentQuestion">
{currentQuestion && (
<React.Fragment key={currentQuestion}>
<Animate.fadeIn>
{getInputComponent(flatRules)(currentQuestion)}
</Animate.fadeIn>
<div className="ui__ answer-group">
{previousAnswers.length > 0 && (
<>
<button
onClick={() =>
goToQuestion(previousAnswers.slice(-1)[0])
}
className="ui__ simple small skip button left">
Précédent
</button>
</>
)}
<button
onClick={() => skipQuestion(nextSteps[0])}
className="ui__ simple small skip button right">
Passer
</button>
</div>
</React.Fragment>
)}
<QuickLinks />
</div>
</>
) : (
<div style={{ textAlign: 'center' }}>
<h3>
{emoji('🌟')}{' '}
<T k="simulation-end.title">Vous avez completé cette simulation</T>{' '}
</h3>
<p>
{customEndMessages ? (
customEndMessages
) : (
<T k="simulation-end.text">
Vous avez maintenant accès à l'estimation la plus précise
possible.
</T>
)}
</p>
<button
style={{ marginRight: '1em', alignSelf: 'baseline' }}
className="ui__ small button "
onClick={() => setShowAnswerModal(true)}>
<T>Voir mes réponses</T>
className="ui__ small simple button "
onClick={resetSimulation}>
<T>Recommencer</T>
</button>
) : (
<span />
)}
<button
className="ui__ small simple skip button left"
style={{ alignSelf: 'baseline' }}
onClick={() => resetSimulation()}>
<T>Recommencer</T>
</button>
</div>
{showAnswerModal && <Answers onClose={() => setShowAnswerModal(false)} />}
<div className="ui__ full-width choice-group">
<div className="ui__ container">
{nextSteps.length ? (
<Scroll.toElement onlyIfNotVisible>
<Aide />
<div id="currentQuestion">
{currentQuestion && (
<React.Fragment key={currentQuestion}>
<Animate.fadeIn>
{getInputComponent(flatRules)(currentQuestion)}
</Animate.fadeIn>
<div className="ui__ answer-group">
{previousAnswers.length > 0 && (
<button
onClick={() =>
goToQuestion(previousAnswers.slice(-1)[0])
}
className="ui__ simple small skip button left">
Précédent
</button>
)}
{nextSteps.length > 0 && (
<button
onClick={() => skipQuestion(nextSteps[0])}
className="ui__ simple small skip button right">
Passer
</button>
)}
</div>
</React.Fragment>
)}
</div>
<QuickLinks />
</Scroll.toElement>
) : (
<>
<h3>
{emoji('🌟')}{' '}
<T k="simulation-end.title">Simulation complétée à 100%</T>{' '}
</h3>
<p>
<T k="simulation-end.text">
Nous n'avons plus de questions à poser, vous avez atteint
l'estimation la plus précise.
</T>
{customEndMessages}
</p>
</>
)}
</div>
</div>
</Animate.fromTop>
)}
</Scroll.toElement>
)
})

View File

@ -0,0 +1,31 @@
import React from 'react'
import InfoBulle from 'Ui/InfoBulle'
export default function ResultReliability({ progress }) {
return (
<span>
<small>
Fiabilité du résultat :{' '}
<strong>
{progress === 0
? 'Mauvaise'
: progress < 0.2
? 'Faible'
: progress < 0.5
? 'Moyenne'
: progress < 0.7
? 'Bonne'
: progress < 0.8
? 'Très bonne'
: progress < 1
? 'Excellente'
: 'Optimale'}
</strong>
</small>{' '}
<InfoBulle>
Le résultat peut varier énormément en fonction de votre situation.
Répondez aux questions pour en améliorer la précision.
</InfoBulle>{' '}
</span>
)
}

View File

@ -0,0 +1,23 @@
import { T } from 'Components'
import React, { useState } from 'react'
import { connect } from 'react-redux'
import Answers from './AnswerList'
import './conversation.css'
export default connect(state => ({
arePreviousAnswers: !!state.conversationSteps.foldedSteps.length
}))(function SeeAnswersButton({ arePreviousAnswers }) {
const [showAnswerModal, setShowAnswerModal] = useState(false)
return (
<>
{arePreviousAnswers && (
<button
className="ui__ small simple button "
onClick={() => setShowAnswerModal(true)}>
<T>Voir mes réponses</T>
</button>
)}
{showAnswerModal && <Answers onClose={() => setShowAnswerModal(false)} />}
</>
)
})

View File

@ -76,6 +76,7 @@
.ui__.button.simple,
.ui__.button.simple.small {
border: none;
background: transparent;
padding-left: 0;
padding-right: 0;
}

View File

@ -95,7 +95,7 @@ ul.ui__.checklist {
}
.ui__.checklist-explanation {
padding-left: calc(0.6rem + 8px);
border-left: 2px dashed var(--colour);
padding-left: calc(0.6rem + 10px);
border-left: 1px solid var(--lighterColour);
margin: 0 0 -1rem 8px;
}

View File

@ -1,6 +1,5 @@
.progress__container {
border-radius: 0.2rem;
height: 0.4rem;
height: 0.2rem;
background-color: var(--lighterColour);
overflow: hidden;
}

View File

@ -1,9 +1,9 @@
import React from 'react'
import './Progress.css'
export default function Progress({ progress }) {
export default function Progress({ progress, style, className }) {
return (
<div className="progress__container">
<div className={'progress__container ' + className} style={style}>
<div className="progress__bar" style={{ width: `${progress * 100}%` }} />
</div>
)

View File

@ -37,10 +37,9 @@ export const fromBottom = ({
{React.Children.map(children, (item, i) => ({ y, ...style }) => (
<animated.div
key={i}
className={item && item.props.className}
style={{
transform: y.interpolate(y => `translate3d(0, ${y}px,0)`),
...(item && item.props.style),
...style,
...inheritedStyle
}}>
@ -68,7 +67,9 @@ export const fromTop = ({
<animated.div
key={i}
style={{
transform: y.interpolate(y => `translate3d(0, ${y}px,0)`),
transform: y.interpolate(y =>
y ? `translate3d(0, ${y}px,0)` : 'none'
),
...style,
...inheritedStyle
}}>

View File

@ -61,7 +61,7 @@ section.ui__:not(:first-of-type) {
padding-left: 0.6rem;
}
.ui__.container .ui__.full-width {
--margin: calc((850px - 100vw) / 2);
--margin: calc((850px - 100vw - 0.6rem) / 2);
margin-right: var(--margin);
margin-left: var(--margin);
}

View File

@ -739,9 +739,7 @@
niveau: avertissement
message: |
Le salaire saisi est inférieur au SMIC.
solution:
cible: contrat salarié . temps partiel
texte: Est-ce un temps partiel ?
- si:
toutes ces conditions:
- brut de base [mensuel] > 10000

View File

@ -265,8 +265,7 @@ export let nextStepsSelector = createSelector(
state => state.conversationSteps.foldedSteps
],
(mv, questions, foldedSteps) => {
let nextSteps = getNextSteps(mv)
questions = difference(nextSteps, foldedSteps)
let nextSteps = difference(getNextSteps(mv), foldedSteps)
if (questions && questions.blacklist) {
return difference(nextSteps, questions.blacklist)
}