Améliore la navigation dans les intégrations pour l'assistant au choix du statut

pull/2782/head
Johan Girod 2023-08-02 11:06:39 +02:00
parent c8b48af193
commit 218a8ea059
9 changed files with 98 additions and 68 deletions

View File

@ -15,7 +15,6 @@ import {
} from '@/hooks/useCurrentSimulatorData'
import { useIsEmbedded } from '@/hooks/useIsEmbedded'
import useSimulationConfig from '@/hooks/useSimulationConfig'
import { useSitePaths } from '@/sitePaths'
import { situationSelector } from '@/store/selectors/simulationSelectors'
import { Merge } from '@/types/utils'
@ -57,7 +56,7 @@ export default function SimulateurOrAssistantPage() {
autoloadLastSimulation,
})
useSearchParamsSimulationSharing()
const { absoluteSitePaths } = useSitePaths()
const trackInfo = {
chapter1:
typeof tracking !== 'string' && tracking && 'chapter1' in tracking

View File

@ -0,0 +1,17 @@
import { useEffect, useState } from 'react'
import { getIframeOffset } from '@/utils'
export const useIFrameOffset = () => {
const [offsetTop, setOffset] = useState<number | null | undefined>(
window.parent !== window ? undefined : null
)
useEffect(() => {
if (window.parent === window) {
return
}
void getIframeOffset().then(setOffset)
}, [])
return offsetTop
}

View File

@ -9,31 +9,18 @@ import {
} from '@react-aria/overlays'
import { AriaDialogProps } from '@react-types/dialog'
import FocusTrap from 'focus-trap-react'
import React, { RefObject, useEffect, useRef, useState } from 'react'
import React, { RefObject, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import styled, { css, keyframes } from 'styled-components'
import { Grid } from '@/design-system/layout'
import { getIframeOffset, wrapperDebounceEvents } from '@/utils'
import { wrapperDebounceEvents } from '@/utils'
import { FocusStyle } from '../global-style'
import { useIFrameOffset } from '../hooks'
import { Container } from '../layout'
import { H2 } from '../typography/heading'
const useIFrameOffset = () => {
const [offsetTop, setOffset] = useState<number | null | undefined>(
window.parent !== window ? undefined : null
)
useEffect(() => {
if (window.parent === window) {
return
}
void getIframeOffset().then(setOffset)
}, [])
return offsetTop
}
export default function Popover(
props: OverlayProps &
AriaDialogProps & {

View File

@ -17,15 +17,15 @@ export const useCurrentSimulatorData = () => {
const entries = Object.entries(simulatorsData)
const [key, data] =
(!isEmbedded
? entries
.sort((a, b) => b[1].path.length - a[1].path.length)
.find(([, data]) => pathname.startsWith(data.path))
: entries
.sort((a, b) => b[1].iframePath.length - a[1].iframePath.length)
.find(([, data]) =>
pathname.startsWith('/iframes/' + data.iframePath)
)) ?? []
// Find the simulator with classic path
entries
.sort((a, b) => b[1].path.length - a[1].path.length)
.find(([, data]) => pathname.startsWith(data.path)) ??
// Find the simulator with iframe path
entries
.sort((a, b) => b[1].iframePath.length - a[1].iframePath.length)
.find(([, data]) => pathname.startsWith('/iframes/' + data.iframePath)) ??
[]
return {
key: key as keyof typeof simulatorsData,

View File

@ -1,5 +1,5 @@
import { Trans, useTranslation } from 'react-i18next'
import styled from 'styled-components'
import styled, { css } from 'styled-components'
import { TrackPage } from '@/components/ATInternetTracking'
import { Button } from '@/design-system/buttons'
@ -20,6 +20,7 @@ export default function Navigation({
onPreviousStep, // TODO : prefer resetOnLeave
assistantIsCompleted = false,
children,
small = false,
}: {
currentStepIsComplete: boolean
nextStepLabel?: false | string
@ -27,6 +28,7 @@ export default function Navigation({
onPreviousStep?: () => void
assistantIsCompleted?: false | Statuts
children?: React.ReactNode
small?: boolean
}) {
const { t } = useTranslation()
const nextStep = useNextStep()
@ -38,8 +40,8 @@ export default function Navigation({
return (
<>
<TrackPage chapter3="pas_a_pas" name={currentStep} />
<Spacing xs />
<StyledNavigation>
{!small && <Spacing xs />}
<StyledNavigation $small={small}>
<Grid container spacing={2}>
{children && (
<Grid item xs={12}>
@ -49,6 +51,7 @@ export default function Navigation({
<Grid item>
<Button
light
size={small ? 'XS' : 'MD'}
color={'secondary'}
to={choixDuStatutPath[previousStep]}
onPress={onPreviousStep}
@ -59,6 +62,7 @@ export default function Navigation({
{nextStep && !assistantIsCompleted && (
<Grid item>
<Button
size={small ? 'XS' : 'MD'}
onPress={onNextStep}
to={choixDuStatutPath[nextStep]}
isDisabled={!currentStepIsComplete}
@ -86,17 +90,21 @@ export default function Navigation({
)}
</Grid>
</StyledNavigation>
<Shadow />
{!small && <Shadow />}
</>
)
}
const StyledNavigation = styled.div`
const StyledNavigation = styled.div<{ $small: boolean }>`
display: flex;
align-items: center;
height: 110px;
${({ $small }) =>
!$small &&
css`
height: 110px;
`}
position: sticky;
padding: ${({ theme }) => theme.spacings.lg} 1rem;
padding: ${({ theme, $small }) => theme.spacings[$small ? 'sm' : 'lg']} 1rem;
margin: 0 -1rem;
bottom: 0;
background: ${({ theme }) =>

View File

@ -50,6 +50,8 @@ export default function StatutsPossibles() {
const StyledMessage = styled(Message)`
padding-top: 2rem;
border: none;
position: sticky;
top: 0;
border-radius: 0.5rem;
background: ${({ theme }) =>
theme.darkMode

View File

@ -7,6 +7,7 @@ import { HelpButtonWithPopover } from '@/design-system/buttons'
import { H5 } from '@/design-system/typography/heading'
import { Link } from '@/design-system/typography/link'
import { Body } from '@/design-system/typography/paragraphs'
import { useIsEmbedded } from '@/hooks/useIsEmbedded'
import { resetSimulation, updateSituation } from '@/store/actions/actions'
import SearchCodeAPE from '../recherche-code-ape/SearchCodeAPE'
@ -17,11 +18,26 @@ export default function RechercheActivité() {
const [codeApe, setCodeApe] = useState('')
const { t } = useTranslation()
const dispatch = useDispatch()
const isEmbedded = useIsEmbedded()
useEffect(() => {
dispatch(resetSimulation())
}, [])
const NavigationComponent = (
<Navigation
small={!!codeApe && isEmbedded}
currentStepIsComplete={!!codeApe}
onNextStep={() => {
dispatch(
updateSituation(
'entreprise . activités . principale . code APE',
`'${codeApe}'`
)
)
}}
/>
)
return (
<>
<Layout
@ -57,18 +73,12 @@ export default function RechercheActivité() {
</Trans>
}
>
<SearchCodeAPE hideGuichetUnique onCodeAPESelected={setCodeApe} />
<Navigation
currentStepIsComplete={!!codeApe}
onNextStep={() => {
dispatch(
updateSituation(
'entreprise . activités . principale . code APE',
`'${codeApe}'`
)
)
}}
<SearchCodeAPE
hideGuichetUnique
onCodeAPESelected={setCodeApe}
underSelection={isEmbedded ? NavigationComponent : null}
/>
{(!codeApe || !isEmbedded) && NavigationComponent}
</Layout>
</>
)

View File

@ -3,13 +3,13 @@ import { Trans, useTranslation } from 'react-i18next'
import styled, { css } from 'styled-components'
import { Appear } from '@/components/ui/animate'
import { Chip } from '@/design-system'
import { Button, HelpButtonWithPopover } from '@/design-system/buttons'
import { ChevronIcon } from '@/design-system/icons'
import { Strong } from '@/design-system/typography'
import { H4, H5 } from '@/design-system/typography/heading'
import { H4, H5, H6 } from '@/design-system/typography/heading'
import { Link } from '@/design-system/typography/link'
import { Li, Ul } from '@/design-system/typography/list'
import { Body } from '@/design-system/typography/paragraphs'
import { Body, SmallBody } from '@/design-system/typography/paragraphs'
import GuichetInfo from './GuichetInfo'
@ -32,15 +32,15 @@ export const Result = ({ item, hideGuichetUnique }: ResultProps) => {
return (
<>
<H4 as="h3">{title}</H4>
<Body
<H5 as="h3">{title}</H5>
<SmallBody
css={`
display: flex;
justify-content: space-between;
align-items: center;
`}
>
<Strong>Code APE : {codeApe}</Strong>
<Chip>Code : {codeApe}</Chip>
<Button
size="XXS"
light
@ -53,12 +53,12 @@ export const Result = ({ item, hideGuichetUnique }: ResultProps) => {
{!open ? t('En savoir plus') : t('Replier')}&nbsp;
<StyledChevron aria-hidden $isOpen={open} />
</Button>
</Body>
</SmallBody>
{open && (
<Appear id={`info-${codeApe}`}>
{contenuCentral.length ? (
<>
<H5 as="h4">Contenu central de cette activité :</H5>
<H6 as="h4">Contenu central de cette activité :</H6>
<Ul>
{contenuCentral.map((contenu, i) => (
<Li key={i}>{contenu}</Li>
@ -69,7 +69,7 @@ export const Result = ({ item, hideGuichetUnique }: ResultProps) => {
{contenuAnnexe.length ? (
<>
<H5 as="h4">Contenu annexe de cette activité :</H5>
<H6 as="h4">Contenu annexe de cette activité :</H6>
<Ul>
{contenuAnnexe.map((contenu, i) => (
<Li key={i}>{contenu}</Li>
@ -80,7 +80,7 @@ export const Result = ({ item, hideGuichetUnique }: ResultProps) => {
{contenuExclu.length ? (
<>
<H5 as="h4">Contenu exclu de cette activité :</H5>
<H6 as="h4">Contenu exclu de cette activité :</H6>
<Ul>
{contenuExclu.map((contenu, i) => (
<Li key={i}>{contenu}</Li>

View File

@ -78,6 +78,7 @@ interface SearchCodeApeProps {
hideGuichetUnique?: boolean
onCodeAPESelected?: (codeAPE: string) => void
trackSearch?: boolean
underSelection?: React.ReactNode
}
export default function SearchCodeAPE({
@ -85,6 +86,7 @@ export default function SearchCodeAPE({
hideGuichetUnique = false,
onCodeAPESelected,
trackSearch = false,
underSelection,
}: SearchCodeApeProps) {
const { t } = useTranslation()
const [searchQuery, setSearchQuery] = usePersistingState<string>(
@ -250,18 +252,23 @@ export default function SearchCodeAPE({
>
{list.slice(0, 25).map(({ item, debug }) => {
return (
<RadioCardSkeleton
isDisabled={disabled}
value={item.codeApe}
key={item.codeApe}
visibleRadioAs="div"
>
<Result
item={item}
debug={debug}
hideGuichetUnique={hideGuichetUnique}
/>
</RadioCardSkeleton>
<>
<RadioCardSkeleton
isDisabled={disabled}
value={item.codeApe}
key={item.codeApe}
visibleRadioAs="div"
>
<Result
item={item}
debug={debug}
hideGuichetUnique={hideGuichetUnique}
/>
</RadioCardSkeleton>
{underSelection && selected === item.codeApe && (
<FromTop>{underSelection}</FromTop>
)}
</>
)
})}
</StyledRadioCardGroup>