Skip to content

Commit

Permalink
Merge pull request #432 from MTES-MCT/431-ajout-modal-creation-de-rap…
Browse files Browse the repository at this point in the history
…port-ulam

feat(431) Ajout Dialog Creation Mission ULAM (frontend only)
  • Loading branch information
aleckvincent authored Dec 17, 2024
2 parents 6793584 + 0c64866 commit fdd0350
Show file tree
Hide file tree
Showing 10 changed files with 462 additions and 9 deletions.
4 changes: 4 additions & 0 deletions frontend/src/features/common/types/mission-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,7 @@ export enum CompletenessForStatsStatusEnum {
COMPLETE = 'COMPLETE',
INCOMPLETE = 'INCOMPLETE'
}




Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { FieldProps } from 'formik'
import { useAbstractFormikSubForm } from './use-abstract-formik-sub-form.tsx'
import { MissionULAMGeneralInfoInitial } from '../types/mission-types.ts'
import { useDate } from './use-date.tsx'

export type MissionULAMGeneralInfoInitialInput = MissionULAMGeneralInfoInitial
export function useMissionGeneralInformationsForm(
name: string,
fieldFormik: FieldProps<MissionULAMGeneralInfoInitial>
) {

const { preprocessDateForPicker, postprocessDateFromPicker } = useDate()


const fromFieldValueToInput = (data: MissionULAMGeneralInfoInitial) => {
return {
...data,
dates: [
preprocessDateForPicker(data.startDateTimeUtc),
preprocessDateForPicker(data.endDateTimeUtc)
]
}
}


const fromInputToFieldValue = (value: MissionULAMGeneralInfoInitialInput): MissionULAMGeneralInfoInitial => {
const { dates, missionReportType, missionType, reinforcementType, nbHourAtSea } = value
return {
startDateTimeUtc: postprocessDateFromPicker(dates[0]),
endDateTimeUtc: postprocessDateFromPicker(dates[1]),
missionType,
missionReportType,
reinforcementType,
nbHourAtSea
}
}

const { initValue, handleSubmit } = useAbstractFormikSubForm<MissionULAMGeneralInfoInitial, MissionULAMGeneralInfoInitialInput>(
name,
fieldFormik,
fromFieldValueToInput,
fromInputToFieldValue
)


return {
initValue,
handleSubmit,
}
}
70 changes: 70 additions & 0 deletions frontend/src/v2/features/common/hooks/use-mission-type.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { MissionReinforcementTypeEnum, MissionReportTypeEnum, MissionTypeEnum } from '../types/mission-types.ts'


interface MissionHook {
getMissionTypeLabel: (type?: MissionTypeEnum) => string | undefined
getReinforcementTypeLabel: (type?: MissionReinforcementTypeEnum) => string | undefined
getReportTypeLabel: (type?: MissionReportTypeEnum) => string | undefined
missionTypeOptions: { label: string; value: MissionTypeEnum }[]
reinforcementTypeOptions: { label: string; value: MissionReinforcementTypeEnum }[]
reportTypeOptions: { label: string; value: MissionReportTypeEnum }[]
}

const MISSION_TYPE_REGISTRY: Record<MissionTypeEnum, string> = {
[MissionTypeEnum.LAND]: 'Terre',
[MissionTypeEnum.SEA]: 'Mer',
[MissionTypeEnum.AIR]: 'Air'
}

const REINFORCEMENT_TYPE_REGISTRY: Record<MissionReinforcementTypeEnum, string> = {
[MissionReinforcementTypeEnum.PATROL]: 'Patrouille (mission PAM)',
[MissionReinforcementTypeEnum.JDP]: 'JDP',
[MissionReinforcementTypeEnum.DIRM]: 'DIRM',
[MissionReinforcementTypeEnum.OTHER_ULAM]: 'Autre ULAM',
[MissionReinforcementTypeEnum.SEA_TRAINER]: 'Formateur ESP Mer',
[MissionReinforcementTypeEnum.OTHER]: 'Autre'
}

const REPORT_TYPE_REGISTRY: Record<MissionReportTypeEnum, string> = {
[MissionReportTypeEnum.FIELD_REPORT]: 'Rapport avec sortie terrain',
[MissionReportTypeEnum.OFFICE_REPORT]: 'Rapport sans sortie terrain (admin. uniquement)',
[MissionReportTypeEnum.EXTERNAL_REINFORCEMENT_TIME_REPORT]: 'Rapport de temps agent en renfort extérieur'
}

export function useMissionType(): MissionHook {
const getMissionTypeLabel = (type?: MissionTypeEnum): string | undefined =>
type ? MISSION_TYPE_REGISTRY[type] : ''

const getReinforcementTypeLabel = (type?: MissionReinforcementTypeEnum): string | undefined =>
type ? REINFORCEMENT_TYPE_REGISTRY[type] : ''

const getReportTypeLabel = (type?: MissionReportTypeEnum): string | undefined =>
type ? REPORT_TYPE_REGISTRY[type] : ''

const getMissionTypeOptions = () =>
Object.keys(MissionTypeEnum).map(key => ({
value: MissionTypeEnum[key as keyof typeof MissionTypeEnum],
label: MISSION_TYPE_REGISTRY[key as keyof typeof MissionTypeEnum]
}))

const getReinforcementTypeOptions = () =>
Object.keys(MissionReinforcementTypeEnum).map(key => ({
value: MissionReinforcementTypeEnum[key as keyof typeof MissionReinforcementTypeEnum],
label: REINFORCEMENT_TYPE_REGISTRY[key as keyof typeof MissionReinforcementTypeEnum]
}))

const getReportTypeOptions = () =>
Object.keys(MissionReportTypeEnum).map(key => ({
value: MissionReportTypeEnum[key as keyof typeof MissionReportTypeEnum],
label: REPORT_TYPE_REGISTRY[key as keyof typeof MissionReportTypeEnum]
}))

return {
getMissionTypeLabel,
getReinforcementTypeLabel,
getReportTypeLabel,
missionTypeOptions: getMissionTypeOptions(),
reinforcementTypeOptions: getReinforcementTypeOptions(),
reportTypeOptions: getReportTypeOptions()
}
}
33 changes: 33 additions & 0 deletions frontend/src/v2/features/common/types/mission-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export enum MissionTypeEnum {
AIR = 'AIR',
LAND = 'LAND',
SEA = 'SEA'
}

export type MissionULAMGeneralInfoInitial = {
startDateTimeUtc: string
endDateTimeUtc: string
missionTypes: MissionTypeEnum[]
missionReportType?: MissionReportTypeEnum
reinforcementType?: MissionReinforcementTypeEnum
nbHourAtSea?: number
}

export enum MissionReportTypeEnum {
FIELD_REPORT = 'FIELD_REPORT',
OFFICE_REPORT = 'OFFICE_REPORT',
EXTERNAL_REINFORCEMENT_TIME_REPORT = 'EXTERNAL_REINFORCEMENT_TIME_REPORT'
}

export enum MissionReinforcementTypeEnum {
PATROL = 'PATROL',
JDP = 'JDP',
OTHER_ULAM = 'OTHER_ULAM',
SEA_TRAINER = 'SEA_TRAINER',
OTHER = 'OTHER',
DIRM = 'DIRM'
}




Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { render, screen, fireEvent } from '../../../../../../test-utils.tsx'
import { describe, it, vi } from 'vitest'
import MissionCreateDialog from '../mission-create-dialog.tsx'

describe('MissionCreateDialog', () => {
it('renders the dialog when isOpen is true', () => {
render(<MissionCreateDialog isOpen={true} onClose={vi.fn()} />)
expect(screen.getByText("Création d'un rapport de mission")).toBeInTheDocument()
})

it('does not render the dialog when isOpen is false', () => {
render(<MissionCreateDialog isOpen={false} onClose={vi.fn()} />)
expect(screen.queryByText("Création d'un rapport de mission")).not.toBeInTheDocument()
})

it('calls onClose when the close button is clicked', () => {
const handleClose = vi.fn()
render(<MissionCreateDialog isOpen={true} onClose={handleClose} />)

fireEvent.click(screen.getByText('Annuler'))
expect(handleClose).toHaveBeenCalled()
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@

import { MissionTypeEnum } from '@common/types/env-mission-types'
import MissionGeneralInformationUlamFormNew from '../mission-general-information-ulam-form-new.tsx'
import { render, fireEvent, screen } from '../../../../../../test-utils.tsx'



describe('MissionGeneralInformationUlamFormNew', () => {
it('renders the form with initial values', () => {
render(
<MissionGeneralInformationUlamFormNew
onClose={vi.fn()}
/>
)

expect(screen.getByText('Type de mission')).toBeInTheDocument()
})

it('check if form submitted', () => {
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});

render(
<MissionGeneralInformationUlamFormNew
onClose={vi.fn()}
/>
);

fireEvent.click(screen.getByText('Créer le rapport'));

// TODO: a remplacer par le check d'un appel api
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Form Submitted'), expect.any(Object));

consoleSpy.mockRestore();
});
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React, { FC, useEffect, useState } from 'react'
import { Dialog, THEME } from '@mtes-mct/monitor-ui'
import { Stack } from 'rsuite'
import { MissionTypeEnum } from '@common/types/env-mission-types.ts'

Check warning on line 4 in frontend/src/v2/features/ulam/components/element/mission-create-dialog.tsx

View workflow job for this annotation

GitHub Actions / build-and-test-frontend

'MissionTypeEnum' is defined but never used

Check warning on line 4 in frontend/src/v2/features/ulam/components/element/mission-create-dialog.tsx

View workflow job for this annotation

GitHub Actions / build-and-test-frontend

'MissionTypeEnum' is defined but never used

Check warning on line 4 in frontend/src/v2/features/ulam/components/element/mission-create-dialog.tsx

View workflow job for this annotation

GitHub Actions / build-and-test-frontend

'MissionTypeEnum' is defined but never used
import MissionGeneralInformationUlamFormNew from './mission-general-information-ulam-form-new.tsx'

interface MissionCreateDialogProps {
isOpen: boolean
onClose: () => void
}

const MissionCreateDialog: FC<MissionCreateDialogProps> = ({isOpen, onClose}) => {

const [isDialogOpen, setIsDialogOpen] = useState(isOpen)

useEffect(() => {
setIsDialogOpen(isOpen)
}, [isOpen])


const handleClose = () => {
setIsDialogOpen(false)
onClose()
}


return (
isDialogOpen && (
<Dialog>
<Dialog.Title>Création d'un rapport de mission</Dialog.Title>
<Dialog.Body style={{ backgroundColor: THEME.color.gainsboro }}>
<Stack direction="column" spacing="1.5rem" >
<MissionGeneralInformationUlamFormNew
onClose={handleClose}
/>
</Stack>
</Dialog.Body>
</Dialog>
)
)
}

export default MissionCreateDialog
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React, { FC } from 'react'
import { FlexboxGrid, Stack } from 'rsuite'
import {
Accent,
Button,
FormikDateRangePicker,
FormikMultiCheckbox,
FormikNumberInput,
FormikSelect
} from '@mtes-mct/monitor-ui'
import { useMissionGeneralInformationsForm } from '../../../common/hooks/use-mission-general-informations-form.tsx'
import { FieldProps, Formik } from 'formik'
import { useMissionType } from '../../../common/hooks/use-mission-type.tsx'
import { MissionReportTypeEnum, MissionULAMGeneralInfoInitial } from '../../../common/types/mission-types.ts'

export interface MissionGeneralInformationInitialFormProps {
name: string
fieldFormik: FieldProps<MissionULAMGeneralInfoInitial>
isCreation?: boolean
onClose?: () => void
}

const MissionGeneralInformationInitialForm: FC<MissionGeneralInformationInitialFormProps> = ({ name, fieldFormik, isCreation = false, onClose }) => {

const { initValue, handleSubmit } = useMissionGeneralInformationsForm(name, fieldFormik)
const { missionTypeOptions, reportTypeOptions, reinforcementTypeOptions} = useMissionType()

return (
<>
{initValue && (
<Formik initialValues={initValue} onSubmit={handleSubmit}>
{formik => (
<Stack direction="column" spacing="1.5rem" style={{paddingBottom: '2rem'}}>
<Stack.Item style={{width: '100%'}}>
<FormikSelect
options={reportTypeOptions}
name="missionReportType"
label={"Type de rapport"}
isLight
/>
</Stack.Item >

<Stack.Item style={{width: '100%', textAlign: 'left'}}>
<FormikMultiCheckbox
label={"Type de mission"}
name="missionTypes"
options={missionTypeOptions}
isInline
isLight
/>
</Stack.Item>

{formik.values['missionReportType'] === MissionReportTypeEnum.EXTERNAL_REINFORCEMENT_TIME_REPORT && (
<Stack.Item style={{width: '100%'}}>
<FormikSelect
label="Nature du renfort"
name="reinforcementType"
options={reinforcementTypeOptions}
isLight
/>
</Stack.Item>
)}

<Stack.Item style={{width: '100%', textAlign: 'left'}}>
<FlexboxGrid>
<FlexboxGrid.Item>
<FormikDateRangePicker
label={"Date et heure de début et de fin"}
isLight
name="dates"
/>
</FlexboxGrid.Item>

{!isCreation && (
<FlexboxGrid.Item>
<FormikNumberInput label={"Nb d'heures en mer"} isLight name={"nbHourAtSea"}/>
</FlexboxGrid.Item>
)}
</FlexboxGrid>


</Stack.Item>

{isCreation && (
<Stack.Item >
<Button accent={Accent.PRIMARY} type="submit" onClick={async () => {
handleSubmit(formik.values).then(() => onClose())
}}>
Créer le rapport
</Button>

<Button accent={Accent.SECONDARY} style={{marginLeft: '10px'}} onClick={onClose}>Annuler</Button>
</Stack.Item>
)}
</Stack>
)}
</Formik>
)}
</>

)
}

export default MissionGeneralInformationInitialForm
Loading

0 comments on commit fdd0350

Please sign in to comment.