Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(stage3eme): recherche #2350

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
[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>
juliebrunetto83 marked this conversation as resolved.
Show resolved Hide resolved
</>}
é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' });
Naorid marked this conversation as resolved.
Show resolved Hide resolved
const resultats = await within(resultatsUl[0]).findAllByTestId('RésultatRechercherSolution');
juliebrunetto83 marked this conversation as resolved.
Show resolved Hide resolved

// 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');
});
});
Naorid marked this conversation as resolved.
Show resolved Hide resolved
});
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') {
Naorid marked this conversation as resolved.
Show resolved Hide resolved
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