Skip to content

Commit

Permalink
Retours formulaire (#253)
Browse files Browse the repository at this point in the history
* Retours formulaire

* Ajout de valeur par défaut pour les radio button

* Meileure validation de la dénomination de structrure

* Ajout de l'adresse complète pour les RIDET

* Modification du menu

* resolve error test

* gestion siret & ridet

* Ajout d'un état disabled sur le bouton envoyer

---------

Co-authored-by: ornella <[email protected]>
  • Loading branch information
Alezco and Ornella452 committed Nov 28, 2024
1 parent dc2584c commit b738d5d
Show file tree
Hide file tree
Showing 19 changed files with 477 additions and 110 deletions.
26 changes: 19 additions & 7 deletions src/components/Menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,25 +97,37 @@ function Menu() {
aria-expanded={activeMenu === 'cnfs'}
aria-controls="menu-cnfs"
onClick={onClickMenu}
{...(location.pathname.startsWith('/aide-candidat') || location.pathname.startsWith('/aide-structure') ? { 'aria-current': true } : {})}>
Recrutement
{
...(location.pathname.startsWith('/nouveau-formulaire-conseiller') ||
location.pathname.startsWith('/nouveau-formulaire-structure') ||
location.pathname.startsWith('/nouveau-formulaire-coordinateur') ? { 'aria-current': true } : {})
}>
Candidature
</button>
<div className={`fr-collapse fr-menu ${activeMenu === 'cnfs' ? 'fr-collapse--expanded' : ''}`} id="menu-cnfs">
<ul className="fr-menu__list">
<li>
<Link
to="/aide-candidat"
to="/nouveau-formulaire-conseiller"
className="fr-nav__link"
{...(location.pathname.startsWith('/aide-candidat') ? { 'aria-current': 'page' } : {})}>
{...(location.pathname.startsWith('/nouveau-formulaire-conseiller') ? { 'aria-current': 'page' } : {})}>
Devenir conseiller num&eacute;rique
</Link>
</li>
<li>
<Link
to="/aide-structure"
to="/nouveau-formulaire-structure"
className="fr-nav__link"
{...(location.pathname.startsWith('/aide-structure') ? { 'aria-current': 'page' } : {})}>
Recruter un conseiller num&eacute;rique
{...(location.pathname.startsWith('/nouveau-formulaire-structure') ? { 'aria-current': 'page' } : {})}>
Obtenir un poste de conseiller num&eacute;rique
</Link>
</li>
<li>
<Link
to="/nouveau-formulaire-coordinateur"
className="fr-nav__link"
{...(location.pathname.startsWith('/nouveau-formulaire-coordinateur') ? { 'aria-current': 'page' } : {})}>
Obtenir un poste de coordinateur de conseiller num&eacute;rique
</Link>
</li>
</ul>
Expand Down
7 changes: 4 additions & 3 deletions src/components/commun/BoutonRadio.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';

export default function BoutonRadio({ children, id, nomGroupe }) {
export default function BoutonRadio({ children, id, nomGroupe, onChange, checked }) {
return (
<div className="fr-fieldset__element">
<div className="fr-radio-group">
<input type="radio" id={id} name={nomGroupe} value={id} required />
<input type="radio" id={id} name={nomGroupe} value={id} required onChange={onChange} checked={checked} />
<label className="fr-label" htmlFor={id}>
{children}
</label>
Expand All @@ -18,5 +18,6 @@ BoutonRadio.propTypes = {
children: PropTypes.node,
id: PropTypes.string,
nomGroupe: PropTypes.string,
value: PropTypes.string,
onChange: PropTypes.func,
checked: PropTypes.bool,
};
46 changes: 46 additions & 0 deletions src/components/commun/RadioGroup.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import BoutonRadio from './BoutonRadio';

export default function RadioGroup({ nomGroupe, options, tailleColonne = options.length }) {
const [checkedValue, setCheckedValue] = useState(options[0].id);

return (
<div role="radiogroup">
<div className="fr-grid-row">
<div className={`${tailleColonne === options.length ? 'fr-col-12' : 'fr-col-6'}`}>
{options.slice(0, tailleColonne).map(({ id, label }) => (
<BoutonRadio
id={id}
key={id}
nomGroupe={nomGroupe}
onChange={() => setCheckedValue(id)}
checked={checkedValue === id}
>
{label}
</BoutonRadio>
))}
</div>
{options.length > tailleColonne && <div className="fr-col-6">
{options.slice(tailleColonne).map(({ id, label }) => (
<BoutonRadio
id={id}
key={id}
nomGroupe={nomGroupe}
onChange={() => setCheckedValue(id)}
checked={checkedValue === id}
>
{label}
</BoutonRadio>
))}
</div>}
</div>
</div>
);
}

RadioGroup.propTypes = {
nomGroupe: PropTypes.string,
options: PropTypes.array,
tailleColonne: PropTypes.number
};
11 changes: 9 additions & 2 deletions src/views/candidature-conseiller/CandidatureConseiller.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export default function CandidatureConseiller() {
const [dateDisponibilite, setDateDisponibilite] = useState('');
const [isSituationValid, setIsSituationValid] = useState(true);
const [validationError, setValidationError] = useState('');
const [isSending, setIsSending] = useState(false);
const [errors, setErrors] = useState({});
const [widgetId, setWidgetId] = useState(null);
const { buildConseillerData, creerCandidatureConseiller } = useApiAdmin();
Expand All @@ -48,6 +49,7 @@ export default function CandidatureConseiller() {
};

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

const formData = new FormData(event.currentTarget);
Expand All @@ -71,6 +73,7 @@ export default function CandidatureConseiller() {
navigate('/candidature-validee-conseiller');
}
}
setIsSending(false);
};

return (
Expand Down Expand Up @@ -103,8 +106,12 @@ export default function CandidatureConseiller() {
<div className="fr-mt-2w fr-mb-2w">
<Captcha setWidgetId={setWidgetId} widgetId={widgetId} />
</div>
<button className="fr-btn cc-envoyer" type="submit" onClick={() => checkValidity(ref, setErrors)}>
Envoyer votre candidature
<button
className="fr-btn cc-envoyer"
type="submit"
onClick={() => checkValidity(ref, setErrors)}
disabled={isSending}>
{isSending ? 'Envoi en cours...' : 'Envoyer votre candidature'}
</button>
</form>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,8 @@ describe('candidature conseiller', () => {
fireEvent.click(envoyer);

// THEN
const envoiEnCours = screen.getByRole('button', { name: 'Envoi en cours...' });
expect(envoiEnCours).toBeInTheDocument();
const erreurCheckboxes = screen.queryByText('Vous devez cocher au moins une case', { selector: 'p' });
expect(erreurCheckboxes).not.toBeInTheDocument();
vi.useRealTimers();
Expand Down
61 changes: 33 additions & 28 deletions src/views/candidature-conseiller/Disponibilite.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,38 @@
import React from 'react';
import BoutonRadio from '../../components/commun/BoutonRadio';
import Datepicker from '../../components/commun/Datepicker';
import PropTypes from 'prop-types';
import RadioGroup from '../../components/commun/RadioGroup';

const distances = [
{
id: '5',
label: '5 km'
},
{
id: '10',
label: '10 km'
},
{
id: '15',
label: '15 km'
},
{
id: '20',
label: '20 km'
},
{
id: '40',
label: '40 km'
},
{
id: '100',
label: '100 km'
},
{
id: '2000',
label: 'France entière'
},
];

export default function Disponibilite({ setDateDisponibilite, errors }) {
const dateDuJour = new Date().toISOString().slice(0, 10);
Expand All @@ -24,33 +55,7 @@ export default function Disponibilite({ setDateDisponibilite, errors }) {
Pour un emploi et une formation de conseiller numérique, vous êtes prêt(e) à vous déplacer à : <span className="cc-obligatoire">*</span>
</p>
<p className="fr-text--sm fr-hint-text">Distance à partir de votre lieu d’habitation</p>
<div className="fr-grid-row">
<div className="fr-col-6">
<BoutonRadio id="5" nomGroupe="distanceMax">
5 km
</BoutonRadio>
<BoutonRadio id="10" nomGroupe="distanceMax">
10 km
</BoutonRadio>
<BoutonRadio id="15" nomGroupe="distanceMax">
15 km
</BoutonRadio>
<BoutonRadio id="20" nomGroupe="distanceMax">
20 km
</BoutonRadio>
</div>
<div className="fr-col-6">
<BoutonRadio id="40" nomGroupe="distanceMax">
40 km
</BoutonRadio>
<BoutonRadio id="100" nomGroupe="distanceMax">
100 km
</BoutonRadio>
<BoutonRadio id="2000" nomGroupe="distanceMax">
France entière
</BoutonRadio>
</div>
</div>
<RadioGroup nomGroupe="distanceMax" options={distances} tailleColonne={4} />
</fieldset>
);
}
Expand Down
20 changes: 13 additions & 7 deletions src/views/candidature-conseiller/SituationEtExperience.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import React, { useState } from 'react';
import Checkbox from '../../components/commun/Checkbox';
import BoutonRadio from '../../components/commun/BoutonRadio';
import Input from '../../components/commun/Input';
import { situations } from './situations';
import PropTypes from 'prop-types';
import RadioGroup from '../../components/commun/RadioGroup';

const options = [
{
id: 'oui',
label: 'Oui'
},
{
id: 'non',
label: 'Non'
},
];

export default function SituationEtExperience({ isSituationValid }) {
const [isDiplomeSelected, setIsDiplomeSelected] = useState(false);
Expand Down Expand Up @@ -41,12 +52,7 @@ export default function SituationEtExperience({ isSituationValid }) {
Avez-vous une expérience professionnelle de médiation numérique ? <span className="cc-obligatoire">*</span>
</p>
<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>
<BoutonRadio id="oui" nomGroupe="aUneExperienceMedNum">
Oui
</BoutonRadio>
<BoutonRadio id="non" nomGroupe="aUneExperienceMedNum">
Non
</BoutonRadio>
<RadioGroup nomGroupe="aUneExperienceMedNum" options={options} />
</fieldset >
);
}
Expand Down
10 changes: 10 additions & 0 deletions src/views/candidature-conseiller/useApiAdmin.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,21 @@ export const useApiAdmin = () => {
return structureData;
};

const handleRidetSiret = structureData => {
structureData.ridet = null;
if (structureData.siret.length < 14) {
structureData.ridet = structureData.siret;
structureData.siret = null;
}
};

const buildStructureData = async (formData, geoLocation, codeCommune) => {
const structureData = Object.fromEntries(formData);
structureData.location = geoLocation;
convertValueToBoolean(structureData, 'aIdentifieCandidat');
handleContact(structureData);
handleInformationsStructure(structureData);
handleRidetSiret(structureData);
await handleAdresse(structureData, codeCommune);
convertValueToBoolean(structureData, 'confirmationEngagement');
delete structureData['g-recaptcha-response'];
Expand All @@ -128,6 +137,7 @@ export const useApiAdmin = () => {
const coordinateurData = Object.fromEntries(formData);
handleContact(coordinateurData);
handleInformationsStructure(coordinateurData);
handleRidetSiret(coordinateurData);
await handleAdresse(coordinateurData, codeCommune);
convertValueToBoolean(coordinateurData, 'aIdentifieCoordinateur');
convertValueToBoolean(coordinateurData, 'confirmationEngagement');
Expand Down
38 changes: 25 additions & 13 deletions src/views/candidature-coordinateur/BesoinEnCoordinateur.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,29 @@
import React from 'react';
import BoutonRadio from '../../components/commun/BoutonRadio';
import Datepicker from '../../components/commun/Datepicker';
import PropTypes from 'prop-types';
import RadioGroup from '../../components/commun/RadioGroup';

const ouiOuNOn = [
{
id: 'oui',
label: 'Oui'
},
{
id: 'non',
label: 'Non'
},
];

const typeContrat = [
{
id: 'FT',
label: 'Effectuera uniquement des missions de coordination'
},
{
id: 'PT',
label: 'Accompagnera également des publics'
},
];

export default function BesoinEnCoordinateur({ errors }) {
const dateDuJour = new Date().toISOString().slice(0, 10);
Expand All @@ -14,19 +36,9 @@ export default function BesoinEnCoordinateur({ errors }) {
Avez-vous déjà identifié un candidat pour le poste de coordinateur de conseiller numérique ? <span className="cc-obligatoire">*</span>
</p>
<p className="fr-text--sm fr-hint-text">Si oui, merci d’inviter ce candidat à s’inscrire sur la plateforme Conseiller numérique</p>
<BoutonRadio id="oui" nomGroupe="aIdentifieCoordinateur">
Oui
</BoutonRadio>
<BoutonRadio id="non" nomGroupe="aIdentifieCoordinateur">
Non
</BoutonRadio>
<RadioGroup nomGroupe="aIdentifieCoordinateur" options={ouiOuNOn} />
<p className="fr-mt-4w fr-mb-3w cc-bold">Le coordinateur<span className="cc-obligatoire">*</span></p>
<BoutonRadio id="FT" nomGroupe="coordinateurTypeContrat">
Effectuera uniquement des missions de coordination
</BoutonRadio>
<BoutonRadio id="PT" nomGroupe="coordinateurTypeContrat">
Accompagnera également des publics
</BoutonRadio>
<RadioGroup nomGroupe="coordinateurTypeContrat" options={typeContrat} />
<hr />
<p className="fr-mb-3w cc-bold">À partir de quand êtes vous prêt à accueillir votre coordinateur ? <span className="cc-obligatoire">*</span></p>
<Datepicker id="dateDebutMission" min={dateDuJour} error={errors.dateDebutMission}>
Expand Down
Loading

0 comments on commit b738d5d

Please sign in to comment.