diff --git a/frontend/src/components/atoms/Alert/index.tsx b/frontend/src/components/atoms/Alert/index.tsx index bd69c3da0..b50910917 100644 --- a/frontend/src/components/atoms/Alert/index.tsx +++ b/frontend/src/components/atoms/Alert/index.tsx @@ -11,7 +11,7 @@ export enum AlertType { type Props = { type?: AlertType; - title?: string; + title?: string | React.ReactNode; onAlertClose?: MouseEventHandler; children: React.ReactNode; }; diff --git a/frontend/src/components/atoms/hyperTexts/Button.tsx b/frontend/src/components/atoms/hyperTexts/Button.tsx index 5aed95f11..2a45761b2 100644 --- a/frontend/src/components/atoms/hyperTexts/Button.tsx +++ b/frontend/src/components/atoms/hyperTexts/Button.tsx @@ -16,6 +16,8 @@ interface ButtonProps extends ButtonHTMLAttributes { download?: boolean; submit?: boolean; children?: React.ReactNode; + target?: string; + rel?: string; } const Button: React.FC = ({ diff --git a/frontend/src/components/atoms/hyperTexts/HyperText.tsx b/frontend/src/components/atoms/hyperTexts/HyperText.tsx index 09e134c6f..8d858d01f 100644 --- a/frontend/src/components/atoms/hyperTexts/HyperText.tsx +++ b/frontend/src/components/atoms/hyperTexts/HyperText.tsx @@ -11,6 +11,7 @@ type Props = { submit?: boolean; iconFill?: boolean; children: React.ReactNode; + target?: string; }; const HyperText: React.FC = ({ diff --git a/frontend/src/components/atoms/hyperTexts/Link.tsx b/frontend/src/components/atoms/hyperTexts/Link.tsx index 34ce82a9c..e6846f772 100644 --- a/frontend/src/components/atoms/hyperTexts/Link.tsx +++ b/frontend/src/components/atoms/hyperTexts/Link.tsx @@ -17,6 +17,8 @@ type Props = { icon?: string; newTab?: boolean; children: React.ReactNode; + title?: string; + target?: string; }; const Link: React.FC = ({ diff --git a/frontend/src/components/atoms/inputs/Label.tsx b/frontend/src/components/atoms/inputs/Label.tsx index 9614024c6..8a739efab 100644 --- a/frontend/src/components/atoms/inputs/Label.tsx +++ b/frontend/src/components/atoms/inputs/Label.tsx @@ -2,7 +2,7 @@ import React, { FunctionComponent, ReactNode } from 'react'; import Helper from '../Helper'; type Props = { - id: string; + id?: string; label: ReactNode; required?: boolean; helper?: string; diff --git a/frontend/src/components/atoms/inputs/RadioInput.tsx b/frontend/src/components/atoms/inputs/RadioInput.tsx index a3e7f7ec5..6d1c565cb 100644 --- a/frontend/src/components/atoms/inputs/RadioInput.tsx +++ b/frontend/src/components/atoms/inputs/RadioInput.tsx @@ -5,7 +5,7 @@ import Label from './Label'; import { InputProps } from './Input'; interface RadioInputProps extends InputProps { - options?: { id: string; label: string }[]; + options?: { id: string | number; label: string }[]; } export const RadioInput: React.FC = ({ diff --git a/frontend/src/components/atoms/inputs/SelectInput.tsx b/frontend/src/components/atoms/inputs/SelectInput.tsx index e8f18fd2a..4918cf8be 100644 --- a/frontend/src/components/atoms/inputs/SelectInput.tsx +++ b/frontend/src/components/atoms/inputs/SelectInput.tsx @@ -10,11 +10,11 @@ import SideBySideWrapper from './SideBySideWrapper'; import Label from './Label'; interface SelectInputProps extends SelectHTMLAttributes { - options?: { id: string; label: string }[]; + options?: { id: string | number; label: string }[]; useOtherOption?: boolean; - label: string; - helper: string; - meta: string; + label: string | React.ReactNode; + helper?: string; + meta?: string; } export const SelectInput: React.FC = ({ diff --git a/frontend/src/components/molecules/FileInput/DownloadButton.tsx b/frontend/src/components/molecules/FileInput/DownloadButton.tsx index e71c8481e..612e71a1e 100644 --- a/frontend/src/components/molecules/FileInput/DownloadButton.tsx +++ b/frontend/src/components/molecules/FileInput/DownloadButton.tsx @@ -7,8 +7,8 @@ import useFileDownloader from '../../templates/hooks/use-file-downloader'; type Props = { id: string; - label: string; - disabled: boolean; + label: string | React.ReactNode; + disabled?: boolean; onReplaceFile: () => void; uploadedDocuments: Document[]; documentType: string; diff --git a/frontend/src/components/molecules/FileInput/FileInput.tsx b/frontend/src/components/molecules/FileInput/FileInput.tsx index 391d812d6..3f0ac9d49 100644 --- a/frontend/src/components/molecules/FileInput/FileInput.tsx +++ b/frontend/src/components/molecules/FileInput/FileInput.tsx @@ -1,6 +1,7 @@ import React, { ChangeEventHandler, FunctionComponent, + InputHTMLAttributes, useEffect, useState, } from 'react'; @@ -10,18 +11,16 @@ import { DocumentToUpload } from './index'; // NB: please keep this limit in sync with the limit in nginx datapass-backend configuration const FILE_SIZE_LIMIT_IN_MB = 10; -type Props = { - id: string; - label: string; +export interface FileInputProps extends InputHTMLAttributes { documentType: string; - disabled: boolean; - onChange: ChangeEventHandler; - mimeTypes: string; - meta?: string; documentsToUpload: DocumentToUpload[]; -}; + mimeTypes?: string; + meta?: string; + label: string | React.ReactNode; + onChange: ChangeEventHandler; +} -export const FileInput: FunctionComponent = ({ +export const FileInput: FunctionComponent = ({ id, label, documentType, diff --git a/frontend/src/components/molecules/FileInput/index.tsx b/frontend/src/components/molecules/FileInput/index.tsx index 94fb009c0..c4eb8e957 100644 --- a/frontend/src/components/molecules/FileInput/index.tsx +++ b/frontend/src/components/molecules/FileInput/index.tsx @@ -6,7 +6,7 @@ import React, { } from 'react'; import { isEmpty, uniqueId } from 'lodash'; import DownloadButton from './DownloadButton'; -import FileInput from './FileInput'; +import FileInput, { FileInputProps } from './FileInput'; export type DocumentToUpload = { attachment: File; type: string }; @@ -21,18 +21,12 @@ export type Document = { filename: string; }; -type Props = { - label: string; - documentType: string; - disabled: boolean; - onChange: ChangeEventHandler; - mimeTypes?: string; - meta?: string; - documentsToUpload: DocumentToUpload[]; +interface IndexProps extends FileInputProps { uploadedDocuments: Document[]; -}; + disabled?: boolean; +} -const Index: FunctionComponent = ({ +const Index: FunctionComponent = ({ label, meta, mimeTypes = '.pdf, application/pdf', diff --git a/frontend/src/components/molecules/Stepper/index.tsx b/frontend/src/components/molecules/Stepper/index.tsx index 5dca6ad98..806e603b0 100644 --- a/frontend/src/components/molecules/Stepper/index.tsx +++ b/frontend/src/components/molecules/Stepper/index.tsx @@ -2,7 +2,7 @@ import './style.css'; import { useDataProviderConfigurations } from '../../templates/hooks/use-data-provider-configurations'; type StepperProps = { - children: React.ReactNode; + children?: React.ReactNode; steps: string[]; currentStep: string | null; previousStepNotCompleted?: boolean; diff --git a/frontend/src/components/organisms/ConfirmationModal.js b/frontend/src/components/organisms/ConfirmationModal.tsx similarity index 76% rename from frontend/src/components/organisms/ConfirmationModal.js rename to frontend/src/components/organisms/ConfirmationModal.tsx index a459378b7..978a000fc 100644 --- a/frontend/src/components/organisms/ConfirmationModal.js +++ b/frontend/src/components/organisms/ConfirmationModal.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { MouseEventHandler, SyntheticEvent } from 'react'; import AriaModal from '@justfixnyc/react-aria-modal'; import Button from '../atoms/hyperTexts/Button'; import ButtonGroup from '../molecules/ButtonGroup'; @@ -6,7 +6,16 @@ import Link from '../atoms/hyperTexts/Link'; import { InfoIcon } from '../atoms/icons/fr-fi-icons'; import './ConfirmationModal.css'; -const ConfirmationModal = ({ +type ConfirmationModalProps = { + handleConfirm: MouseEventHandler; + confirmLabel?: string; + handleCancel: (event: Event | SyntheticEvent) => void; + cancelLabel?: string; + title: string; + children: React.ReactNode; +}; + +const ConfirmationModal: React.FC = ({ handleConfirm, confirmLabel = 'Confirmer', handleCancel, @@ -18,7 +27,7 @@ const ConfirmationModal = ({ titleText={title} onExit={handleCancel} focusDialog - getApplicationNode={() => document.getElementById('root')} + getApplicationNode={() => document.getElementById('root') as Element} scrollDisabled={false} alert > diff --git a/frontend/src/components/organisms/ErrorBoundaryFallback.js b/frontend/src/components/organisms/ErrorBoundaryFallback.tsx similarity index 88% rename from frontend/src/components/organisms/ErrorBoundaryFallback.js rename to frontend/src/components/organisms/ErrorBoundaryFallback.tsx index 653d646a6..45677d717 100644 --- a/frontend/src/components/organisms/ErrorBoundaryFallback.js +++ b/frontend/src/components/organisms/ErrorBoundaryFallback.tsx @@ -1,11 +1,11 @@ import React from 'react'; -import Alert from '../atoms/Alert'; +import Alert, { AlertType } from '../atoms/Alert'; import IndexPointingRightEmoji from '../atoms/icons/IndexPointingRightEmoji'; export const ErrorBoundaryFallback = () => (
- +

Une erreur est survenue. Merci de réessayer ultérieurement. Vous pouvez également essayer d'utiliser un autre navigateur (ex: Firefox diff --git a/frontend/src/components/organisms/Footer.js b/frontend/src/components/organisms/Footer.tsx similarity index 100% rename from frontend/src/components/organisms/Footer.js rename to frontend/src/components/organisms/Footer.tsx diff --git a/frontend/src/components/organisms/FormRouter.js b/frontend/src/components/organisms/FormRouter.tsx similarity index 80% rename from frontend/src/components/organisms/FormRouter.js rename to frontend/src/components/organisms/FormRouter.tsx index 2c8d94005..e7c05065c 100644 --- a/frontend/src/components/organisms/FormRouter.js +++ b/frontend/src/components/organisms/FormRouter.tsx @@ -5,13 +5,14 @@ import Loader from '../atoms/Loader'; import Enrollment from '../templates/Enrollment'; import { useFullDataProvider } from '../templates/hooks/use-full-data-provider'; import NotFound from './NotFound'; +import { TargetAPI } from '../../config/data-provider-configurations'; const FormRouter = () => { const { targetApi: targetApiFromUrl } = useParams(); - const targetApi = targetApiFromUrl.replace(/-/g, '_'); + const targetApi = (targetApiFromUrl as TargetAPI).replace(/-/g, '_'); const { Component, configuration, notFound } = useFullDataProvider({ - targetApi: targetApi, + targetApi: targetApi as TargetAPI, }); if (notFound) { diff --git a/frontend/src/components/organisms/Header.js b/frontend/src/components/organisms/Header.tsx similarity index 96% rename from frontend/src/components/organisms/Header.js rename to frontend/src/components/organisms/Header.tsx index cf7df6244..793882a2b 100644 --- a/frontend/src/components/organisms/Header.js +++ b/frontend/src/components/organisms/Header.tsx @@ -13,8 +13,8 @@ import { isEmpty } from 'lodash'; const { REACT_APP_BACK_HOST: BACK_HOST } = process.env; const Header = () => { - const [displayContactLink, setDisplayContactLink] = useState(); - const [targetApi, setTargetApi] = useState(); + const [displayContactLink, setDisplayContactLink] = useState(); + const [targetApi, setTargetApi] = useState(); let location = useLocation(); const { user, logout } = useAuth(); const { dataProviderConfigurations } = useDataProviderConfigurations(); @@ -119,7 +119,7 @@ const Header = () => { )} - {user && user.roles.includes('administrator') && ( + {user && user?.roles?.includes('administrator') && ( <>

  • ); } + + static defaultProps = { + children: null, + className: 'panel', + }; } -ScrollablePanel.propTypes = { - className: PropTypes.string, - children: PropTypes.node, - scrollableId: PropTypes.string.isRequired, -}; +interface ScrollableLinkProps { + scrollableId: string; + children: ReactNode; +} -ScrollablePanel.defaultProps = { - children: null, - className: 'panel', -}; +interface ScrollableLinkState { + selected: boolean; +} -export class ScrollableLink extends Component { - constructor(props) { +export class ScrollableLink extends Component< + ScrollableLinkProps, + ScrollableLinkState +> { + constructor(props: ScrollableLinkProps) { super(props); this.state = { selected: false, @@ -81,40 +91,35 @@ export class ScrollableLink extends Component { }, 16 * 8); componentDidMount() { - // Hackish way to trigger initial scroll. - // As it's difficult to determine when all ScrollablePanels are fully rendered, - // we suppose that after 500ms this is the case to avoid complex implementation. - // Then we simply trigger the link by clicking on it. delay(() => { const hash = getWindowHash(); if (!this.state.selected && this.props.scrollableId === hash) { - document - .querySelector( + ( + document.querySelector( `.fr-sidemenu__item a[href="#${this.props.scrollableId}"]` - ) - ?.click(); + ) as HTMLElement + )?.click(); } }, 500); - return window.addEventListener('scroll', this.handleScroll); + window.addEventListener('scroll', this.handleScroll); } componentWillUnmount() { this.handleScroll.cancel(); - return window.removeEventListener('scroll', this.handleScroll); + window.removeEventListener('scroll', this.handleScroll); } render() { - const { scrollableId, children, style } = this.props; + const { scrollableId, children } = this.props; return (
  • {children} @@ -122,9 +127,3 @@ export class ScrollableLink extends Component { ); } } - -ScrollableLink.propTypes = { - scrollableId: PropTypes.string.isRequired, - children: PropTypes.node.isRequired, - style: PropTypes.object, -}; diff --git a/frontend/src/components/organisms/form-sections/ArchivedSection.js b/frontend/src/components/organisms/form-sections/ArchivedSection.tsx similarity index 100% rename from frontend/src/components/organisms/form-sections/ArchivedSection.js rename to frontend/src/components/organisms/form-sections/ArchivedSection.tsx diff --git a/frontend/src/components/organisms/form-sections/CguSection/index.js b/frontend/src/components/organisms/form-sections/CguSection/index.tsx similarity index 87% rename from frontend/src/components/organisms/form-sections/CguSection/index.js rename to frontend/src/components/organisms/form-sections/CguSection/index.tsx index 55eb892a0..199c23613 100644 --- a/frontend/src/components/organisms/form-sections/CguSection/index.js +++ b/frontend/src/components/organisms/form-sections/CguSection/index.tsx @@ -1,5 +1,4 @@ import React, { useContext } from 'react'; -import PropTypes from 'prop-types'; import { isEmpty } from 'lodash'; import { ScrollablePanel } from '../../Scrollable'; import { FormContext } from '../../../templates/Form'; @@ -9,7 +8,19 @@ import Link from '../../../atoms/hyperTexts/Link'; const SECTION_LABEL = 'Les modalités d’utilisation'; const SECTION_ID = encodeURIComponent(SECTION_LABEL); -export const CguSection = ({ cguLink, additionalTermsOfUse = [] }) => { +type CguSectionProps = { + cguLink: string; + additionalTermsOfUse: { id: string; label: string }[]; +}; + +interface CguSectionType extends React.FC { + sectionLabel: string; +} + +export const CguSection: CguSectionType = ({ + cguLink, + additionalTermsOfUse = [], +}) => { const { disabled, onChange, @@ -62,10 +73,4 @@ export const CguSection = ({ cguLink, additionalTermsOfUse = [] }) => { CguSection.sectionLabel = SECTION_LABEL; -CguSection.propTypes = { - CguDescription: PropTypes.func, - cguLink: PropTypes.string.isRequired, - AdditionalCguContent: PropTypes.func, -}; - export default CguSection; diff --git a/frontend/src/components/organisms/form-sections/DemarcheSection/DemarcheSectionReadOnly.tsx b/frontend/src/components/organisms/form-sections/DemarcheSection/DemarcheSectionReadOnly.tsx index e294b39b9..48ced76a6 100644 --- a/frontend/src/components/organisms/form-sections/DemarcheSection/DemarcheSectionReadOnly.tsx +++ b/frontend/src/components/organisms/form-sections/DemarcheSection/DemarcheSectionReadOnly.tsx @@ -10,7 +10,7 @@ import { ScopeConfiguration } from '../DonneesSection/Scopes'; const valueToLabel = ( key: string, - scopesConfiguration: ScopeConfiguration[] + scopesConfiguration?: ScopeConfiguration[] ) => { const scope = find(scopesConfiguration, { value: key }); if (scope) { @@ -22,7 +22,7 @@ const valueToLabel = ( type DemarcheSectionReadOnlyProps = { scrollableId: string; - scopesConfiguration: ScopeConfiguration[]; + scopesConfiguration?: ScopeConfiguration[]; }; export const DemarcheSectionReadOnly: React.FC< diff --git a/frontend/src/components/organisms/form-sections/DemarcheSection/DemarcheSectionSelect.js b/frontend/src/components/organisms/form-sections/DemarcheSection/DemarcheSectionSelect.tsx similarity index 89% rename from frontend/src/components/organisms/form-sections/DemarcheSection/DemarcheSectionSelect.js rename to frontend/src/components/organisms/form-sections/DemarcheSection/DemarcheSectionSelect.tsx index 6ba0e3a9e..013cf8a78 100644 --- a/frontend/src/components/organisms/form-sections/DemarcheSection/DemarcheSectionSelect.js +++ b/frontend/src/components/organisms/form-sections/DemarcheSection/DemarcheSectionSelect.tsx @@ -1,5 +1,12 @@ import { get, has, isEmpty, merge, pickBy } from 'lodash'; -import { useCallback, useContext, useEffect, useMemo, useState } from 'react'; +import { + ChangeEventHandler, + useCallback, + useContext, + useEffect, + useMemo, + useState, +} from 'react'; import { findModifiedFields } from '../../../../lib'; import SelectInput from '../../../atoms/inputs/SelectInput'; import { FormContext } from '../../../templates/Form'; @@ -7,16 +14,24 @@ import ConfirmationModal from '../../ConfirmationModal'; import { ScrollablePanel } from '../../Scrollable'; import DemarcheSectionSelectNotification from './DemarcheSectionSelectNotification'; -export const DemarcheSectionSelect = ({ body, scrollableId }) => { +export const DemarcheSectionSelect = ({ + body, + scrollableId, +}: { + body: React.ReactNode; + scrollableId: string; +}) => { const { disabled, onChange, enrollment, demarches } = useContext(FormContext); const { demarche: selectedDemarcheId } = enrollment; const [isLoading, setIsLoading] = useState(false); - const [confirmNewDemarcheId, setConfirmNewDemarcheId] = useState(false); + const [confirmNewDemarcheId, setConfirmNewDemarcheId] = useState< + string | null + >(null); // reducer expects onChange events from HTML Element const selectNewDemarche = useCallback( - (newDemarcheId) => { + (newDemarcheId: string) => { onChange({ target: { value: newDemarcheId, name: 'demarche' } }); }, [onChange] @@ -36,7 +51,7 @@ export const DemarcheSectionSelect = ({ body, scrollableId }) => { [demarches, enrollment.technical_team_value, selectedDemarcheId] ); - const onSelectDemarche = (event) => { + const onSelectDemarche: ChangeEventHandler = (event) => { let newDemarcheId = event.target.value || 'default'; const preFilledEnrollment = merge( @@ -97,9 +112,9 @@ export const DemarcheSectionSelect = ({ body, scrollableId }) => { {confirmNewDemarcheId && ( setConfirmNewDemarcheId(false)} + handleCancel={() => setConfirmNewDemarcheId(null)} handleConfirm={() => { - setConfirmNewDemarcheId(false); + setConfirmNewDemarcheId(null); selectNewDemarche(confirmNewDemarcheId); }} confirmLabel="Changer tout de même" diff --git a/frontend/src/components/organisms/form-sections/DemarcheSection/DemarcheSectionSelectNotification.js b/frontend/src/components/organisms/form-sections/DemarcheSection/DemarcheSectionSelectNotification.tsx similarity index 72% rename from frontend/src/components/organisms/form-sections/DemarcheSection/DemarcheSectionSelectNotification.js rename to frontend/src/components/organisms/form-sections/DemarcheSection/DemarcheSectionSelectNotification.tsx index 36b6cc2b4..6f68980e6 100644 --- a/frontend/src/components/organisms/form-sections/DemarcheSection/DemarcheSectionSelectNotification.js +++ b/frontend/src/components/organisms/form-sections/DemarcheSection/DemarcheSectionSelectNotification.tsx @@ -4,12 +4,17 @@ import { get, isEmpty } from 'lodash'; import Alert from '../../../atoms/Alert'; import HighVoltageEmoji from '../../../atoms/icons/HighVoltageEmoji'; import Link from '../../../atoms/hyperTexts/Link'; +import { Demarche } from '../../../templates/Form/enrollmentReducer'; -const DemarcheSectionNotification = ({ - isLoading = false, - selectedDemarcheId, - demarches, -}) => ( +type DemarcheSectionNotificationProps = { + isLoading: boolean; + selectedDemarcheId: string; + demarches: Record; +}; + +const DemarcheSectionNotification: React.FC< + DemarcheSectionNotificationProps +> = ({ isLoading = false, selectedDemarcheId, demarches }) => ( <> {isLoading ? ( @@ -23,14 +28,12 @@ const DemarcheSectionNotification = ({ >
    Vous avez sélectionné le cas d’usage «{' '} - - {get(demarches, selectedDemarcheId, {}).label || selectedDemarcheId} - + {get(demarches, selectedDemarcheId)?.label || selectedDemarcheId} {' '} ».{' '} {!isEmpty(demarches) && selectedDemarcheId && - get(demarches, selectedDemarcheId, {}).about && ( + get(demarches, selectedDemarcheId)?.about && ( <> Pour en savoir plus sur ce cas d’usage, vous pouvez en consulter la{' '} @@ -38,7 +41,7 @@ const DemarcheSectionNotification = ({ inline newTab aria-label={`Plus d’information sur le cas d’usage « ${selectedDemarcheId} »`} - href={get(demarches, selectedDemarcheId, {}).about} + href={get(demarches, selectedDemarcheId)?.about} > fiche explicative diff --git a/frontend/src/components/organisms/form-sections/DescriptionSection.js b/frontend/src/components/organisms/form-sections/DescriptionSection.tsx similarity index 100% rename from frontend/src/components/organisms/form-sections/DescriptionSection.js rename to frontend/src/components/organisms/form-sections/DescriptionSection.tsx diff --git a/frontend/src/components/organisms/form-sections/FranceConnectPlusSection.js b/frontend/src/components/organisms/form-sections/FranceConnectPlusSection.tsx similarity index 100% rename from frontend/src/components/organisms/form-sections/FranceConnectPlusSection.js rename to frontend/src/components/organisms/form-sections/FranceConnectPlusSection.tsx diff --git a/frontend/src/components/organisms/form-sections/HeadSection/EnrollmentHasCopiesNotification.js b/frontend/src/components/organisms/form-sections/HeadSection/EnrollmentHasCopiesNotification.tsx similarity index 66% rename from frontend/src/components/organisms/form-sections/HeadSection/EnrollmentHasCopiesNotification.js rename to frontend/src/components/organisms/form-sections/HeadSection/EnrollmentHasCopiesNotification.tsx index 686b934e4..73fbf34bb 100644 --- a/frontend/src/components/organisms/form-sections/HeadSection/EnrollmentHasCopiesNotification.js +++ b/frontend/src/components/organisms/form-sections/HeadSection/EnrollmentHasCopiesNotification.tsx @@ -2,14 +2,19 @@ import React, { useEffect, useState } from 'react'; import { getEnrollmentCopies } from '../../../../services/enrollments'; import { isEmpty } from 'lodash'; import { Linkify } from '../../../molecules/Linkify'; -import Alert from '../../../atoms/Alert'; +import Alert, { AlertType } from '../../../atoms/Alert'; +import { Enrollment } from '../../../templates/InstructorEnrollmentList'; -const EnrollmentHasCopiesNotification = ({ enrollmentId }) => { - const [enrollmentCopies, setEnrollmentCopies] = useState(false); +const EnrollmentHasCopiesNotification = ({ + enrollmentId, +}: { + enrollmentId: number; +}) => { + const [enrollmentCopies, setEnrollmentCopies] = useState([]); useEffect(() => { async function fetchEnrollmentCopies() { - if (!enrollmentId) return setEnrollmentCopies(false); + if (!enrollmentId) return setEnrollmentCopies([]); const enrollmentsCopies = await getEnrollmentCopies(enrollmentId); @@ -25,7 +30,7 @@ const EnrollmentHasCopiesNotification = ({ enrollmentId }) => { return ( diff --git a/frontend/src/components/organisms/form-sections/HeadSection/HasNextEnrollmentsNotification.js b/frontend/src/components/organisms/form-sections/HeadSection/HasNextEnrollmentsNotification.tsx similarity index 81% rename from frontend/src/components/organisms/form-sections/HeadSection/HasNextEnrollmentsNotification.js rename to frontend/src/components/organisms/form-sections/HeadSection/HasNextEnrollmentsNotification.tsx index d7b391302..5c1c7c510 100644 --- a/frontend/src/components/organisms/form-sections/HeadSection/HasNextEnrollmentsNotification.js +++ b/frontend/src/components/organisms/form-sections/HeadSection/HasNextEnrollmentsNotification.tsx @@ -5,14 +5,19 @@ import { Linkify } from '../../../molecules/Linkify'; import Alert from '../../../atoms/Alert'; import IndexPointingRightEmoji from '../../../atoms/icons/IndexPointingRightEmoji'; import { useDataProviderConfigurations } from '../../../templates/hooks/use-data-provider-configurations'; +import { Enrollment } from '../../../templates/InstructorEnrollmentList'; -const HasNextEnrollmentsNotification = ({ enrollmentId }) => { - const [nextEnrollments, setNextEnrollments] = useState(false); +const HasNextEnrollmentsNotification = ({ + enrollmentId, +}: { + enrollmentId: number; +}) => { + const [nextEnrollments, setNextEnrollments] = useState([]); const { dataProviderConfigurations } = useDataProviderConfigurations(); useEffect(() => { async function fetchNextEnrollments() { - if (!enrollmentId) return setNextEnrollments(false); + if (!enrollmentId) return setNextEnrollments([]); const fetchedNextEnrollments = await getNextEnrollments(enrollmentId); @@ -22,7 +27,7 @@ const HasNextEnrollmentsNotification = ({ enrollmentId }) => { fetchNextEnrollments(); }, [enrollmentId]); - const formatNextEnrollment = (enrollment) => + const formatNextEnrollment = (enrollment: Enrollment) => `${ dataProviderConfigurations?.[enrollment.target_api]?.label || enrollment.target_api diff --git a/frontend/src/components/organisms/form-sections/HeadSection/NotificationSubSection.js b/frontend/src/components/organisms/form-sections/HeadSection/NotificationSubSection.tsx similarity index 89% rename from frontend/src/components/organisms/form-sections/HeadSection/NotificationSubSection.js rename to frontend/src/components/organisms/form-sections/HeadSection/NotificationSubSection.tsx index e47633cda..761f225a2 100644 --- a/frontend/src/components/organisms/form-sections/HeadSection/NotificationSubSection.js +++ b/frontend/src/components/organisms/form-sections/HeadSection/NotificationSubSection.tsx @@ -1,7 +1,7 @@ import { get } from 'lodash'; import { useContext } from 'react'; import { useLocation } from 'react-router-dom'; -import Alert from '../../../atoms/Alert'; +import Alert, { AlertType } from '../../../atoms/Alert'; import { FormContext } from '../../../templates/Form'; import EnrollmentHasCopiesNotification from './EnrollmentHasCopiesNotification'; import HasNextEnrollmentsNotification from './HasNextEnrollmentsNotification'; @@ -17,7 +17,7 @@ export const NotificationSubSection = () => { return ( <> {get(location, 'state.source') === 'copy-authorization-request' && ( - + Vous trouverez ci-dessous une copie de votre habilitation initiale. Merci de vérifier que ces informations sont à jour puis cliquez sur "Soumettre la demande d’habilitation". @@ -28,7 +28,7 @@ export const NotificationSubSection = () => { {acl.update && ( - + Pensez à enregistrer régulièrement vos modifications. )} diff --git a/frontend/src/components/organisms/form-sections/HeadSection/index.js b/frontend/src/components/organisms/form-sections/HeadSection/index.tsx similarity index 91% rename from frontend/src/components/organisms/form-sections/HeadSection/index.js rename to frontend/src/components/organisms/form-sections/HeadSection/index.tsx index 57504a3b4..6eb6e0296 100644 --- a/frontend/src/components/organisms/form-sections/HeadSection/index.js +++ b/frontend/src/components/organisms/form-sections/HeadSection/index.tsx @@ -1,6 +1,6 @@ import { isEmpty } from 'lodash'; import { useContext } from 'react'; -import Badge from '../../../atoms/hyperTexts/Badge'; +import Badge, { BadgeType } from '../../../atoms/hyperTexts/Badge'; import Link from '../../../atoms/hyperTexts/Link'; import { StatusBadge } from '../../../molecules/StatusBadge'; import { FormContext } from '../../../templates/Form'; @@ -23,7 +23,7 @@ export const HeadSection = () => { <>Vous demandez l’accès à

    {label}

    - {id && Habilitation n°{id}} + {id && Habilitation n°{id}} {copied_from_enrollment_id && ( diff --git a/frontend/src/components/organisms/form-sections/OrganisationSection/DisconnectionModal.js b/frontend/src/components/organisms/form-sections/OrganisationSection/DisconnectionModal.js deleted file mode 100644 index 31314fe57..000000000 --- a/frontend/src/components/organisms/form-sections/OrganisationSection/DisconnectionModal.js +++ /dev/null @@ -1,16 +0,0 @@ -import ConfirmationModal from '../../ConfirmationModal'; - -export const DisconnectionModal = ({ disconnectionUrl, handleCancel }) => { - return ( - (window.location = disconnectionUrl)} - handleCancel={handleCancel} - > - Afin de mettre à jour vos informations personnelles, vous allez être - déconnecté. - - ); -}; - -export default DisconnectionModal; diff --git a/frontend/src/components/organisms/form-sections/OrganisationSection/DisconnectionModal.tsx b/frontend/src/components/organisms/form-sections/OrganisationSection/DisconnectionModal.tsx new file mode 100644 index 000000000..ad61b0a16 --- /dev/null +++ b/frontend/src/components/organisms/form-sections/OrganisationSection/DisconnectionModal.tsx @@ -0,0 +1,25 @@ +import { SyntheticEvent } from 'react'; +import ConfirmationModal from '../../ConfirmationModal'; + +type DisconnectionModalProps = { + disconnectionUrl: string; + handleCancel: (event: Event | SyntheticEvent) => void; +}; + +export const DisconnectionModal: React.FC = ({ + disconnectionUrl, + handleCancel, +}) => { + return ( + (window.location.href = disconnectionUrl)} + handleCancel={handleCancel} + > + Afin de mettre à jour vos informations personnelles, vous allez être + déconnecté. + + ); +}; + +export default DisconnectionModal; diff --git a/frontend/src/components/organisms/form-sections/OrganisationSection/OrganisationCard.js b/frontend/src/components/organisms/form-sections/OrganisationSection/OrganisationCard.tsx similarity index 90% rename from frontend/src/components/organisms/form-sections/OrganisationSection/OrganisationCard.js rename to frontend/src/components/organisms/form-sections/OrganisationSection/OrganisationCard.tsx index fb216aeab..e242bb669 100644 --- a/frontend/src/components/organisms/form-sections/OrganisationSection/OrganisationCard.js +++ b/frontend/src/components/organisms/form-sections/OrganisationSection/OrganisationCard.tsx @@ -2,7 +2,7 @@ import { isEmpty } from 'lodash'; import { useCallback, useContext, useEffect, useState } from 'react'; import { isValidNAFCode } from '../../../../lib'; import { getCachedOrganizationInformation } from '../../../../services/external'; -import Alert from '../../../atoms/Alert'; +import Alert, { AlertType } from '../../../atoms/Alert'; import Button from '../../../atoms/hyperTexts/Button'; import Loader from '../../../atoms/Loader'; import { Card, CardHead } from '../../../molecules/Card'; @@ -43,7 +43,13 @@ export const OrganisationCard = () => { const { user, isLoading } = useAuth(); const updateOrganizationInfo = useCallback( - ({ organization_id, siret }) => { + ({ + organization_id, + siret, + }: { + organization_id: number; + siret: string; + }) => { onChange({ target: { name: 'organization_id', @@ -63,7 +69,8 @@ export const OrganisationCard = () => { !isUserEnrollmentLoading && !disabled && !organization_id && - !isEmpty(user.organizations) + user?.organizations && + user.organizations.length > 0 ) { updateOrganizationInfo({ organization_id: user.organizations[0].id, @@ -79,7 +86,7 @@ export const OrganisationCard = () => { ]); useEffect(() => { - const fetchOrganizationInfo = async (siret) => { + const fetchOrganizationInfo = async (siret: string) => { try { setIsOrganizationInfoLoading(true); const { @@ -131,14 +138,14 @@ export const OrganisationCard = () => { } }, [siret]); - const onOrganizationChange = (new_organization_id) => { + const onOrganizationChange = (new_organization_id: number) => { setShowOrganizationPrompt(false); - if (!isEmpty(user.organizations)) { + if (!isEmpty(user?.organizations)) { updateOrganizationInfo({ organization_id: new_organization_id, - siret: user.organizations.find((o) => o.id === new_organization_id) - .siret, + siret: user?.organizations?.find((o) => o.id === new_organization_id) + ?.siret as string, }); } }; @@ -157,21 +164,21 @@ export const OrganisationCard = () => {

    Vous faites cette demande pour

    {activite && !isValidNAFCode(target_api, activite) && (
    - + Votre organisme ne semble pas être éligible
    )} {showOrganizationInfoNotFound && (
    - + Cet établissement est fermé ou le SIRET est invalide.
    )} {showOrganizationInfoError && (
    - + Erreur inconnue lors de la récupération des informations de cet établissement. @@ -215,7 +222,7 @@ export const OrganisationCard = () => { onSelect={onOrganizationChange} onJoinOrganization={onJoinOrganization} onClose={() => setShowOrganizationPrompt(false)} - organizations={user.organizations} + organizations={user?.organizations} /> )} diff --git a/frontend/src/components/organisms/form-sections/OrganisationSection/OrganizationPrompt.js b/frontend/src/components/organisms/form-sections/OrganisationSection/OrganizationPrompt.tsx similarity index 56% rename from frontend/src/components/organisms/form-sections/OrganisationSection/OrganizationPrompt.js rename to frontend/src/components/organisms/form-sections/OrganisationSection/OrganizationPrompt.tsx index fb184ec45..28e315a0c 100644 --- a/frontend/src/components/organisms/form-sections/OrganisationSection/OrganizationPrompt.js +++ b/frontend/src/components/organisms/form-sections/OrganisationSection/OrganizationPrompt.tsx @@ -1,19 +1,35 @@ -import React, { useMemo } from 'react'; +import React, { + ChangeEventHandler, + MouseEventHandler, + SyntheticEvent, + useMemo, +} from 'react'; import RadioInput from '../../../atoms/inputs/RadioInput'; import ConfirmationModal from '../../ConfirmationModal'; +import { User } from '../../../templates/InstructorEnrollmentList'; -const OrganizationPrompt = ({ +type OrganizationPromptProps = { + selectedOrganizationId: number; + onSelect: Function; + onClose: (event: Event | SyntheticEvent) => void; + onJoinOrganization: MouseEventHandler; + organizations: User['organizations']; +}; + +const OrganizationPrompt: React.FC = ({ selectedOrganizationId, onSelect, onClose, onJoinOrganization, organizations, }) => { - const onChange = ({ target: { value } }) => onSelect(parseInt(value)); + const onChange: ChangeEventHandler = ({ + target: { value }, + }) => onSelect(parseInt(value)); const options = useMemo( () => - organizations.map(({ id, label, siret }) => ({ + organizations?.map(({ id, label, siret }) => ({ id, label: label || siret, })), diff --git a/frontend/src/components/organisms/form-sections/OrganisationSection/PersonalInformationCard.js b/frontend/src/components/organisms/form-sections/OrganisationSection/PersonalInformationCard.tsx similarity index 77% rename from frontend/src/components/organisms/form-sections/OrganisationSection/PersonalInformationCard.js rename to frontend/src/components/organisms/form-sections/OrganisationSection/PersonalInformationCard.tsx index 96e7a58e6..e2a594789 100644 --- a/frontend/src/components/organisms/form-sections/OrganisationSection/PersonalInformationCard.js +++ b/frontend/src/components/organisms/form-sections/OrganisationSection/PersonalInformationCard.tsx @@ -17,13 +17,19 @@ export const PersonalInformationCard = () => { const { isLoading } = useAuth(); - const [personalInformation, setPersonalInformation] = useState({}); + const [personalInformation, setPersonalInformation] = useState<{ + given_name: string; + family_name: string; + email: string; + phone_number: string; + job: string; + } | null>(null); const [showDisconnectionPrompt, setShowDisconnectionPrompt] = useState(false); useEffect(() => { const firstDemandeur = !isEmpty(team_members) && - team_members.find(({ type }) => type === 'demandeur'); + team_members.find(({ type }: { type: string }) => type === 'demandeur'); if (firstDemandeur) { // note that they might be more than one demandeur // for now we just display the first demandeur found @@ -38,7 +44,7 @@ export const PersonalInformationCard = () => {

    Vous êtes

    - {personalInformation.given_name} {personalInformation.family_name} + {personalInformation?.given_name} {personalInformation?.family_name} {!disabled && (