diff --git a/site/source/components/Feedback/Feedback.tsx b/site/source/components/Feedback/Feedback.tsx new file mode 100644 index 000000000..578dba6ad --- /dev/null +++ b/site/source/components/Feedback/Feedback.tsx @@ -0,0 +1,168 @@ +import { useCallback, useContext, useState } from 'react' +import { Trans, useTranslation } from 'react-i18next' +import { useLocation } from 'react-router-dom' + +import { TrackingContext } from '@/components/ATInternetTracking' +import { Popover } from '@/design-system' +import { Button } from '@/design-system/buttons' +import { Emoji } from '@/design-system/emoji' +import { Spacing } from '@/design-system/layout' +import { Strong } from '@/design-system/typography' +import { H4 } from '@/design-system/typography/heading' +import { StyledLink } from '@/design-system/typography/link' +import { Body, SmallBody } from '@/design-system/typography/paragraphs' +import { useSitePaths } from '@/sitePaths' + +import * as safeLocalStorage from '../../storage/safeLocalStorage' +import { JeDonneMonAvis } from '../JeDonneMonAvis' +import { INSCRIPTION_LINK } from '../layout/Footer/InscriptionBetaTesteur' +import FeedbackForm from './FeedbackForm' +import FeedbackRating, { FeedbackT } from './FeedbackRating' +import { useFeedback } from './useFeedback' + +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 months +const getShouldAskFeedback = (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)) + ) +} + +const IFRAME_SIMULATEUR_EMBAUCHE_PATH = '/iframes/simulateur-embauche' + +export function Feedback({ + onEnd, + onFeedbackFormOpen, +}: { + onEnd?: () => void + onFeedbackFormOpen?: () => void +}) { + const [isShowingThankMessage, setIsShowingThankMessage] = useState(false) + const [isShowingSuggestionForm, setIsShowingSuggestionForm] = useState(false) + const [isNotSatisfied, setIsNotSatisfied] = useState(false) + const { t } = useTranslation() + const url = useLocation().pathname + const tag = useContext(TrackingContext) + + const { absoluteSitePaths } = useSitePaths() + const currentPath = useLocation().pathname + const isSimulateurSalaire = + currentPath.includes(absoluteSitePaths.simulateurs.salarié) || + currentPath.includes(IFRAME_SIMULATEUR_EMBAUCHE_PATH) + + const { shouldShowRater, customTitle } = useFeedback() + + const submitFeedback = useCallback( + (rating: FeedbackT) => { + setFeedbackGivenForUrl(url) + tag.events.send('click.action', { + click_chapter1: 'satisfaction', + click: rating, + }) + const isNotSatisfiedValue = ['mauvais', 'moyen'].includes(rating) + if (isNotSatisfiedValue) { + setIsNotSatisfied(true) + onFeedbackFormOpen?.() + } + + setIsShowingThankMessage(!isNotSatisfiedValue) + setIsShowingSuggestionForm(isNotSatisfiedValue) + }, + [tag, url] + ) + + const shouldAskFeedback = getShouldAskFeedback(url) + + return ( + <> + {isShowingThankMessage || !shouldAskFeedback ? ( + <> + + + Merci de votre retour !{' '} + + + + + + Pour continuer à donner votre avis et accéder aux nouveautés en + avant-première,{' '} + + inscrivez-vous sur la liste des beta-testeur + + + + + ) : ( + <> +

{customTitle || Un avis sur cette page ?}

+ + {shouldShowRater && ( + + )} + + )} + + {isSimulateurSalaire ? ( + + ) : ( + + )} + {isShowingSuggestionForm && ( + { + setIsShowingSuggestionForm(false) + setTimeout(() => onEnd?.()) + }} + title={ + isNotSatisfied + ? t('Vos attentes ne sont pas remplies') + : t('Votre avis nous intéresse') + } + > + + + Vous n’avez pas été satisfait(e) de votre expérience, nous + en sommes désolé(e)s. + + + ) + } + /> + + )} + + ) +} diff --git a/site/source/components/Feedback/FeedbackForm.tsx b/site/source/components/Feedback/FeedbackForm.tsx index 3372e3d11..6d929c8b2 100644 --- a/site/source/components/Feedback/FeedbackForm.tsx +++ b/site/source/components/Feedback/FeedbackForm.tsx @@ -22,8 +22,6 @@ type SubmitError = { const SHORT_MAX_LENGTH = 254 const FeedbackThankYouContent = () => { - const { t } = useTranslation() - return ( <> @@ -51,14 +49,12 @@ const FeedbackThankYouContent = () => { } export default function FeedbackForm({ - title, infoSlot, description, placeholder, tags, hideShare, }: { - title: string infoSlot?: ReactNode description?: ReactNode placeholder?: string @@ -129,10 +125,6 @@ export default function FeedbackForm({ {isSubmittedSuccessfully && } {!isSubmittedSuccessfully && ( <> -

- {title} -

-
{ diff --git a/site/source/components/Feedback/FeedbackRating.tsx b/site/source/components/Feedback/FeedbackRating.tsx index d059bda30..02d518b00 100644 --- a/site/source/components/Feedback/FeedbackRating.tsx +++ b/site/source/components/Feedback/FeedbackRating.tsx @@ -2,12 +2,12 @@ import styled from 'styled-components' import { Emoji } from '@/design-system/emoji' -export type Feedback = 'mauvais' | 'moyen' | 'bien' | 'très bien' +export type FeedbackT = 'mauvais' | 'moyen' | 'bien' | 'très bien' const FeedbackRating = ({ submitFeedback, }: { - submitFeedback: (feedbackValue: Feedback) => void + submitFeedback: (feedbackValue: FeedbackT) => void }) => { return (
`app::feedback::v3::${url}` -const setFeedbackGivenForUrl = (url: string) => { - safeLocalStorage.setItem( - localStorageKey(url), - JSON.stringify(new Date().toISOString()) - ) -} - -// Ask for feedback again after 4 months -const getShouldAskFeedback = (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)) - ) -} - -const IFRAME_SIMULATEUR_EMBAUCHE_PATH = '/iframes/simulateur-embauche' +import { ForceThemeProvider } from '../utils/DarkModeContext' +import { Feedback } from './Feedback' const FeedbackButton = ({ isEmbedded }: { isEmbedded?: boolean }) => { const [isFormOpen, setIsFormOpen] = useState(false) - const [isShowingThankMessage, setIsShowingThankMessage] = useState(false) - const [isShowingSuggestionForm, setIsShowingSuggestionForm] = useState(false) - const [isNotSatisfied, setIsNotSatisfied] = useState(false) const { t } = useTranslation() - const url = useLocation().pathname - const tag = useContext(TrackingContext) const containerRef = useRef(null) - - const { absoluteSitePaths } = useSitePaths() - const currentPath = useLocation().pathname - const isSimulateurSalaire = - currentPath.includes(absoluteSitePaths.simulateurs.salarié) || - currentPath.includes(IFRAME_SIMULATEUR_EMBAUCHE_PATH) - - const { shouldShowRater, customTitle } = useFeedback() - + const [feedbackFormIsOpened, setFeedbackFormIsOpened] = useState(false) useOnClickOutside( containerRef, - () => !isShowingSuggestionForm && setIsFormOpen(false) - ) - - const submitFeedback = useCallback( - (rating: Feedback) => { - setFeedbackGivenForUrl(url) - tag.events.send('click.action', { - click_chapter1: 'satisfaction', - click: rating, - }) - const isNotSatisfiedValue = ['mauvais', 'moyen'].includes(rating) - if (isNotSatisfiedValue) { - setIsNotSatisfied(true) - } - - setIsShowingThankMessage(!isNotSatisfiedValue) - setIsShowingSuggestionForm(isNotSatisfiedValue) - }, - [tag, url] + () => !feedbackFormIsOpened && setIsFormOpen(false) ) const buttonRef = useRef() as MutableRefObject - const shouldAskFeedback = getShouldAskFeedback(url) const handleClose = () => { setIsFormOpen(false) setTimeout(() => { @@ -124,7 +48,7 @@ const FeedbackButton = ({ isEmbedded }: { isEmbedded?: boolean }) => { return (
-
+ { - {isShowingThankMessage || !shouldAskFeedback ? ( - <> - - - - Merci de votre retour ! - {' '} - - - - - - Pour continuer à donner votre avis et accéder aux nouveautés - en avant-première,{' '} - - inscrivez-vous sur la liste des beta-testeur - - - - - ) : ( - <> - - {customTitle || Un avis sur cette page ?} - - On vous écoute. - - {shouldShowRater && ( - - )} - - )} - - {isSimulateurSalaire ? ( - - ) : ( - - )} -
-
- {isShowingSuggestionForm && ( - { - setIsShowingSuggestionForm(false) - setTimeout(() => setIsFormOpen(false)) - }} - title={ - isNotSatisfied - ? t('Vos attentes ne sont pas remplies') - : t('Votre avis nous intéresse') - } - > - - - Vous n’avez pas été satisfait(e) de votre expérience, nous - en sommes désolé(e)s. - - - ) - } - title={ - isNotSatisfied - ? t('Vos attentes ne sont pas remplies') - : t('Votre avis nous intéresse') - } + { + if (!feedbackFormIsOpened) { + setIsFormOpen(false) + } + setFeedbackFormIsOpened(false) + }} + onFeedbackFormOpen={() => setFeedbackFormIsOpened(true)} /> - - )} + +
) } @@ -324,16 +170,6 @@ const StyledButton = styled.button<{ } ` -const StyledH4 = styled(H4)` - margin: 0; - color: ${({ theme }) => theme.colors.extended.grey[100]}; - font-size: 1rem; -` - -const StyledBody = styled(Body)` - margin: 0; -` - const Section = styled.section<{ $isEmbedded?: boolean }>` position: fixed; top: 10.5rem; @@ -342,10 +178,7 @@ const Section = styled.section<{ $isEmbedded?: boolean }>` width: 17.375rem; background-color: ${({ theme }) => theme.colors.bases.primary[700]}; border-radius: 2rem 0 0 2rem; - color: ${({ theme }) => theme.colors.extended.grey[100]}; - & ${Body} { - color: ${({ theme }) => theme.colors.extended.grey[100]}; - } + padding: 1.5rem; padding-top: 0.75rem; display: flex; @@ -360,10 +193,6 @@ const Section = styled.section<{ $isEmbedded?: boolean }>` } ` -const ThankYouText = styled(Body)` - font-size: 14px; -` - const CloseButtonContainer = styled.div` display: flex; justify-content: flex-end; diff --git a/site/source/components/layout/Footer/useFeedback.ts b/site/source/components/Feedback/useFeedback.ts similarity index 90% rename from site/source/components/layout/Footer/useFeedback.ts rename to site/source/components/Feedback/useFeedback.ts index 0b352c5f6..0921ab815 100644 --- a/site/source/components/layout/Footer/useFeedback.ts +++ b/site/source/components/Feedback/useFeedback.ts @@ -26,12 +26,15 @@ export const useFeedback = () => { absoluteSitePaths.budget, absoluteSitePaths.assistants.index, absoluteSitePaths.assistants['choix-du-statut'].index, + absoluteSitePaths.assistants['choix-du-statut']['recherche-activité'], + absoluteSitePaths.assistants['choix-du-statut']['détails-activité'], absoluteSitePaths.accessibilité, ].includes(currentPathDecoded) && // Exclure les pages et sous-pages ![ absoluteSitePaths.documentation.index, absoluteSitePaths.nouveautés.index, + absoluteSitePaths.stats, absoluteSitePaths.développeur.index, ].some((path) => currentPathDecoded.includes(path)) diff --git a/site/source/components/References.tsx b/site/source/components/References.tsx index 06c5a34f4..3ac3b49b4 100644 --- a/site/source/components/References.tsx +++ b/site/source/components/References.tsx @@ -4,6 +4,7 @@ import { useContext } from 'react' import styled from 'styled-components' import { EngineContext, useEngine } from '@/components/utils/EngineContext' +import { Grid } from '@/design-system/layout' import { Link } from '@/design-system/typography/link' import { Li, Ul } from '@/design-system/typography/list' import { capitalise0 } from '@/utils' @@ -55,26 +56,38 @@ function Reference({ href, title }: { href: string; title: string }) { return (
  • - -
    + + {capitalise0(title)} + + + - {capitalise0(title)} -
    - {domain in referencesImages && ( - - )} - + {domain in referencesImages && ( + + )} + +
  • ) } @@ -89,7 +102,7 @@ const StyledImage = styled.img` border-radius: ${({ theme }) => theme.box.borderRadius}; background-color: ${({ theme }) => theme.colors.extended.grey[100]}; - max-height: 2.5rem; + max-height: 2.25rem; ` const referencesImages = { 'service-public.fr': '/rĂ©fĂ©rences-images/service-public.png', diff --git a/site/source/pages/assistants/choix-du-statut/rĂ©sultat.tsx b/site/source/pages/assistants/choix-du-statut/rĂ©sultat.tsx index 1255b1a7b..b9bf3465a 100644 --- a/site/source/pages/assistants/choix-du-statut/rĂ©sultat.tsx +++ b/site/source/pages/assistants/choix-du-statut/rĂ©sultat.tsx @@ -5,9 +5,11 @@ import { useLocation } from 'react-router-dom' import { TrackPage } from '@/components/ATInternetTracking' import { CurrentSimulatorCard } from '@/components/CurrentSimulatorCard' +import { Feedback } from '@/components/Feedback/Feedback' import { References } from '@/components/References' import { StatutType } from '@/components/StatutTag' import { useEngine } from '@/components/utils/EngineContext' +import { Message } from '@/design-system' import { Button } from '@/design-system/buttons' import { Article } from '@/design-system/card' import { Emoji } from '@/design-system/emoji' @@ -44,7 +46,7 @@ export default function RĂ©sultat() { nĂ©cessaires Ă  la crĂ©ation de votre entreprise. Voici quelques pistes. - +
    - +
    + + + + + + + + + - - - + + theme.darkMode diff --git a/site/source/pages/assistants/recherche-code-ape/ActivityNotFound.tsx b/site/source/pages/assistants/recherche-code-ape/ActivityNotFound.tsx index a30f80a18..1b5f30ccd 100644 --- a/site/source/pages/assistants/recherche-code-ape/ActivityNotFound.tsx +++ b/site/source/pages/assistants/recherche-code-ape/ActivityNotFound.tsx @@ -19,6 +19,7 @@ export default function ActivityNotFound({ job }: { job: string }) { return ( <> // eslint-disable-next-line react/jsx-props-no-spreading hide ? ( @@ -43,7 +44,6 @@ export default function ActivityNotFound({ job }: { job: string }) { {() => ( <>