Skip to content

Commit

Permalink
add sequence pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
ddecrulle committed Apr 11, 2024
1 parent bb35413 commit e24ea53
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 42 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"@codegouvfr/react-dsfr": "^1.9.6",
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@inseefr/lunatic": "3.0.0-rc.21",
"@inseefr/lunatic": "3.0.0-rc.22",
"@inseefr/lunatic-dsfr": "2.0.0-rc.8",
"@mui/material": "^5.15.15",
"@tanstack/react-query": "^5.29.0",
Expand Down
2 changes: 1 addition & 1 deletion src/api/axiosInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const stromaeInstance = <T>(
}).then(({ data }) => data)
}

//DepositProofInstance
//We use a customInstance for depositProof because we need response headers to get fileName.
export const depositProofInstance = <T>(
config: AxiosRequestConfig,
options?: AxiosRequestConfig
Expand Down
120 changes: 88 additions & 32 deletions src/components/Orchestrator/Navigation.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import Button from '@codegouvfr/react-dsfr/Button'
import { Stepper } from '@codegouvfr/react-dsfr/Stepper'
import ButtonsGroup from '@codegouvfr/react-dsfr/ButtonsGroup'
import { Grid } from 'components/Grid'
import type { InternalPageType } from 'model/Page'
import { useMemo, type PropsWithChildren } from 'react'
import { useStyles } from 'tss-react'
import { useMemo, useState, type PropsWithChildren } from 'react'
import type { OrchestratorProps } from './Orchestrator'
import { fr } from '@codegouvfr/react-dsfr'
import type { LunaticOverview } from './utils/lunaticType'

export function Navigation(
props: PropsWithChildren<{
Expand All @@ -14,6 +15,9 @@ export function Navigation(
handleDownloadData: () => void
handleDepositProofClick: () => Promise<void>
mode: OrchestratorProps['mode']
pagination: 'question' | 'sequence' | 'subsequence'
overview: LunaticOverview
isSequencePage: boolean
}>
) {
const {
Expand All @@ -24,8 +28,10 @@ export function Navigation(
handleDepositProofClick,
mode,
children,
pagination,
overview,
isSequencePage,
} = props
const { css } = useStyles()

const nextLabel = useMemo(() => {
switch (currentPage) {
Expand All @@ -40,40 +46,77 @@ export function Navigation(
}
}, [currentPage])

const isPreviousButtonDisable = ['welcomePage', 'endPage'].includes(
const isPreviousButtonDisplayed = ['welcomePage', 'endPage'].includes(
currentPage
)

const [isLayoutExpanded, setIsLayoutExpanded] = useState<boolean>(false)

const currentSequence = findLatestReachedElement(overview)

const displaySequenceSteeper =
!isSequencePage &&
currentPage === 'lunaticPage' &&
overview.length > 0 &&
currentSequence

return (
<>
<Button
id="button-precedent"
title="Revenir à l'étape précédente"
priority="tertiary no outline"
iconId="fr-icon-arrow-left-line"
onClick={handlePreviousClick}
disabled={isPreviousButtonDisable}
className={css({
visibility: isPreviousButtonDisable ? 'hidden' : 'visible',
})}
>
Précédent
</Button>
<Grid>
{children}
<Button
priority="primary"
title={"Passer à l'étape suivante"}
id="continue-button"
onClick={
currentPage === 'endPage'
? handleDepositProofClick
: handleNextClick
}
{!isPreviousButtonDisplayed && (
<div className={fr.cx('fr-grid-row')}>
<div className={fr.cx('fr-container')}>
<Button
id="button-precedent"
title="Revenir à l'étape précédente"
priority="tertiary no outline"
iconId="fr-icon-arrow-left-line"
onClick={handlePreviousClick}
disabled={isPreviousButtonDisplayed}
>
Précédent
</Button>
{displaySequenceSteeper && (
<Stepper
currentStep={currentSequence.index}
stepCount={overview.length}
title={currentSequence.element.label}
className={fr.cx('fr-mx-1w')}
/>
)}
</div>
</div>
)}

<div className={fr.cx('fr-container')}>
<div
className={fr.cx('fr-grid-row', 'fr-grid-row--center', 'fr-my-10v')}
>
{nextLabel}
</Button>
</Grid>
<div className={fr.cx(isLayoutExpanded ? 'fr-col-12' : 'fr-col-8')}>
{pagination === 'sequence' && currentPage === 'lunaticPage' && (
<Button
iconId="ri-expand-diagonal-line"
priority="tertiary no outline"
onClick={() => setIsLayoutExpanded((expanded) => !expanded)}
title="Étendre la vue"
style={{ float: 'right' }}
/>
)}
{children}
<Button
priority="primary"
title={"Passer à l'étape suivante"}
id="continue-button"
onClick={
currentPage === 'endPage'
? handleDepositProofClick
: handleNextClick
}
>
{nextLabel}
</Button>
</div>
</div>
</div>
{mode === 'visualize' && (
<ButtonsGroup
buttons={[
Expand All @@ -92,3 +135,16 @@ export function Navigation(
</>
)
}

/**
* function will be deleted when lunatic will add currentSequence boolean in overview
*/
function findLatestReachedElement(array: LunaticOverview) {
for (let i = array.length - 1; i >= 0; i--) {
if (array[i].reached === true) {
return { index: i + 1, element: array[i] }
}
}
// If no element with reached true is found, return null or handle accordingly
return null
}
21 changes: 17 additions & 4 deletions src/components/Orchestrator/Orchestrator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import type { LunaticGetReferentiel } from './utils/lunaticType'
import { isObjectEmpty } from 'utils/isObjectEmpty'
import { useUpdateEffect } from 'hooks/useUpdateEffect'
import { useRefSync } from 'hooks/useRefSync'
import { isSequencePage } from './utils/sequence'

export type OrchestratorProps = OrchestratorProps.Common &
(OrchestratorProps.Visualize | OrchestratorProps.Collect)
Expand Down Expand Up @@ -55,6 +56,8 @@ export function Orchestrator(props: OrchestratorProps) {

const initialCurrentPage = surveyUnitData?.stateData?.currentPage

const pagination = source.pagination ?? 'question'

const {
getComponents,
Provider,
Expand All @@ -68,11 +71,13 @@ export function Orchestrator(props: OrchestratorProps) {
goToPage: goToLunaticPage,
getChangedData,
resetChangedData,
overview,
} = useLunatic(source, surveyUnitData?.data, {
activeControls: true,
getReferentiel,
autoSuggesterLoading: true,
trackChanges: mode === 'collect',
withOverview: true,
})

const [activeErrors, setActiveErrors] = useState<
Expand Down Expand Up @@ -147,7 +152,7 @@ export function Orchestrator(props: OrchestratorProps) {
})
})

// Persist data when page change in "collect" mode
// Persist data and stateData when page change in "collect" mode
useUpdateEffect(() => {
if (mode !== 'collect') return
const { updateCollectedData, updateStateData } = props
Expand Down Expand Up @@ -175,17 +180,25 @@ export function Orchestrator(props: OrchestratorProps) {
}
}
}

const components = getComponents({
except: pagination === 'sequence' ? 'Sequence' : undefined,
})

return (
<div className={fr.cx('fr-container--fluid', 'fr-mt-1w', 'fr-mb-7w')}>
<div className={fr.cx('fr-container--fluid')}>
<Provider>
<div className={fr.cx('fr-col-12', 'fr-container')}>
<div>
<Navigation
handleNextClick={goNext}
handlePreviousClick={goPrevious}
handleDownloadData={downloadAsJsonRef.current}
currentPage={currentPage}
mode={mode}
handleDepositProofClick={handleDepositProofClick}
pagination={pagination}
overview={overview}
isSequencePage={isSequencePage(components)}
>
<div className={fr.cx('fr-mb-4v')}>
{currentPage === 'welcomePage' && (
Expand All @@ -197,7 +210,7 @@ export function Orchestrator(props: OrchestratorProps) {
{currentPage === 'lunaticPage' && (
<LunaticComponents
autoFocusKey={pageTag}
components={getComponents()}
components={components}
slots={slotComponents}
componentProps={() => ({
errors: activeErrors,
Expand Down
8 changes: 8 additions & 0 deletions src/components/Orchestrator/utils/lunaticType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,11 @@ export type LunaticGoPreviousPage = ReturnType<
typeof useLunatic
>['goPreviousPage']
export type LunaticGoNextPage = ReturnType<typeof useLunatic>['goNextPage']

export type LunaticOverview = ReturnType<typeof useLunatic>['overview']

export type LunaticPageTag = ReturnType<typeof useLunatic>['pageTag']

export type LunaticComponentProps = ReturnType<
ReturnType<typeof useLunatic>['getComponents']
>
59 changes: 59 additions & 0 deletions src/components/Orchestrator/utils/overview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import type { InternalPageType } from 'model/Page'
import type { LunaticOverview, LunaticPageTag } from './lunaticType'

const pageTagComparator = (a: LunaticPageTag, b: LunaticPageTag) => {
const pageA = a.split(/\D/).map((v) => parseInt(v, 10))
const pageB = b.split(/\D/).map((v) => parseInt(v, 10))
// [0, 2, 1] is used to extract the significant part of the pageTag part (start with page, then iteration, then subpage)
for (const index of [0, 2, 1]) {
if (pageA[index] !== pageB[index]) {
return (pageA[index] ?? -1) - (pageB[index] ?? -1)
}
}
return 0
}

const getCurrentAndNextSequenceLabel = ({
overview,
currentPageTag,
}: {
overview: LunaticOverview
currentPageTag: LunaticPageTag
}) => {
const sequences = overview.filter(
(sequence) => pageTagComparator(currentPageTag, sequence.page) <= 0
)
console.log(currentPageTag)
console.log({ overview })
console.log({ sequences })
//overview is ordered so sequences too
const currentSequence = sequences[0]
const nextSequence = sequences[1]

return {
currentLabel: currentSequence.label,
nextLabel: nextSequence?.label,
currentStep:
overview.findIndex((sequence) => sequence.id === currentSequence.id) + 1,
}
}

export const getSteeperInfo = (params: {
overview: LunaticOverview
currentPageTag: LunaticPageTag
currentPage: InternalPageType
}) => {
const { overview } = params

if (overview.length === 0) return { displayOverview: false }

const { currentLabel, nextLabel, currentStep } =
getCurrentAndNextSequenceLabel(params)
return {
displayOverview: true,
currentSequenceLabel: currentLabel,
nextSequenceLabel: nextLabel,
sequenceCount: overview.length,
currentStep,
}
}
4 changes: 4 additions & 0 deletions src/components/Orchestrator/utils/sequence.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import type { LunaticComponentProps } from './lunaticType'

export const isSequencePage = (components: LunaticComponentProps) =>
components.some((component) => component.componentType === 'Sequence')
1 change: 1 addition & 0 deletions src/pages/Collect/CollectPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { useSetLogoutQuestionnaire } from 'hooks/useLogoutUrl'
export function CollectPage() {
const { surveyUnitId, questionnaireId } = collectRoute.useParams()
const queryClient = useQueryClient()

useSetLogoutQuestionnaire(questionnaireId)

const loaderResults = collectRoute.useLoaderData()
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -757,10 +757,10 @@
dependencies:
react-number-format "^5.3.4"

"@inseefr/[email protected].21":
version "3.0.0-rc.21"
resolved "https://registry.yarnpkg.com/@inseefr/lunatic/-/lunatic-3.0.0-rc.21.tgz#233a752d3120c8503b9b94af001c47458b3f2577"
integrity sha512-D7P2h3JcdifO52DVj4xqaurgmoPGyUVWol0VLTxhKDelPIG3ZoSbFs9jyYdHDUXSSeQ3yo3SefGWZFp7duOkiQ==
"@inseefr/[email protected].22":
version "3.0.0-rc.22"
resolved "https://registry.yarnpkg.com/@inseefr/lunatic/-/lunatic-3.0.0-rc.22.tgz#d83503b69c3ae2bc9b3a2104b37197a9b2a6dfb3"
integrity sha512-dTCi5AYRi+sDV7QZdMgZA0jBpM0vK1DqiUWNq9AQJ2OteU0xfNVuUhUXe3KQjEsvWvp9YXWrYAQ7NCDmKbDDXA==
dependencies:
"@inseefr/trevas" "^0.1.20"
"@inseefr/vtl-2.0-antlr-tools" "^0.1.0"
Expand Down

0 comments on commit e24ea53

Please sign in to comment.