Skip to content

Commit

Permalink
pair with @Grafikart
Browse files Browse the repository at this point in the history
  • Loading branch information
ddecrulle committed Apr 8, 2024
1 parent c74d8a1 commit c5aa054
Show file tree
Hide file tree
Showing 11 changed files with 96 additions and 37 deletions.
11 changes: 5 additions & 6 deletions src/components/Orchestrator/CustomPages/ValidationModal.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { createModal } from '@codegouvfr/react-dsfr/Modal'
import { useState, useEffect, useId } from 'react'
import { useState, useEffect, useId, type MutableRefObject } from 'react'
import { assert } from 'tsafe/assert'

export type Props = {
actions: {
actionsRef: MutableRefObject<{
open?: () => Promise<void>
}
}>
}
export function ValidationModal(props: Props) {
const { actions } = props
export function ValidationModal({actionsRef}: Props) {

const id = useId()

Expand All @@ -27,7 +26,7 @@ export function ValidationModal(props: Props) {
>(undefined)

useEffect(() => {
actions.open = () =>
actionsRef.current.open = () =>
new Promise<void>((resolve) => {
setOpenState({ resolve })
modal.open()
Expand Down
43 changes: 21 additions & 22 deletions src/components/Orchestrator/Orchestrator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
type LunaticError,
type LunaticData,
} from '@inseefr/lunatic'
import { useRef } from 'react'
import { fr } from '@codegouvfr/react-dsfr'
import { downloadAsJson } from 'utils/downloadAsJson'
import { useNavigate } from '@tanstack/react-router'
Expand All @@ -15,14 +16,14 @@ import { Validation } from './CustomPages/Validation'
import { useStromaeNavigation } from './useStromaeNavigation'
import { EndPage } from './CustomPages/EndPage'
import { ValidationModal } from './CustomPages/ValidationModal'
import { assert } from 'tsafe/assert'
import type { SurveyUnitData } from 'model/SurveyUnitData'
import type { StateData } from 'model/StateData'
import { isBlockingError, isSameErrors } from './utils/controls'
import { slotComponents } from './slotComponents'
import type { LunaticGetReferentiel } from './utils/lunaticType'
import { isObjectEmpty } from 'utils/isObjectEmpty'
import { useUpdateEffect } from 'utils/useUpdateEffect'
import { useUpdateEffect } from 'hooks/useUpdateEffect'
import { useRefSync } from 'hooks/useRefSync'

export type OrchestratorProps = OrchestratorProps.Common &
(OrchestratorProps.Visualize | OrchestratorProps.Collect)
Expand Down Expand Up @@ -79,21 +80,22 @@ export function Orchestrator(props: OrchestratorProps) {
Record<string, LunaticError[]> | undefined
>(undefined)

const [validationModalActions] = useState<{
open?: () => Promise<void>
}>({})
const validationModalActionsRef = useRef({
open: () => Promise.resolve(),
})

const goNextHandlingControls = () => {
// Decorates goNext function with controls behavior
const goNextWithControls = () => {
const { currentErrors } = compileControls()

//No errors, we goNext
// No errors, continue
if (!currentErrors) {
setActiveErrors(undefined)
goNextLunatic()
return
}

//An error is blocking, we stay on the page
// An error is blocking, we stay on the page
if (isBlockingError(currentErrors)) {
//compileControls returns isCritical but I prefer define my own rules of blocking error in the orchestrator
setActiveErrors(currentErrors)
Expand All @@ -111,19 +113,16 @@ export function Orchestrator(props: OrchestratorProps) {
}

const { currentPage, goNext, goToPage, goPrevious } = useStromaeNavigation({
goNextLunatic: goNextHandlingControls,
goNextLunatic: goNextWithControls,
goPrevLunatic,
isFirstPage,
isLastPage,
goToLunaticPage,
initialCurrentPage,
openValidationModal: () => {
assert(validationModalActions.open !== undefined)
return validationModalActions.open()
},
openValidationModal: () => validationModalActionsRef.current.open(),
})

function getCurrentStateData(): StateData {
const getCurrentStateData = (): StateData => {
switch (currentPage) {
case 'endPage':
return { date: Date.now(), currentPage, state: 'VALIDATED' }
Expand All @@ -139,7 +138,7 @@ export function Orchestrator(props: OrchestratorProps) {
}
}

function handleDownloadData() {
const downloadAsJsonRef = useRefSync(() => {
downloadAsJson<SurveyUnitData>({
dataToDownload: {
data: getData(false),
Expand All @@ -149,19 +148,19 @@ export function Orchestrator(props: OrchestratorProps) {
//The label of source is not dynamic
filename: `${source.label.value}-${new Date().toLocaleDateString()}`,
})
}
})

const isDownloadPage = currentPage === 'downloadPage'

// When reaching the download page, start downloading the page
useEffect(() => {
if (!isDownloadPage || mode !== 'visualize') return
handleDownloadData()
downloadAsJsonRef.current()
navigate({ to: '/visualize' })
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isDownloadPage])
}, [isDownloadPage, downloadAsJsonRef, navigate, mode])

// Persist data when page change in "collect" mode
useUpdateEffect(() => {
// Persist data when the currentPage or pageTag changes and mode is 'collect'.
if (mode !== 'collect') return
const { updateCollectedData, updateStateData } = props

Expand All @@ -181,7 +180,7 @@ export function Orchestrator(props: OrchestratorProps) {
<Navigation
handleNextClick={goNext}
handlePreviousClick={goPrevious}
handleDownloadData={handleDownloadData}
handleDownloadData={downloadAsJsonRef.current}
currentPage={currentPage}
mode={mode}
>
Expand All @@ -206,7 +205,7 @@ export function Orchestrator(props: OrchestratorProps) {
{currentPage === 'endPage' && (
<EndPage date={surveyUnitData?.stateData?.date} />
)}
<ValidationModal actions={validationModalActions} />
<ValidationModal actionsRef={validationModalActionsRef} />
</div>
</Navigation>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useDocumentTitle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export function useDocumentTitle(title: string) {
return () => {
document.title = prevTitle
}
})
}, [title])
}

export function useSequenceTitle(sequenceLabel: ReactNode) {
Expand Down
40 changes: 40 additions & 0 deletions src/hooks/useLogoutUrl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {
useState,
createContext,
useContext,
useEffect,
type PropsWithChildren,
} from 'react'

/**
* We need to know which questionnaire is currently used to redirect the user when he logs out
*
* ## Example
*
* - For "LOG2021X11" we will redirect the user to domain.ltd/log after logout
* - For "rece2021X11" we will redirect the user to domain.ltd/rece
*/
const SetLogoutPathContext = createContext((() => {}) as (s: string) => void)
const LogoutPathContext = createContext('')

export const LogoutPathProvider = ({ children }: PropsWithChildren) => {
const [logoutPath, setLogoutPath] = useState('')
return (
<SetLogoutPathContext.Provider value={setLogoutPath}>
<LogoutPathContext.Provider value={logoutPath}>
{children}
</LogoutPathContext.Provider>
</SetLogoutPathContext.Provider>
)
}

export function useLogoutUrl(): string {
return `${import.meta.env.VITE_PORTAIL_URL}/${useContext(LogoutPathContext)}`
}

export function useSetLogoutQuestionnaire(questionnaireId: string): void {
const setLogoutPath = useContext(SetLogoutPathContext)
useEffect(() => {
setLogoutPath((questionnaireId.match(/^[^2]+/) ?? '')[0].toLowerCase())
}, [questionnaireId, setLogoutPath])
}
10 changes: 10 additions & 0 deletions src/hooks/useRefSync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { useRef } from 'react'

/**
* useRef, but keep the value in sync on every render
*/
export function useRefSync<T>(value: T) {
const ref = useRef(value)
ref.current = value
return ref
}
File renamed without changes.
File renamed without changes.
8 changes: 7 additions & 1 deletion src/pages/Collect/CollectPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,19 @@ import type {
import { useSetStateData, useUpdateCollectedData } from 'api/06-survey-units'
import type { LunaticData } from '@inseefr/lunatic'
import type { StateData } from 'model/StateData'
import { useDocumentTitle } from 'hooks/useDocumentTitle'
import { useSetLogoutQuestionnaire } from 'hooks/useLogoutUrl'

export function CollectPage() {
const { surveyUnitId } = collectRoute.useParams()
const { surveyUnitId, questionnaireId } = collectRoute.useParams()
const queryClient = useQueryClient()
useSetLogoutQuestionnaire(questionnaireId)

const loaderResults = collectRoute.useLoaderData()

//TODO -> use Metadata
useDocumentTitle("Questionnaire | Filière d'Enquête")

const { source, surveyUnitData } = loaderResults

const getReferentiel: LunaticGetReferentiel = (name: string) =>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Collect/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const collectRoute = createRoute({
params: { questionnaireId, surveyUnitId },
context: { queryClient },
}) => {
document.title = "Questionnaire | Filière d'Enquête"


const sourcePr = queryClient
.ensureQueryData(getGetQuestionnaireDataQueryOptions(questionnaireId))
Expand Down
4 changes: 3 additions & 1 deletion src/router/Layout/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { Header as DsfrHeader } from '@codegouvfr/react-dsfr/Header'
import logoInsee from 'assets/logo-insee.png'
import { headerFooterDisplayItem } from '@codegouvfr/react-dsfr/Display'
import { Badge } from '@codegouvfr/react-dsfr/Badge'
import { useLogoutUrl } from 'hooks/useLogoutUrl'

export function Header() {
const { isUserLoggedIn, logout } = useOidc()

const logoutUrl = useLogoutUrl()
return (
<DsfrHeader
brandTop={
Expand Down Expand Up @@ -39,7 +41,7 @@ export function Header() {
onClick: () =>
logout({
redirectTo: 'specific url',
url: import.meta.env.VITE_PORTAIL_URL,
url: logoutUrl,
}),
},
text: 'Se déconnecter',
Expand Down
13 changes: 8 additions & 5 deletions src/router/Layout/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import { Header } from './Header'
import { Footer } from './Footer'
import type { PropsWithChildren } from 'react'
import { LogoutPathProvider } from 'hooks/useLogoutUrl'

export function Layout(props: PropsWithChildren) {
const { children } = props
return (
<div
style={{ minHeight: '100vh', display: 'flex', flexDirection: 'column' }}
>
<Header />
<main id="contenu" role="main">
{children}
</main>
<Footer />
<LogoutPathProvider>
<Header />
<main id="contenu" role="main">
{children}
</main>
<Footer />
</LogoutPathProvider>
</div>
)
}

0 comments on commit c5aa054

Please sign in to comment.