Skip to content

Commit

Permalink
✨ Accorder / Rejeter / Annuler un dépot de Gfs - Storybook (#1605)
Browse files Browse the repository at this point in the history
* ✨ Accorder / Rejeter Gfs

* 🚚 Accorder -> valider gfs

* ♻️ Reorganize gf page architecture + add annuler action

* 🔥 Delete details GF (will be remarke in other PR)

* 🎨 Utiliser le terme "dépôt"
  • Loading branch information
HubM authored Feb 22, 2024
1 parent 57bd85d commit 6eeb0e4
Show file tree
Hide file tree
Showing 16 changed files with 463 additions and 317 deletions.
4 changes: 2 additions & 2 deletions packages/applications/ssr/.storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { createMuiDsfrThemeProvider } from '@codegouvfr/react-dsfr/mui';

import { StartDsfr } from '../src/app/StartDsfr';

import '../public/dsfr/dsfr.min.css';
import '../public/dsfr/utility/icons/icons.min.css';
import './static/dsfr/dsfr.min.css';
import './static/dsfr/utility/icons/icons.min.css';
import '../src/app/global.css';

export const decorators = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,23 @@ import Input from '@codegouvfr/react-dsfr/Input';

import { formatDateForInput } from '@/utils/formatDateForInput';

type TypeGarantiesFinancières = '6 mois après achèvement' | 'consignation' | 'avec date d’échéance';
export type TypeGarantiesFinancières =
| '6 mois après achèvement'
| 'consignation'
| 'avec date d’échéance';

export type GarantiesFinancières = {
dateConsitution: string;
attestationConstitution: string;
} & (
| {
type: Exclude<TypeGarantiesFinancières, 'avec date d’échéance'>;
}
| {
type: Extract<TypeGarantiesFinancières, 'avec date d’échéance'>;
dateÉchéance: string;
}
);

export type TypeGarantiesFinancièresSelectProps = {
id: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import { FC, useState } from 'react';
import { useRouter } from 'next/navigation';
import Input from '@codegouvfr/react-dsfr/Input';
import Button from '@codegouvfr/react-dsfr/Button';
import { Upload } from '@codegouvfr/react-dsfr/Upload';
import Link from 'next/link';
import Alert from '@codegouvfr/react-dsfr/Alert';

import { Routes } from '@potentiel-libraries/routes';

import { ColumnPageTemplate } from '@/components/templates/ColumnPage.template';
import { ProjetBanner, ProjetBannerProps } from '@/components/molecules/projet/ProjetBanner';
import { Form } from '@/components/atoms/form/Form';
import { formatDateForInput } from '@/utils/formatDateForInput';
import { SubmitButton } from '@/components/atoms/form/SubmitButton';

import { TitrePageGarantiesFinancières } from '../../TitrePageGarantiesFinancieres';
import {
GarantiesFinancières,
TypeGarantiesFinancièresSelect,
TypeGarantiesFinancièresSelectProps,
} from '../../TypeGarantiesFinancièresSelect';

import { ValiderDépôtGarantiesFinancières } from './valider/ValiderDépôtGarantiesFinancières';
import { RejeterDépôtGarantiesFinancières } from './rejeter/RejeterDépôtGarantiesFinancières';
import { modifierGarantiesFinancièresAction } from './modifierDépôtGarantiesFinancières.action';
import { AnnulerDépôtGarantiesFinancières } from './annuler/AnnulerDépôtGarantiesFinancières';

type AvailableActions = Array<'valider' | 'rejeter' | 'annuler'>;

export type ModifierDépôtGarantiesFinancièresProps = {
projet: ProjetBannerProps;
garantiesFinancieres: GarantiesFinancières;
showWarning?: true;
actions: AvailableActions;
};

export const ModifierDépôtGarantiesFinancières: FC<ModifierDépôtGarantiesFinancièresProps> = ({
projet,
garantiesFinancieres,
showWarning,
actions,
}) => {
const router = useRouter();
const [validationErrors, setValidationErrors] = useState<Array<string>>([]);

return (
<ColumnPageTemplate
banner={<ProjetBanner {...projet} />}
heading={<TitrePageGarantiesFinancières />}
leftColumn={{
children: (
<>
<Form
method="POST"
encType="multipart/form-data"
action={modifierGarantiesFinancièresAction}
onSuccess={() =>
router.push(Routes.GarantiesFinancières.détail(projet.identifiantProjet))
}
onValidationError={(validationErrors) => setValidationErrors(validationErrors)}
heading="Modifier des garanties financières"
>
{showWarning && (
<Alert
severity="warning"
className="mb-3"
title=""
description={
<>
Vous pouvez modifier cette soumission de garanties financières jusqu'à sa
validation par la DREAL concernée.
</>
}
/>
)}

<TypeGarantiesFinancièresSelect
id="typeGarantiesFinancieres"
name="typeGarantiesFinancieres"
validationErrors={validationErrors}
typeGarantiesFinancièresActuel={
garantiesFinancieres.type as TypeGarantiesFinancièresSelectProps['typeGarantiesFinancièresActuel']
}
dateÉchéanceActuelle={
garantiesFinancieres.type === 'avec date d’échéance'
? garantiesFinancieres.dateÉchéance
: undefined
}
/>

<Input
label="Date de constitution"
nativeInputProps={{
type: 'date',
name: 'dateConstitution',
max: formatDateForInput(new Date().toISOString()),
defaultValue: garantiesFinancieres.dateConsitution
? formatDateForInput(garantiesFinancieres.dateConsitution)
: undefined,
required: true,
'aria-required': true,
}}
state={validationErrors.includes('dateConstitution') ? 'error' : 'default'}
stateRelatedMessage="Date de constitution des garanties financières obligatoire"
/>

<Upload
label={
<>
Attestation de constitution{' '}
{garantiesFinancieres.attestationConstitution && (
<>
<br />
<small>
Pour que la modification puisse fonctionner, merci de joindre un nouveau
fichier ou{' '}
<Link
href={Routes.Document.télécharger(
garantiesFinancieres.attestationConstitution,
)}
target="_blank"
>
celui préalablement transmis
</Link>
</small>
</>
)}
</>
}
hint="Format accepté : pdf"
nativeInputProps={{
name: 'attestationConstitution',
required: true,
'aria-required': true,
accept: '.pdf',
}}
state={validationErrors.includes('attestationConstitution') ? 'error' : 'default'}
stateRelatedMessage="Attestation de consitution des garantières financières obligatoire"
/>

<div className="flex flex-col md:flex-row gap-4 mt-5">
<Button
priority="secondary"
linkProps={{
href: Routes.Projet.details(projet.identifiantProjet),
}}
iconId="fr-icon-arrow-left-line"
>
Retour au détail du projet
</Button>
<SubmitButton>Soumettre</SubmitButton>
</div>
</Form>
</>
),
}}
rightColumn={{
className: 'flex flex-col w-full md:w-1/4 gap-4',
children: mapToActionComponents({
actions,
identifiantProjet: projet.identifiantProjet,
}),
}}
/>
);
};

type MapToActionsComponentsProps = {
actions: AvailableActions;
identifiantProjet: string;
};
const mapToActionComponents = ({ actions, identifiantProjet }: MapToActionsComponentsProps) => {
return actions.length ? (
<>
{actions.includes('valider') && (
<ValiderDépôtGarantiesFinancières identifiantProjet={identifiantProjet} />
)}
{actions.includes('rejeter') && (
<RejeterDépôtGarantiesFinancières identifiantProjet={identifiantProjet} />
)}
{actions.includes('annuler') && (
<AnnulerDépôtGarantiesFinancières identifiantProjet={identifiantProjet} />
)}
</>
) : null;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import type { Meta, StoryObj } from '@storybook/react';

import { ProjetBannerProps } from '@/components/molecules/projet/ProjetBanner';

import {
ModifierDépôtGarantiesFinancières,
ModifierDépôtGarantiesFinancièresProps,
} from './ModifierDépôtGarantiesFinancières.page';

const meta = {
title: 'Pages/Garanties-financières/Dépôt/Modifier',
component: ModifierDépôtGarantiesFinancières,
parameters: {},
tags: ['autodocs'],
argTypes: {},
} satisfies Meta<ModifierDépôtGarantiesFinancièresProps>;

export default meta;
type Story = StoryObj<typeof meta>;

const projet: ProjetBannerProps = {
identifiantProjet: 'identifiantProjet#1',
appelOffre: 'Appel offre',
période: 'Période',
famille: 'Famille',
nom: 'Nom du projet',
dateDésignation: '2021-10-23',
localité: {
codePostal: 'XXXXX',
commune: 'Commune',
département: 'Département',
région: 'Région',
},
statut: 'classé',
};

export const EnTantQueDreal: Story = {
args: {
projet,
garantiesFinancieres: {
type: 'consignation',
dateConsitution: '2021-10-23',
attestationConstitution: 'path/to/attestationConstitution',
},
actions: ['valider', 'rejeter'],
},
};

export const EnTantQuePorteur: Story = {
args: {
projet,
garantiesFinancieres: {
type: 'avec date d’échéance',
dateÉchéance: '2021-10-23',
dateConsitution: '2021-10-23',
attestationConstitution: 'path/to/attestationConstitution',
},
actions: ['annuler'],
showWarning: true,
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use client';

import { useRouter } from 'next/navigation';

import { Routes } from '@potentiel-libraries/routes';

import { ButtonWithFormInModal } from '@/components/molecules/ButtonWithFormInModal';

import { annulerDépôtGarantiesFinancièresAction } from './annulerDépôtGarantiesFinancières.action';

type AnnulerDépôtGarantiesFinancièresFormProps = {
identifiantProjet: string;
};

export const AnnulerDépôtGarantiesFinancières = ({
identifiantProjet,
}: AnnulerDépôtGarantiesFinancièresFormProps) => {
const router = useRouter();

return (
<ButtonWithFormInModal
name="Annuler"
yesNo
description="Annuler le dépôt"
form={{
id: 'annuler-dépôt-garanties-financieres-form',
action: annulerDépôtGarantiesFinancièresAction,
method: 'post',
encType: 'multipart/form-data',
omitMandatoryFieldsLegend: true,
onSuccess: () => router.push(Routes.GarantiesFinancières.soumettre(identifiantProjet)),
children: (
<>
<p className="mt-3">Êtes-vous sûr de vouloir annuler ce dépôt ?</p>
<input type={'hidden'} value={identifiantProjet} name="identifiantProjet" />
</>
),
}}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use server';

import * as zod from 'zod';

import { FormAction, formAction, FormState } from '@/utils/formAction';
import { withUtilisateur } from '@/utils/withUtilisateur';

const schema = zod.object({
identifiantProjet: zod.string().min(1),
});

const action: FormAction<FormState, typeof schema> = async (_, { identifiantProjet }) => {
return withUtilisateur(async (utilisateur) => {
/**
* @todo appel au use-case à faire
*/
return {
status: 'success',
};
});
};

export const annulerDépôtGarantiesFinancièresAction = formAction(action, schema);
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use client';

import { useRouter } from 'next/navigation';

import { Routes } from '@potentiel-libraries/routes';

import { ButtonWithFormInModal } from '@/components/molecules/ButtonWithFormInModal';

import { rejeterDépôtGarantiesFinancièresAction } from './rejeterDépôtGarantiesFinancières.action';

type RejeterDépôtGarantiesFinancièresFormProps = {
identifiantProjet: string;
};

export const RejeterDépôtGarantiesFinancières = ({
identifiantProjet,
}: RejeterDépôtGarantiesFinancièresFormProps) => {
const router = useRouter();

return (
<ButtonWithFormInModal
name="Rejeter"
yesNo
description="Rejeter le dépôt"
form={{
id: 'rejeter-dépôt-garanties-financieres-form',
action: rejeterDépôtGarantiesFinancièresAction,
method: 'post',
encType: 'multipart/form-data',
omitMandatoryFieldsLegend: true,
onSuccess: () => router.push(Routes.GarantiesFinancières.détail(identifiantProjet)),
children: (
<>
<p className="mt-3">Êtes-vous sûr de vouloir rejeter ce dépôt ?</p>
<input type={'hidden'} value={identifiantProjet} name="identifiantProjet" />
</>
),
}}
/>
);
};
Loading

0 comments on commit 6eeb0e4

Please sign in to comment.