Skip to content

Commit

Permalink
Ajout de la page de formulaire validé
Browse files Browse the repository at this point in the history
  • Loading branch information
Alezco committed Sep 2, 2024
1 parent 4a3f3e5 commit b7a4f11
Show file tree
Hide file tree
Showing 13 changed files with 135 additions and 17 deletions.
2 changes: 2 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ function App() {
const PageCandidatureConseiller = lazy(() => import('./views/candidature-conseiller/PageCandidatureConseiller'));
const PageCandidatureStructure = lazy(() => import('./views/candidature-structure/PageCandidatureStructure'));
const PageCandidatureCoordinateur = lazy(() => import('./views/candidature-coordinateur/PageCandidatureCoordinateur'));
const PageCandidatureValidee = lazy(() => import('./views/candidature-validee/PageCandidatureValidee'));

return (
<div className="App">
Expand All @@ -49,6 +50,7 @@ function App() {
<Route path="/nouveau-formulaire-conseiller" element={<PageCandidatureConseiller />}/>
<Route path="/nouveau-formulaire-structure" element={<PageCandidatureStructure />}/>
<Route path="/nouveau-formulaire-coordinateur" element={<PageCandidatureCoordinateur />}/>
<Route path="/candidature-validee" element={<PageCandidatureValidee />}/>
<Route path="/kit-communication" element={<KitCommunication />}/>
<Route path="/donnees-personnelles" element={<DonneesPersonnelles />}/>
<Route path="/mentions-legales" element={<MentionsLegales />}/>
Expand Down
17 changes: 17 additions & 0 deletions src/components/commun/Alert.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import PropTypes from 'prop-types';

export default function Alert({ children, titre }) {
return (
<div className="fr-alert fr-alert--error">
<h3 className="fr-alert__title">{titre}</h3>
<p>{children}</p>
</div>
);
}

Alert.propTypes = {
children: PropTypes.node,
titre: PropTypes.string,
};

2 changes: 1 addition & 1 deletion src/components/commun/BoutonRadio.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export default function BoutonRadio({ children, id, nomGroupe }) {
return (
<div className="fr-fieldset__element">
<div className="fr-radio-group">
<input type="radio" id={id} name={nomGroupe} required />
<input type="radio" id={id} name={nomGroupe} value={id} required />
<label className="fr-label" htmlFor={id}>
{children}
</label>
Expand Down
2 changes: 1 addition & 1 deletion src/components/commun/Datepicker.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default function Datepicker({ children, id, isRequired = true, onChange,
<label className="fr-label" htmlFor={id}>
{children}
</label>
<input className="fr-input cc-datepicker" id={id} type="date" required={isRequired} onChange={onChange} min={min} />
<input className="fr-input cc-datepicker" id={id} type="date" required={isRequired} onChange={onChange} min={min} name={id} />
</div>
);
}
Expand Down
12 changes: 11 additions & 1 deletion src/components/commun/Input.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,17 @@ export default function Input({ children, id, isRequired = true, type = 'text',
<div className="fr-fieldset__element">
<div className="fr-input-group">
<label className="fr-label" htmlFor={id}>{children}</label>
<input className="fr-input" type={type} id={id} required={isRequired} pattern={pattern} onChange={onChange} list={list} min={min} />
<input
className="fr-input"
type={type}
id={id}
required={isRequired}
pattern={pattern}
onChange={onChange}
list={list}
min={min}
name={id}
/>
</div>
</div>
);
Expand Down
34 changes: 28 additions & 6 deletions src/views/candidature-conseiller/CandidatureConseiller.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Disponibilite from './Disponibilite';
import Motivation from './Motivation';
import EnResume from './EnResume';
import { useScrollToSection } from '../../hooks/useScrollToSection';
import { useNavigate } from 'react-router-dom';

import '@gouvfr/dsfr/dist/component/form/form.min.css';
import '@gouvfr/dsfr/dist/component/input/input.min.css';
Expand All @@ -14,27 +15,45 @@ import '@gouvfr/dsfr/dist/component/radio/radio.min.css';
import '@gouvfr/dsfr/dist/component/badge/badge.min.css';
import '@gouvfr/dsfr/dist/component/notice/notice.min.css';
import '@gouvfr/dsfr/dist/component/sidemenu/sidemenu.min.css';
import '@gouvfr/dsfr/dist/component/alert/alert.min.css';
import './CandidatureConseiller.css';
import { useApiAdmin } from './useApiAdmin';
import Alert from '../../components/commun/Alert';

export default function CandidatureConseiller() {
const [dateDisponibilite, setDateDisponibilite] = useState('');
const [isSituationValid, setIsSituationValid] = useState(true);
const { creerCandidatureConseiller } = useApiAdmin();
const [validationError, setValidationError] = useState();
const { buildConseillerData, creerCandidatureConseiller } = useApiAdmin();
const navigate = useNavigate();
useScrollToSection();

const validerLaCandidature = event => {
const estSituationRemplie = formData => {
const demandeurEmploi = formData.get('demandeurEmploi');
const enEmploi = formData.get('enEmploi');
const enFormation = formData.get('enFormation');
const diplome = formData.get('diplome');

return demandeurEmploi || enEmploi || enFormation || diplome;
};

const validerLaCandidature = async event => {
event.preventDefault();

const formData = new FormData(event.currentTarget);
const situations = formData.get('situations');

if (situations === null) {
if (!estSituationRemplie(formData)) {
setIsSituationValid(false);
document.getElementById('situation-et-experience').scrollIntoView();
} else {
event.currentTarget.submit();
creerCandidatureConseiller();
const conseillerData = buildConseillerData(formData);
const resultatCreation = await creerCandidatureConseiller(conseillerData);
if (resultatCreation.status >= 400) {
const error = await resultatCreation.json();
setValidationError(error.message);
} else {
navigate('/candidature-validee');

Check failure on line 55 in src/views/candidature-conseiller/CandidatureConseiller.jsx

View workflow job for this annotation

GitHub Actions / tests

Unhandled error

TypeError: navigate is not a function ❯ validerLaCandidature src/views/candidature-conseiller/CandidatureConseiller.jsx:55:9 ❯ processTicksAndRejections node:internal/process/task_queues:95:5 This error originated in "src/views/candidature-conseiller/CandidatureConseiller.test.jsx" test file. It doesn't mean the error was thrown inside the file itself, but while it was running.
}
}
};

Expand All @@ -47,6 +66,9 @@ export default function CandidatureConseiller() {
<div className="fr-col-12 fr-col-md-8 fr-py-12v">
<h1 className="cc-titre fr-mb-5w">Je veux devenir conseiller numérique</h1>
<p className="fr-text--sm fr-hint-text">Les champs avec <span className="cc-obligatoire">*</span> sont obligatoires.</p>
{validationError && <Alert titre="Erreur de validation">
{validationError}
</Alert>}
<form
aria-label="Candidature conseiller"
onSubmit={validerLaCandidature}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { textMatcher, dateDujour } from '../../../test/test-utils';

vi.mock('react-router-dom', () => ({
useLocation: () => ({ hash: '' }),
useNavigate: () => { }
}));

describe('candidature conseiller', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/views/candidature-conseiller/Disponibilite.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default function Disponibilite({ setDateDisponibilite }) {
<p className="fr-text--sm fr-hint-text">
Accompagnement de personnes vers l’autonomie dans leurs usages de technologies, services et médias numériques.
</p>
<Datepicker id="choisir-date" onChange={event => setDateDisponibilite(event.target.value)} min={dateDuJour}>
<Datepicker id="dateDisponibilite" onChange={event => setDateDisponibilite(event.target.value)} min={dateDuJour}>
Choisir une date
</Datepicker>
<hr />
Expand Down
2 changes: 1 addition & 1 deletion src/views/candidature-conseiller/SituationEtExperience.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default function SituationEtExperience({ isSituationValid }) {
Êtes-vous actuellement dans l’une des situations suivantes ? <span className="cc-obligatoire">*</span>
</p>
{situations.map(({ id, libelle }) =>
<Checkbox id={id} key={id} name="situations" onCheck={handleCheck} required={false}>
<Checkbox id={id} key={id} name={id} onCheck={handleCheck} required={false}>
{libelle}
</Checkbox>
)}
Expand Down
28 changes: 22 additions & 6 deletions src/views/candidature-conseiller/useApiAdmin.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,34 @@
export const useApiAdmin = () => {
const creerCandidatureConseiller = async () => {
const creerCandidatureConseiller = async conseillerData => {
const baseUrl = import.meta.env.VITE_APP_API_URL;
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
headers: { 'Content-Type': 'application/json' },
body: conseillerData
};

try {
const result = fetch(`${baseUrl}/candidature-conseiller`, requestOptions);
console.log('===================', result);
return await fetch(`${baseUrl}/candidature-conseiller`, requestOptions);
} catch (error) {
console.error(error);
return error;
}
};

return { creerCandidatureConseiller };
const convertStringToBoolean = (conseillerData, key) => {
if (conseillerData[key] === 'on') {
conseillerData[key] = true;
}
};

const buildConseillerData = formData => {
const conseillerData = JSON.stringify(Object.fromEntries(formData));
convertStringToBoolean(conseillerData, 'demandeurEmploi');
convertStringToBoolean(conseillerData, 'enEmploi');
convertStringToBoolean(conseillerData, 'enFormation');
convertStringToBoolean(conseillerData, 'diplome');

return conseillerData;
};

return { buildConseillerData, creerCandidatureConseiller };
};
10 changes: 10 additions & 0 deletions src/views/candidature-validee/CandidatureValidee.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.cv-contenu {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}

.cv-titre {
color: var(--blue-france-sun-113-625);
}
28 changes: 28 additions & 0 deletions src/views/candidature-validee/CandidatureValidee.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';

import './CandidatureValidee.css';


export default function CandidatureValidee() {
const navigate = useNavigate();

const goToHome = () => {
navigate('/');
};

return (
<div className="fr-container fr-p-10w cv-contenu">
<h1 className="fr-mb-5w">👏</h1>
<h2 className="fr-mb-5w cv-titre">Merci, votre demande a été envoyée.</h2>
<p>
Pour confirmer votre inscription et recevoir des propositions de candidats, veuillez{' '}
consulter l’email qui vient de vous être envoyé. Si vous ne recevez pas cet email dans les prochaines minutes,{' '}
pensez à vérifier votre dossier de spams.
</p>
<button className="fr-btn fr-btn--secondary" onClick={goToHome}>
Retour à la page d’accueil
</button>
</div>
);
}
12 changes: 12 additions & 0 deletions src/views/candidature-validee/PageCandidatureValidee.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';
import Header from '../../components/Header';
import CandidatureValidee from './CandidatureValidee';

export default function PageCandidatureValidee() {
return (
<>
<Header />
<CandidatureValidee />
</>
);
}

0 comments on commit b7a4f11

Please sign in to comment.