diff --git a/source/actions/actions.js b/source/actions/actions.js index bc724f846..fb299f21f 100644 --- a/source/actions/actions.js +++ b/source/actions/actions.js @@ -6,6 +6,9 @@ import type { StartConversationAction } from 'Types/ActionsTypes' import { deletePersistedSimulation } from '../storage/persistSimulation' +import { normalizeBasePath } from '../utils' + +import type { RouterHistory } from 'react-router-dom' export function resetSimulation(): ResetSimulationAction { return { @@ -22,13 +25,18 @@ export const deletePreviousSimulation = () => ( deletePersistedSimulation() } -export function startConversation( - priorityNamespace: ?string -): StartConversationAction { - return { +export const startConversation = (priorityNamespace: ?string) => ( + dispatch: StartConversationAction => void, + _: any, + history: RouterHistory +) => { + dispatch({ type: 'START_CONVERSATION', ...(priorityNamespace ? { priorityNamespace } : {}) - } + }) + const simulationPath = + normalizeBasePath(history.location.pathname) + 'simulation' + history.push(simulationPath) } // $FlowFixMe diff --git a/source/components/Feedback/PageFeedback.js b/source/components/Feedback/PageFeedback.js index 0871b606d..4f961cca5 100644 --- a/source/components/Feedback/PageFeedback.js +++ b/source/components/Feedback/PageFeedback.js @@ -9,35 +9,63 @@ import './Feedback.css' import Form from './FeedbackForm' import type { Tracker } from 'Components/utils/withTracker' import type { Location } from 'react-router-dom' +import type { Node } from 'react' type Props = { location: Location, blacklist: Array, - tracker: Tracker + customMessage?: Node, + tracker: Tracker, + customEventName?: string } type State = { showForm: boolean, showThanks: boolean } +const localStorageKey = (feedback: [string, string]) => + `app::feedback::${feedback.join('::')}` +const saveFeedbackOccurrenceInLocalStorage = ([name, path, rating]: [ + string, + string, + number +]) => { + localStorage.setItem(localStorageKey([name, path]), JSON.stringify(rating)) +} +const feedbackAlreadyGiven = (feedback: [string, string]) => { + return !!localStorage.getItem(localStorageKey(feedback)) +} + class PageFeedback extends Component { static defaultProps = { blacklist: [] } - state = { - showForm: false, - showThanks: false + feedbackAlreadyGiven: boolean + feedbackAlreadyGiven = false + constructor(props) { + super(props) + this.state = { + showForm: false, + showThanks: false + } + this.feedbackAlreadyGiven = feedbackAlreadyGiven([ + this.props.customEventName || 'rate page usefulness', + this.props.location.pathname + ]) } handleFeedback = ({ useful }) => { - this.props.tracker.push([ - 'trackEvent', - 'Feedback', - 'rate page usefulness', + const feedback = [ + this.props.customEventName || 'rate page usefulness', this.props.location.pathname, useful ? 10 : 0 - ]) - this.setState({ showThanks: useful, showForm: !useful }) + ] + this.props.tracker.push(['trackEvent', 'Feedback', ...feedback]) + saveFeedbackOccurrenceInLocalStorage(feedback) + this.setState({ + showThanks: useful, + showForm: !useful + }) } handleErrorReporting = () => { this.props.tracker.push([ @@ -49,6 +77,9 @@ class PageFeedback extends Component { this.setState({ showForm: true }) } render() { + if (this.feedbackAlreadyGiven) { + return null + } return ( !this.props.blacklist.includes(this.props.location.pathname) && (
@@ -56,9 +87,11 @@ class PageFeedback extends Component { !this.state.showThanks && ( <>
- - Cette page vous a-t-elle été utile ? - {' '} + {this.props.customMessage || ( + + Cette page vous a-t-elle été utile ? + + )}{' '} {' '} )} @@ -105,7 +140,7 @@ const PageFeedbackWithRouter = ({ location, ...props }) => ( ) export default compose( - withRouter, translate(), - withTracker + withTracker, + withRouter )(PageFeedbackWithRouter) diff --git a/source/components/QuickLink.js b/source/components/QuickLink.js index 042b3b26f..b0297b8d1 100644 --- a/source/components/QuickLink.js +++ b/source/components/QuickLink.js @@ -1,12 +1,19 @@ /* @flow */ import { startConversation } from 'Actions/actions' import withLanguage from 'Components/utils/withLanguage' -import { toPairs, compose } from 'ramda' +import { compose, toPairs } from 'ramda' import React from 'react' import { Trans } from 'react-i18next' import { connect } from 'react-redux' +import { withRouter } from 'react-router' +import { animated, Spring } from 'react-spring' +import { validInputEnteredSelector } from 'Selectors/analyseSelectors' +import type { Location } from 'react-router' + type Props = { - startConversation: (?string) => void + startConversation: (?string) => void, + location: Location, + validInputEntered: boolean } let quickLinks = { @@ -17,22 +24,53 @@ let quickLinks = { Autres: null } -const QuickLink = ({ startConversation }: Props) => ( - <> - {toPairs(quickLinks).map(([label, dottedName]) => ( - - ))} - -) +const QuickLink = ({ + startConversation, + location, + validInputEntered +}: Props) => { + const show = !location.pathname.endsWith('/simulation') && validInputEntered + return ( + + {styles => ( + + {toPairs(quickLinks).map(([label, dottedName]) => ( + + ))} + + )} + + ) +} + export default compose( withLanguage, + withRouter, connect( - (state, props) => ({ key: props.language }), + (state, props) => ({ + key: props.language, + validInputEntered: validInputEnteredSelector(state) + }), { startConversation } diff --git a/source/components/RuleLink.js b/source/components/RuleLink.js index a43358eca..dc87a2e60 100644 --- a/source/components/RuleLink.js +++ b/source/components/RuleLink.js @@ -4,7 +4,7 @@ import { compose } from 'ramda' import React from 'react' import { withRouter } from 'react-router' import { Link } from 'react-router-dom' -import { capitalise0 } from '../utils' +import { capitalise0, normalizeBasePath } from '../utils' import './RuleLink.css' import type { Règle } from 'Types/RegleTypes' import type { Match } from 'react-router' @@ -13,14 +13,18 @@ type Props = Règle & { style: CSSStyleDeclaration, colours: { colour: string } } -const RuleLink = ({ lien, nom, colours: { colour }, match, style }: Props) => ( - - {capitalise0(nom)} - -) +const RuleLink = ({ lien, nom, colours: { colour }, match, style }: Props) => { + const newPath = + normalizeBasePath(match.path).replace(/simulation\/$/, '') + lien + return ( + + {capitalise0(nom)} + + ) +} export default compose( withRouter, diff --git a/source/components/RulePage.js b/source/components/RulePage.js index 4d6f83df4..3da6a80a3 100644 --- a/source/components/RulePage.js +++ b/source/components/RulePage.js @@ -83,7 +83,7 @@ class BackToSimulation extends Component { return ( { setExample(null) }} diff --git a/source/components/Simu.js b/source/components/Simu.js index b1b98e251..5a72e25c7 100644 --- a/source/components/Simu.js +++ b/source/components/Simu.js @@ -1,48 +1,48 @@ -import { resetSimulation, startConversation } from 'Actions/actions' +import { startConversation } from 'Actions/actions' +import AnswerList from 'Components/AnswerList' import { ScrollToTop } from 'Components/utils/Scroll' import withColours from 'Components/utils/withColours' import withLanguage from 'Components/utils/withLanguage' import React, { Component } from 'react' import { Trans, translate } from 'react-i18next' import { connect } from 'react-redux' +import { Redirect, withRouter } from 'react-router' import { Link } from 'react-router-dom' -import { animated, Spring } from 'react-spring' -import { reset } from 'redux-form' import { blockingInputControlsSelector, nextStepsSelector, - noUserInputSelector + noUserInputSelector, + validInputEnteredSelector } from 'Selectors/analyseSelectors' import * as Animate from 'Ui/animate' -import AnswerList from './AnswerList' +import { normalizeBasePath } from '../utils' import Conversation from './conversation/Conversation' import Distribution from './Distribution' +import PageFeedback from './Feedback/PageFeedback' import PaySlip from './PaySlip' import QuickLink from './QuickLink' import ResultView from './ResultView' import './Simu.css' import TargetSelection from './TargetSelection' +@withRouter @withColours @translate() // Triggers rerender when the language changes @connect( state => ({ - noUserInput: noUserInputSelector(state), blockingInputControls: blockingInputControlsSelector(state), conversationStarted: state.conversationStarted, + validInputEntered: validInputEnteredSelector(state), arePreviousAnswers: state.conversationSteps.foldedSteps.length !== 0, - nextSteps: state.conversationStarted && nextStepsSelector(state) + nextSteps: state.conversationStarted && nextStepsSelector(state), + userInput: noUserInputSelector(state) }), - dispatch => ({ - startConversation: () => dispatch(startConversation()), - resetSimulation: () => { - dispatch(resetSimulation()) - dispatch(reset('conversation')) - } - }) + { + startConversation + } ) @withLanguage -class Simu extends Component { +class Simulation extends Component { state = { displayPreviousAnswers: false } @@ -50,141 +50,155 @@ class Simu extends Component { let { colours, conversationStarted, - noUserInput, arePreviousAnswers, nextSteps, startConversation, - resetSimulation, - blockingInputControls + blockingInputControls, + match, + validInputEntered, + location } = this.props - const firstValidInputEntered = - !conversationStarted && !blockingInputControls && !noUserInput const displayConversation = conversationStarted && !blockingInputControls const simulationCompleted = !blockingInputControls && conversationStarted && !nextSteps.length const displayPreviousAnswers = arePreviousAnswers && this.state.displayPreviousAnswers + const simulationHomePath = normalizeBasePath(match.path).replace( + /simulation\/$/, + '' + ) return ( <>
- {displayConversation && ( - - )} - {arePreviousAnswers && ( -
- -
- )} - - {styles => ( - - - - )} - - {simulationCompleted && ( + + {location.pathname.endsWith('/simulation') && ( <> -

- - Plus de questions ! - -

-

- - Vous avez atteint l'estimation la plus précise. Vous pouvez - maintenant concrétiser votre projet d'embauche. - -

- {this.props.displayHiringProcedures && ( -
- - - Connaître les démarches - - + {!conversationStarted && } + +