Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Naorid committed Nov 23, 2023
1 parent 63f1e8d commit 4042dc1
Show file tree
Hide file tree
Showing 33 changed files with 702 additions and 43 deletions.
4 changes: 4 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ API_ETABLISSEMENTS_PUBLICS=https://etablissements-publics.api.gouv.fr/v3/
API_EURES_BASE_URL=https://webgate.acceptance.ec.europa.eu/eures-api/output/api/v1/jv/
API_EURES_IS_MOCK_ACTIVE=0

# API IMMERSION FACILE
API_IMMERSION_FACILE_STAGE_3EME_API_KEY=API_IMMERSION_FACILE_STAGE_3EME_API_KEY
API_IMMERSION_FACILE_STAGE_3EME_URL=https://staging.immersion-facile.beta.gouv.fr/api/v2

# API ONISEP
API_ONISEP_BASE_URL=https://api.opendata.onisep.fr/api/1.0
API_ONISEP_ACCOUNT_EMAIL=[email protected]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ export function FormulaireRechercheEmploisEurope() {
return (
<form
ref={rechercheEmploiEuropeForm}
role="search"
className={styles.rechercheOffreForm}
aria-label="Rechercher une offre d'emploi en Europe"
onSubmit={updateRechercherEmploiEuropeQueryParams}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ describe('RechercherEmploisEurope', () => {
<RechercherEmploisEurope/>
</DependenciesProvider>,
);
const formulaireRechercheEmploisEurope = screen.getByRole('form');
const formulaireRechercheEmploisEurope = screen.getByRole('search');

// THEN
expect(formulaireRechercheEmploisEurope).toBeVisible();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { useRouter } from 'next/router';
import { FormEvent, useRef } from 'react';

import styles
from '~/client/components/features/OffreEmploi/FormulaireRecherche/FormulaireRechercheOffreEmploi.module.scss';
import { ButtonComponent } from '~/client/components/ui/Button/ButtonComponent';
import { Icon } from '~/client/components/ui/Icon/Icon';

export function FormulaireRechercheStages3eme() {
const rechercheStage3emeForm = useRef<HTMLFormElement>(null);

const router = useRouter();

function updateRechercherEmploiEuropeQueryParams(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
// NOTE (DORO 22-11-2023): Query params temporaire pour afficher les résultats de recherche (à remplacer par les vrais query params quand la recherche par localisation sera implémentée)
return router.push({ query: 'location=here' }, undefined, { shallow: true });
}

return (
<form
ref={rechercheStage3emeForm}
role="search"
className={styles.rechercheOffreForm}
aria-label="Rechercher un stage de 3ème"
onSubmit={updateRechercherEmploiEuropeQueryParams}
>
<div className={styles.buttonRechercher}>
<ButtonComponent
label='Rechercher'
icon={<Icon name="magnifying-glass" />}
iconPosition='right'
type='submit'
/>
</div>
</form>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { v4 as uuidv4 } from 'uuid';

import {
ListeRésultatsRechercherSolution,
} from '~/client/components/layouts/RechercherSolution/ListeRésultats/ListeRésultatsRechercherSolution';
import { RésultatRechercherSolution } from '~/client/components/layouts/RechercherSolution/Résultat/RésultatRechercherSolution';
import { ResultatRechercheStage3eme, Stage3eme } from '~/server/stage-3eme/domain/stage3eme';

interface ListeResultatsStage3emeProps {
resultatList: ResultatRechercheStage3eme | undefined;
}

export function ListeResultatsStage3eme({ resultatList }: ListeResultatsStage3emeProps) {
if (!resultatList || resultatList.resultats.length === 0) {
return null;
}

return (
<ListeRésultatsRechercherSolution
aria-label={'Stages de 3ème'}
>
{resultatList.resultats.map((stage3eme) => ResultatStage3eme(stage3eme))}
</ListeRésultatsRechercherSolution>
);
}

function ResultatStage3eme(stage3eme: Stage3eme) {
return (
<li key={uuidv4()}>
<RésultatRechercherSolution
intituléOffre={stage3eme.nomEntreprise}
sousTitreOffre={<>
<p>{stage3eme.domaine}</p>
<p>{stage3eme.adresse.ligne}, {stage3eme.adresse.codePostal} {stage3eme.adresse.ville}</p>
</>}
étiquetteOffreList={[]}
/>
</li>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* @jest-environment jsdom
*/

import { render, screen, within } from '@testing-library/react';

import { mockUseRouter } from '~/client/components/useRouter.mock';
import { mockSmallScreen } from '~/client/components/window.mock';
import { DependenciesProvider } from '~/client/context/dependenciesContainer.context';
import { aStage3emeService } from '~/client/services/stage3eme/stage3eme.service.fixture';
import { createSuccess } from '~/server/errors/either';
import { aResultatRechercheStage3eme } from '~/server/stage-3eme/domain/stage3eme.fixture';

import RechercherStages3eme from './RechercherStages3eme';

describe('La recherche des stages de 3ème', () => {
describe('quand le composant est affiché sans paramètres de recherche dans l’URL', () => {
it('affiche un formulaire de recherche', async () => {
// GIVEN
mockUseRouter({});
const stage3emeServiceMock = aStage3emeService();
// WHEN
render(<DependenciesProvider stage3emeService={stage3emeServiceMock}>
<RechercherStages3eme/>
</DependenciesProvider>);

// THEN
const formulaireRecherche = await screen.findByRole('search', { name: 'Rechercher un stage de 3ème' });
expect(formulaireRecherche).toBeVisible();
const titre = await screen.findByRole('heading', {
level: 1,
name: 'Des milliers d’entreprises prêtes à vous accueillir pour votre stage de 3ème',
});
expect(titre).toBeVisible();
});
});
describe('quand le composant est affiché pour une recherche avec résultats', () => {
it('affiche les résultats de la recherche', async () => {
// GIVEN
mockSmallScreen();
mockUseRouter({ query: { location: 'here' } });
const stage3emeServiceMock = aStage3emeService();
const resultatRecherche = aResultatRechercheStage3eme({
nombreDeResultats: 1,
resultats: [
{
adresse: {
codeDepartement: '75',
codePostal: '75000',
ligne: '1 rue de la Paix',
ville: 'Paris',
},
domaine: 'Informatique',
nomEntreprise: 'Entreprise 1',
},
],
});
jest.spyOn(stage3emeServiceMock, 'rechercherStage3eme').mockResolvedValue(createSuccess(resultatRecherche));

// WHEN
render(<DependenciesProvider stage3emeService={stage3emeServiceMock}>
<RechercherStages3eme/>
</DependenciesProvider>);
const resultatsUl = await screen.findAllByRole('list', { name: 'Stages de 3ème' });
const resultats = await within(resultatsUl[0]).findAllByTestId('RésultatRechercherSolution');

// THEN
expect(resultats).toHaveLength(resultatRecherche.nombreDeResultats);
expect(resultats[0]).toHaveTextContent('Entreprise 1');
expect(resultats[0]).toHaveTextContent('Informatique');
expect(resultats[0]).toHaveTextContent('1 rue de la Paix');
expect(resultats[0]).toHaveTextContent('75000 Paris');
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { useEffect, useMemo, useState } from 'react';

import {
FormulaireRechercheStages3eme,
} from '~/client/components/features/Stages3eme/FormulaireRecherche/FormulaireRechercheStages3eme';
import {
ListeResultatsStage3eme,
} from '~/client/components/features/Stages3eme/FormulaireRecherche/ListeResultatsStage3eme';
import { Head } from '~/client/components/head/Head';
import { RechercherSolutionLayout } from '~/client/components/layouts/RechercherSolution/RechercherSolutionLayout';
import { LightHero, LightHeroPrimaryText, LightHeroSecondaryText } from '~/client/components/ui/Hero/LightHero';
import { useDependency } from '~/client/context/dependenciesContainer.context';
import { Stage3emeService } from '~/client/services/stage3eme/stage3eme.service';
import { formatRechercherSolutionDocumentTitle } from '~/client/utils/formatRechercherSolutionDocumentTitle.util';
import { Erreur } from '~/server/errors/erreur.types';
import { ResultatRechercheStage3eme } from '~/server/stage-3eme/domain/stage3eme';

const PREFIX_TITRE_PAGE = 'Rechercher un stage de 3ème';

export default function RechercherStages3eme() {
const stage3emeService = useDependency<Stage3emeService>('stage3emeService');

const [title, setTitle] = useState<string>(`${PREFIX_TITRE_PAGE} | 1jeune1solution`);
const [isLoading, setIsLoading] = useState(false);
const [erreurRecherche, setErreurRecherche] = useState<Erreur | undefined>(undefined);
const [stage3emeList, setStage3emeList] = useState<ResultatRechercheStage3eme | undefined>(undefined);

useEffect(() => {
setIsLoading(true);
setErreurRecherche(undefined);

stage3emeService.rechercherStage3eme()
.then((response) => {
if (response.instance === 'success') {
setTitle(formatRechercherSolutionDocumentTitle(`${PREFIX_TITRE_PAGE}${response.result.nombreDeResultats === 0 ? ' - Aucun résultat' : ''}`));
setStage3emeList(response.result);
} else {
setTitle(formatRechercherSolutionDocumentTitle(PREFIX_TITRE_PAGE, response.errorType));
setErreurRecherche(response.errorType);
}
setIsLoading(false);
});
}, [stage3emeService]);

const messageResultatsRecherche: string = useMemo(() => {
const messageResultatRechercheSplit: string[] = [`${stage3emeList?.nombreDeResultats}`];
if (stage3emeList && stage3emeList.nombreDeResultats > 1) {
messageResultatRechercheSplit.push('stages de 3ème');
} else {
messageResultatRechercheSplit.push('stage de 3ème');
}
return messageResultatRechercheSplit.join(' ');
}, [stage3emeList]);

return <>
<Head
title={title}
description="Des milliers d’entreprises prêtes à vous accueillir pour votre stage de 3ème"
robots="index,follow"
/>
<main id="contenu">
<RechercherSolutionLayout
bannière={<BaniereStages3eme/>}
erreurRecherche={erreurRecherche}
formulaireRecherche={<FormulaireRechercheStages3eme/>}
isLoading={isLoading}
listeSolutionElement={<ListeResultatsStage3eme resultatList={stage3emeList} />}
messageRésultatRecherche={messageResultatsRecherche}
nombreSolutions={stage3emeList?.nombreDeResultats ?? 0}
/>
</main>
</>;
}

function BaniereStages3eme() {
return (
<LightHero>
<h1>
<LightHeroPrimaryText>Des milliers d’entreprises prêtes à vous accueillir</LightHeroPrimaryText>
<LightHeroSecondaryText>pour votre stage de 3ème</LightHeroSecondaryText>
</h1>
</LightHero>
);
}

This file was deleted.

20 changes: 0 additions & 20 deletions src/client/components/features/Stages3eme/RechercherStages3eme.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import classNames from 'classnames';
import Image from 'next/image';
import React, { PropsWithChildren, useId } from 'react';
import React, { PropsWithChildren, ReactNode, useId } from 'react';

import styles from '~/client/components/layouts/RechercherSolution/Résultat/RésultatRechercherSolution.module.scss';
import { Icon } from '~/client/components/ui/Icon/Icon';
Expand All @@ -13,7 +13,7 @@ type RésultatRechercherSolutionProps = {
lienOffre?: string;
intituléOffre: string;
intituléLienOffre?: string;
sousTitreOffre?: string;
sousTitreOffre?: string | ReactNode;
étiquetteOffreList: string[];
} & LogoProps

Expand Down
6 changes: 6 additions & 0 deletions src/client/dependencies.container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ import { MetierService } from '~/client/services/metiers/metier.service';
import { MissionEngagementService } from '~/client/services/missionEngagement/missionEngagement.service';
import { OffreService } from '~/client/services/offre/offre.service';
import { StageService } from '~/client/services/stage/stage.service';
import { BffStage3emeService } from '~/client/services/stage3eme/bff.stage3eme.service';
import { Stage3emeService } from '~/client/services/stage3eme/stage3eme.service';
import { VideoService } from '~/client/services/video/video.service';
import { YoutubeVideoService } from '~/client/services/video/youtube/youtube.video.service';

Expand All @@ -59,6 +61,7 @@ export type Dependencies = {
marketingService: MarketingService
dateService: DateService
emploiEuropeService: EmploiEuropeService
stage3emeService: Stage3emeService
}

class DependencyInitException extends Error {
Expand Down Expand Up @@ -115,6 +118,8 @@ export default function dependenciesContainer(sessionId: string): Dependencies {
primaryKey: 'slug',
},
);

const stage3emeService = new BffStage3emeService(httpClientService);

return {
alternanceService,
Expand All @@ -132,6 +137,7 @@ export default function dependenciesContainer(sessionId: string): Dependencies {
missionEngagementService,
offreService,
rechercheClientService,
stage3emeService,
stageService,
youtubeService,
établissementAccompagnementService,
Expand Down
22 changes: 22 additions & 0 deletions src/client/services/stage3eme/bff.stage3eme.service.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* @jest-environment jsdom
*/

import { anHttpClientService } from '~/client/services/httpClientService.fixture';
import { BffStage3emeService } from '~/client/services/stage3eme/bff.stage3eme.service';

describe('BffStage3emeService', () => {
describe('rechercherStage3eme', () => {
it('appelle le endpoint', async () => {
// Given
const httpClientService = anHttpClientService();
const bffStage3emeService = new BffStage3emeService(httpClientService);

// When
await bffStage3emeService.rechercherStage3eme();

// Then
expect(httpClientService.get).toHaveBeenCalledWith('stages-3eme');
});
});
});
12 changes: 12 additions & 0 deletions src/client/services/stage3eme/bff.stage3eme.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { HttpClientService } from '~/client/services/httpClient.service';
import { ResultatRechercheStage3eme } from '~/server/stage-3eme/domain/stage3eme';

import { Stage3emeService } from './stage3eme.service';

export class BffStage3emeService implements Stage3emeService {
constructor(private httpClientService: HttpClientService) {}

async rechercherStage3eme() {
return await this.httpClientService.get<ResultatRechercheStage3eme>('stages-3eme');
}
}
Loading

0 comments on commit 4042dc1

Please sign in to comment.