diff --git a/mon-entreprise/source/ATInternetTracking/Tracker.ts b/mon-entreprise/source/ATInternetTracking/Tracker.ts index 36033bdad..0019dad02 100644 --- a/mon-entreprise/source/ATInternetTracking/Tracker.ts +++ b/mon-entreprise/source/ATInternetTracking/Tracker.ts @@ -33,7 +33,9 @@ export interface ATTracker { set(infos: ATHit): void } click: { - set(infos: ATHit): void + set( + infos: ATHit & { type: 'exit' | 'download' | 'action' | 'navigation' } + ): void } customVars: { set(variables: CustomVars): void diff --git a/mon-entreprise/source/components/Feedback/Feedback.css b/mon-entreprise/source/components/Feedback/Feedback.css index 80bdea302..c7d645842 100644 --- a/mon-entreprise/source/components/Feedback/Feedback.css +++ b/mon-entreprise/source/components/Feedback/Feedback.css @@ -1,24 +1,3 @@ -.feedback-page { - display: flex; - justify-content: center; - flex-wrap: wrap; - padding-top: 0.6rem; - padding-bottom: 0.6rem; - background: var(--lightestColor); - border-radius: 0.9rem; - padding: 0.6rem 1rem; - margin: 1rem 0; -} -.feedback-page button.link-button { - margin: 0 0.6rem; -} - -@media (min-width: 1200px) { - .feedback-page .feedbackButtons { - display: inline; - } -} - .zammad-form > .form-group:nth-of-type(2) { display: none; } diff --git a/mon-entreprise/source/components/Feedback/FeedbackForm.tsx b/mon-entreprise/source/components/Feedback/FeedbackForm.tsx index b0c0bc9fd..5ae914241 100644 --- a/mon-entreprise/source/components/Feedback/FeedbackForm.tsx +++ b/mon-entreprise/source/components/Feedback/FeedbackForm.tsx @@ -1,14 +1,12 @@ import { ScrollToElement } from 'Components/utils/Scroll' -import { TrackerContext } from 'Components/utils/withTracker' -import React, { useContext, useEffect, useRef } from 'react' -import { Trans, useTranslation } from 'react-i18next' +import React, { useEffect } from 'react' +import { useTranslation } from 'react-i18next' import { useLocation } from 'react-router' -type Props = { onEnd: () => void; onCancel: () => void } declare global { const $: any } -export default function FeedbackForm({ onEnd, onCancel }: Props) { +export default function FeedbackForm() { // const tracker = useContext(TrackerContext) const pathname = useLocation().pathname const page = pathname.split('/').slice(-1)[0] @@ -29,12 +27,12 @@ export default function FeedbackForm({ onEnd, onCancel }: Props) { isSimulateur ? 'le simulateur' : 'la page' } ${page}`, messageSubmit: 'Envoyer', - messageThankYou: - 'Merci pour votre retour ! Vous pouvez aussi nous contacter directement à contact@mon-entreprise.beta.gouv.fr', + messageThankYou: 'Merci de votre retour !', lang, attributes: [ { - display: 'Message', + display: + "Que pouvons-nous améliorer afin de mieux répondre à vos attentes ? (ne pas mettre d'informations personnelles)", name: 'body', tag: 'textarea', placeholder: 'Your Message...', @@ -49,7 +47,7 @@ export default function FeedbackForm({ onEnd, onCancel }: Props) { defaultValue: '-', }, { - display: 'Email (pour recevoir notre réponse)', + display: 'Email (pour recevoir une réponse)', name: 'email', tag: 'input', type: 'email', @@ -61,29 +59,10 @@ export default function FeedbackForm({ onEnd, onCancel }: Props) { script.src = 'https://mon-entreprise.zammad.com/assets/form/form.js' document.body.appendChild(script) }, 100) - // tracker.push(['trackEvent', 'Feedback', 'written feedback submitted']) }, []) return ( -
- -
- -

- - Votre retour nous est précieux afin d'améliorer ce site en continu. - Sur quoi devrions nous travailler afin de mieux répondre à vos - attentes ? - -

) diff --git a/mon-entreprise/source/components/Feedback/PageFeedback.tsx b/mon-entreprise/source/components/Feedback/PageFeedback.tsx deleted file mode 100644 index 241384d38..000000000 --- a/mon-entreprise/source/components/Feedback/PageFeedback.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import { TrackerContext } from 'Components/utils/withTracker' -import React, { useCallback, useContext, useState } from 'react' -import { Trans } from 'react-i18next' -import { useLocation } from 'react-router-dom' -import { TrackingContext } from '../../ATInternetTracking' -import safeLocalStorage from '../../storage/safeLocalStorage' -import './Feedback.css' -import Form from './FeedbackForm' - -type PageFeedbackProps = { - blacklist?: Array - customMessage?: React.ReactNode - customEventName?: string -} - -const localStorageKey = (feedback: [string, string]) => - `app::feedback::v2::${feedback.join('::')}` -const saveFeedbackOccurrenceInLocalStorage = ([name, path, rating]: [ - string, - string, - number -]) => { - safeLocalStorage.setItem( - localStorageKey([name, path]), - JSON.stringify(rating) - ) -} -const feedbackAlreadyGiven = (feedback: [string, string]) => { - return !!safeLocalStorage.getItem(localStorageKey(feedback)) -} - -export default function PageFeedback({ - customMessage, - customEventName, -}: PageFeedbackProps) { - const location = useLocation() - const tracker = useContext(TrackerContext) - const [state, setState] = useState({ - showForm: false, - showThanks: false, - feedbackAlreadyGiven: feedbackAlreadyGiven([ - customEventName || 'rate page usefulness', - location.pathname, - ]), - }) - const ATTracker = useContext(TrackingContext) - - const handleFeedback = useCallback(({ useful }: { useful: boolean }) => { - tracker.push([ - 'trackEvent', - 'Feedback', - useful ? 'positive rating' : 'negative rating', - location.pathname, - ]) - ATTracker.click.set({ - chapter1: 'satisfaction', - name: useful ? 'positive' : 'negative', - }) - ATTracker.dispatch() - const feedback = [ - customEventName || 'rate page usefulness', - location.pathname, - useful ? 10 : 0.1, - ] as [string, string, number] - tracker.push(['trackEvent', 'Feedback', ...feedback]) - saveFeedbackOccurrenceInLocalStorage(feedback) - setState({ - showThanks: useful, - feedbackAlreadyGiven: true, - showForm: !useful, - }) - }, []) - - const handleErrorReporting = useCallback(() => { - tracker.push(['trackEvent', 'Feedback', 'report error', location.pathname]) - setState({ ...state, showForm: true }) - }, []) - - if (state.feedbackAlreadyGiven && !state.showForm && !state.showThanks) { - return null - } - - return ( -
-
- {!state.showForm && !state.showThanks && ( - <> -
- {customMessage || ( - - Cette page vous est utile ? - - )}{' '} -
-
- {' '} - - -
- - )} - {state.showThanks && ( -
- - Merci pour votre retour ! Vous pouvez nous contacter directement à{' '} - - contact@mon-entreprise.beta.gouv.fr - - -
- )} - {state.showForm && ( -
- setState({ ...state, showThanks: true, showForm: false }) - } - onCancel={() => - setState({ ...state, showThanks: false, showForm: false }) - } - /> - )} -
-
- ) -} diff --git a/mon-entreprise/source/components/Feedback/index.tsx b/mon-entreprise/source/components/Feedback/index.tsx new file mode 100644 index 000000000..a3cc41db2 --- /dev/null +++ b/mon-entreprise/source/components/Feedback/index.tsx @@ -0,0 +1,157 @@ +import React, { useCallback, useContext, useState } from 'react' +import emoji from 'react-easy-emoji' +import { Trans } from 'react-i18next' +import { useLocation } from 'react-router-dom' +import styled from 'styled-components' +import { TrackingContext } from '../../ATInternetTracking' +import safeLocalStorage from '../../storage/safeLocalStorage' +import './Feedback.css' +import Form from './FeedbackForm' + +type PageFeedbackProps = { + blacklist?: Array + customMessage?: React.ReactNode + customEventName?: string +} + +const localStorageKey = (url: string) => `app::feedback::v3::${url}` +const setFeedbackGivenForUrl = (url: string) => { + safeLocalStorage.setItem( + localStorageKey(url), + JSON.stringify(new Date().toISOString()) + ) +} +// Ask for feedback again after 4 month +const askFeedback = (url: string) => { + const previousFeedbackDate = safeLocalStorage.getItem(localStorageKey(url)) + if (!previousFeedbackDate) { + return true + } + return ( + new Date(previousFeedbackDate) < + new Date(new Date().setMonth(new Date().getMonth() - 4)) + ) +} + +export default function PageFeedback({ customMessage }: PageFeedbackProps) { + const url = useLocation().pathname + const [display, setDisplay] = useState(askFeedback(url)) + const [state, setState] = useState({ + showForm: false, + showThanks: false, + }) + const ATTracker = useContext(TrackingContext) + + const handleFeedback = useCallback( + (rating: 'mauvais' | 'moyen' | 'bien' | 'très bien') => { + setFeedbackGivenForUrl(url) + ATTracker.click.set({ + chapter1: 'satisfaction', + type: 'action', + name: rating, + }) + ATTracker.dispatch() + const askDetails = ['mauvais', 'moyen'].includes(rating) + setState({ + showThanks: !askDetails, + showForm: askDetails, + }) + }, + [ATTracker, url] + ) + + const handleErrorReporting = useCallback(() => { + setState({ ...state, showForm: true }) + }, [state]) + + if (!display) { + return null + } + + return ( +
+ {!state.showForm && !state.showThanks && ( +
+

+ {customMessage || ( + + Êtes-vous satisfait de cette page ? + + )}{' '} +

+
+
+ handleFeedback('mauvais')}> + {emoji('🙁')} + + handleFeedback('moyen')}> + {emoji('😐')} + +
+
+ handleFeedback('bien')}> + {emoji('🙂')} + + handleFeedback('très bien')}> + {emoji('😀')} + +
+
+ +
+ )} + {(state.showThanks || state.showForm) && ( + + )} + {state.showThanks && ( +
+ Merci de votre retour ! +
+ )} + {state.showForm && } +
+ ) +} + +const EmojiButton = styled.button` + font-size: 1.5rem; + padding: 0.6rem; + transition: transform 0.05s; + will-change: transform; + :hover { + transform: scale(1.3); + } +` diff --git a/mon-entreprise/source/components/Simulation.tsx b/mon-entreprise/source/components/Simulation.tsx index 1673558dd..66fa8e146 100644 --- a/mon-entreprise/source/components/Simulation.tsx +++ b/mon-entreprise/source/components/Simulation.tsx @@ -1,7 +1,7 @@ import Conversation, { ConversationProps, } from 'Components/conversation/Conversation' -import PageFeedback from 'Components/Feedback/PageFeedback' +import PageFeedback from 'Components/Feedback' import SearchButton from 'Components/SearchButton' import ShareSimulationBanner from 'Components/ShareSimulationBanner' import TargetSelection from 'Components/TargetSelection' @@ -38,6 +38,7 @@ export default function Simulation({ return ( <> {simulationBloc} + {!firstStepCompleted && } {firstStepCompleted && ( @@ -47,15 +48,67 @@ export default function Simulation({
- - Êtes-vous satisfait de ce simulateur ? - - } - customEventName="rate simulator" - /> - {explanations} +
+
+ {explanations && ( + <> +
+
+ {explanations} +
+ + )} +
+
+
+ + Êtes-vous satisfait de ce simulateur ? + + } + /> +
+
+
+
+
)} diff --git a/mon-entreprise/source/components/conversation/Conversation.tsx b/mon-entreprise/source/components/conversation/Conversation.tsx index 84b37577f..9adc4f9b9 100644 --- a/mon-entreprise/source/components/conversation/Conversation.tsx +++ b/mon-entreprise/source/components/conversation/Conversation.tsx @@ -57,7 +57,9 @@ export default function Conversation({ customEndMessages }: ConversationProps) { return currentQuestion ? ( <> - + {Object.keys(situation).length !== 0 && ( + + )}
diff --git a/mon-entreprise/source/components/layout/Footer/Footer.css b/mon-entreprise/source/components/layout/Footer/Footer.css index 560f8ba5d..3cb47797f 100644 --- a/mon-entreprise/source/components/layout/Footer/Footer.css +++ b/mon-entreprise/source/components/layout/Footer/Footer.css @@ -1,5 +1,7 @@ .footer { margin-top: 5rem; + margin-left: -1rem; + margin-right: -1rem; } .footer img { height: 2.5rem; diff --git a/mon-entreprise/source/components/layout/Footer/Footer.tsx b/mon-entreprise/source/components/layout/Footer/Footer.tsx index 50ebdc585..bf5ba4921 100644 --- a/mon-entreprise/source/components/layout/Footer/Footer.tsx +++ b/mon-entreprise/source/components/layout/Footer/Footer.tsx @@ -1,4 +1,4 @@ -import PageFeedback from 'Components/Feedback/PageFeedback' +import PageFeedback from 'Components/Feedback' import LegalNotice from 'Components/LegalNotice' import NewsletterRegister from 'Components/NewsletterRegister' import SocialIcon from 'Components/ui/SocialIcon' @@ -31,6 +31,8 @@ const useShowFeedback = () => { return ![ sitePath.index, ...Object.values(simulators).map((s) => s.path), + '', + '/', ].includes(currentPath) } export default function Footer() { @@ -58,7 +60,19 @@ export default function Footer() { ))}