Skip to content

Commit

Permalink
Merge branches 'fix/compteurs-places-ouvertes-2023', 'feat/accessibil…
Browse files Browse the repository at this point in the history
…ite/titles', 'fix/reset-password-msg' and 'fix/clean-console' into feat/merged-pr-next
  • Loading branch information
LucasDetre committed Nov 21, 2024
4 parents f88c66f + 4a0043c + c845c16 + 22308f5 commit 8679bc5
Show file tree
Hide file tree
Showing 22 changed files with 429 additions and 100 deletions.
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ services:
timeout: 5s
retries: 12
start_period: 10s
shm_size: 128mb

smtp:
image: axllent/mailpit:latest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ describe("resetPassword usecase", () => {
repeatPassword: correctPassword,
resetPasswordToken: undefined as unknown as string,
})
).rejects.toThrow("missing token");
).rejects.toThrow(
"Lien de réinitialisation incorrect ou expiré. Veuillez reprendre la procédure de réinitialisation depuis le début."
);
});

it("should throw an exception if the token is invalid", async () => {
Expand All @@ -38,7 +40,9 @@ describe("resetPassword usecase", () => {
repeatPassword: correctPassword,
resetPasswordToken: "fakeToken",
})
).rejects.toThrow("wrong token");
).rejects.toThrow(
"Lien de réinitialisation incorrect ou expiré. Veuillez reprendre la procédure de réinitialisation depuis le début."
);
});

it("should throw an exception if passwords are different", async () => {
Expand All @@ -53,7 +57,7 @@ describe("resetPassword usecase", () => {
repeatPassword: "bbb",
resetPasswordToken,
})
).rejects.toThrow("different passwords");
).rejects.toThrow("Mot de passe non identiques.");
});

it("should throw an exception if password is unsafe", async () => {
Expand All @@ -68,7 +72,9 @@ describe("resetPassword usecase", () => {
repeatPassword: "azerty",
resetPasswordToken,
})
).rejects.toThrow("password unsafe");
).rejects.toThrow(
"Le mot de passe doit contenir entre 8 et 15 caractères, une lettre en minuscule, une lettre en majuscule, un chiffre et un caractère spécial (les espaces ne sont pas acceptés)"
);
});

it("should set password", async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,32 @@ export const [resetPassword, resetPasswordFactory] = inject(
repeatPassword: string;
resetPasswordToken: string;
}) => {
if (!resetPasswordToken) throw Boom.unauthorized("missing token");
if (!resetPasswordToken)
throw Boom.unauthorized(
"Lien de réinitialisation incorrect ou expiré. Veuillez reprendre la procédure de réinitialisation depuis le début."
);

let decryptedToken: { email: string };
try {
decryptedToken = jwt.verify(resetPasswordToken, deps.jwtSecret) as {
email: string;
};
} catch {
throw Boom.unauthorized("wrong token");
throw Boom.unauthorized(
"Lien de réinitialisation incorrect ou expiré. Veuillez reprendre la procédure de réinitialisation depuis le début."
);
}

const email = decryptedToken.email.toLowerCase();

if (password !== repeatPassword) {
throw Boom.badRequest("different passwords");
throw Boom.badRequest("Mot de passe non identiques.");
}

if (!password.match(passwordRegex)) {
throw Boom.badRequest("password unsafe");
throw Boom.badRequest(
"Le mot de passe doit contenir entre 8 et 15 caractères, une lettre en minuscule, une lettre en majuscule, un chiffre et un caractère spécial (les espaces ne sont pas acceptés)"
);
}

const hashedPassword = hashPassword(password);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ export const [submitRequeteEnregistreeUsecase, submitRequeteEnregistreeFactory]
user: Pick<RequestUser, "id" | "role" | "codeRegion" | "uais">;
requeteEnregistree: RequeteEnregistree;
}) => {
const requeteEnregistreeVide = Object.keys(requeteEnregistree.filtres).length === 0;

if (requeteEnregistreeVide) {
throw Boom.badRequest("Requête enregistrée vide", {
errors: {
empty_requete:
"La requête enregistrée ne peut pas être vide. Choisissez des filtres pour enregistrer une requête.",
},
});
}

const requeteEnregistreeExistante = await findOneSimilarRequeteEnregistreeQuery({
...requeteEnregistree,
userId: user.id,
Expand Down
1 change: 1 addition & 0 deletions ui/app/(wrapped)/admin/campagnes/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
useDisclosure,
} from "@chakra-ui/react";
import { toDate } from "date-fns";
import Head from "next/head";
import { useMemo, useState } from "react";

import { client } from "@/api.client";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const ActivateAccountForm = ({ activationToken }: { activationToken: stri
pattern: {
value: new RegExp(passwordRegex),
message:
"Le mot de passe doit contenir au moins 8 caractères, une lettre en minuscule, une lettre en majuscule, un chiffre et un caractère spécial (les espaces ne sont pas acceptés)",
"Le mot de passe doit contenir entre 8 et 15 caractères, une lettre en minuscule, une lettre en majuscule, un chiffre et un caractère spécial (les espaces ne sont pas acceptés)",
},
})}
/>
Expand Down
17 changes: 12 additions & 5 deletions ui/app/(wrapped)/auth/reset-password/ResetPasswordForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
Input,
Text,
} from "@chakra-ui/react";
import { isAxiosError } from "axios";
import { useRouter } from "next/navigation";
import { useForm } from "react-hook-form";
import { passwordRegex } from "shared/utils/passwordRegex";
Expand All @@ -31,6 +32,7 @@ export const ResetPasswordForm = ({ resetPasswordToken }: { resetPasswordToken:

const {
mutate: activateAccount,
error,
isError,
isLoading,
} = client.ref("[POST]/auth/reset-password").useMutation({
Expand All @@ -54,7 +56,7 @@ export const ResetPasswordForm = ({ resetPasswordToken }: { resetPasswordToken:
pattern: {
value: new RegExp(passwordRegex),
message:
"Le mot de passe doit contenir au moins 8 caractères, une lettre en minuscule, une lettre en majuscule, un chiffre et un caractère spécial (les espaces ne sont pas acceptés)",
"Le mot de passe doit contenir entre 8 et 15 caractères, une lettre en minuscule, une lettre en majuscule, un chiffre et un caractère spécial (les espaces ne sont pas acceptés)",
},
})}
/>
Expand All @@ -75,10 +77,15 @@ export const ResetPasswordForm = ({ resetPasswordToken }: { resetPasswordToken:
/>
{!!errors.repeatPassword && <FormErrorMessage>{errors.repeatPassword.message}</FormErrorMessage>}
</FormControl>
{isError && (
<Text fontSize="sm" mt="4" textAlign="center" color="red.500">
Erreur lors de la réinitialisation du mot de passe
</Text>
{isError && isAxiosError(error) && (
<>
<Text fontSize="sm" mt="4" textAlign="center" color="red.500">
Erreur lors de la réinitialisation du mot de passe :
</Text>
<Text fontSize="sm" mt="4" textAlign="center" color="red.500">
{error.response?.data?.message}
</Text>
</>
)}
<Flex>
<Button isLoading={isLoading} type="submit" mt="4" ml="auto" variant="primary">
Expand Down
12 changes: 11 additions & 1 deletion ui/app/(wrapped)/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,17 @@ import { useSelectedLayoutSegments } from "next/navigation";
import { LandingFooter } from "./LandingFooter";
import { MinimalFooter } from "./MinimalFooter";

const LANDING_FOOTER_SEGMENTS = ["panorama", "pilotage", "pilotage-reforme", "ressources", "changelog"];
const LANDING_FOOTER_SEGMENTS = [
"panorama",
"pilotage",
"pilotage-reforme",
"ressources",
"changelog",
"declaration-accessibilite",
"politique-de-confidentialite",
"cgu",
"mentions-legales",
];

export const Footer = () => {
const segments = useSelectedLayoutSegments();
Expand Down
1 change: 0 additions & 1 deletion ui/app/(wrapped)/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ export const Header = ({ isMaintenance }: { isMaintenance?: boolean }) => {
boxShadow="0 2px 3px rgba(0,0,18,0.16)"
position="sticky"
top={0}
left={0}
zIndex="docked"
backgroundColor="white"
>
Expand Down
12 changes: 3 additions & 9 deletions ui/app/(wrapped)/components/MinimalFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,14 @@ export const MinimalFooter = () => {
borderTop="1px"
borderTopColor={"grey.900"}
backgroundColor={"white"}
padding={{
base: "16px",
md: "20px 100px 20px 100px",
}}
zIndex={10}
padding={4}
zIndex={"docked"}
>
<HStack
width="100%"
fontSize="11px"
color={"grey.425"}
gap={{
base: "4px 4px",
md: "4px",
}}
gap={2}
divider={<StackDivider borderColor={"grey.900"} />}
wrap="wrap"
justifyContent={"center"}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { useQueryClient } from "@tanstack/react-query";
import type { AxiosError } from "axios";
import { format } from "date-fns";
import NextLink from "next/link";
import type { ReactNode } from "react";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";

Expand Down Expand Up @@ -72,6 +73,7 @@ export const CreateRequeteEnregistreeModal = ({
onClose,
searchParams,
filtersList,
altText,
}: {
page: TypePage;
isOpen: boolean;
Expand All @@ -81,6 +83,7 @@ export const CreateRequeteEnregistreeModal = ({
search?: string;
};
filtersList?: FiltersList;
altText?: ReactNode;
}) => {
const toast = useToast();
const { auth } = useAuth();
Expand Down Expand Up @@ -159,7 +162,7 @@ export const CreateRequeteEnregistreeModal = ({
});
})}
>
<ModalHeader>Enregistrer la requête</ModalHeader>
<ModalHeader>{altText ? altText : "Enregistrer la requête"}</ModalHeader>
<ModalCloseButton />
<ModalBody>
<FormControl mb="4" isInvalid={!!errors.nom}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const DeleteRequeteEnregistreeButton = ({
// Wait until view is updated before invalidating queries
setTimeout(() => {
queryClient.invalidateQueries({
queryKey: ["[GET]/requetesEnregistrees"],
queryKey: ["[GET]/requetes"],
});
setIsDeleting(false);
onClose();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,27 +98,28 @@ export const FiltersSection = ({
return (
<Flex direction={"column"} gap={4} wrap={"wrap"}>
<Wrap spacing={3}>
<Menu matchWidth={true} autoSelect={false}>
<Menu matchWidth={true} autoSelect={false} gutter={3}>
<MenuButton
as={Button}
variant={"selectButton"}
rightIcon={<ChevronDownIcon />}
width={[null, null, "64"]}
width={"15rem"}
size="md"
borderWidth="1px"
borderStyle="solid"
borderColor="grey.900"
bg={"white"}
isDisabled={!requetesEnregistrees || !requetesEnregistrees.length}
>
<Flex direction="row" gap={2}>
<Flex direction="row" gap={2} overflow={"hidden"} whiteSpace="nowrap">
{requeteEnregistreeActuelle.couleur && (
<Tag size={"sm"} bgColor={requeteEnregistreeActuelle.couleur} borderRadius={"100%"} />
)}
<Text my={"auto"}>{requeteEnregistreeActuelle.nom}</Text>
</Flex>
</MenuButton>
<Portal>
<MenuList py={0} borderTopRadius={0} minW={"fit-content"} zIndex={3}>
<MenuList py={0} borderColor="grey.900" borderTopRadius={0} minW={"fit-content"} zIndex={3}>
{requetesEnregistrees && requetesEnregistrees.length > 0 && (
<>
<Text p={2} color="grey.425">
Expand All @@ -141,7 +142,9 @@ export const FiltersSection = ({
gap={2}
>
<Tag size={"sm"} bgColor={requete.couleur} borderRadius={"100%"} />
<Flex direction="row">{requete.nom}</Flex>
<Flex direction="row" whiteSpace={"nowrap"}>
{requete.nom}
</Flex>
{deleteButtonToDisplay === requete.id && (
<DeleteRequeteEnregistreeButton requeteEnregistree={requete} />
)}
Expand Down Expand Up @@ -181,7 +184,7 @@ export const FiltersSection = ({
placeholder="Toutes les régions"
size="md"
variant="newInput"
width={"15rem"}
width="14rem"
onChange={(e) => {
handleFilters("codeRegion", [e.target.value]);
}}
Expand All @@ -198,7 +201,7 @@ export const FiltersSection = ({
disabled={!searchParams.filters?.codeRegion}
size="md"
variant="newInput"
width={"15rem"}
width="14rem"
onChange={(selected) => handleFilters("codeAcademie", selected)}
options={filtersList?.academies}
value={searchParams.filters?.codeAcademie ?? []}
Expand All @@ -209,7 +212,7 @@ export const FiltersSection = ({
disabled={!searchParams.filters?.codeRegion}
size="md"
variant="newInput"
width={"15rem"}
width="14rem"
onChange={(selected) => handleFilters("codeDepartement", selected)}
options={filtersList?.departements}
value={searchParams.filters?.codeDepartement ?? []}
Expand All @@ -220,7 +223,7 @@ export const FiltersSection = ({
disabled={!searchParams.filters?.codeRegion}
size="md"
variant="newInput"
width="15rem"
width="14rem"
onChange={(selected) => handleFilters("commune", selected)}
options={filtersList?.communes}
value={searchParams.filters?.commune ?? []}
Expand Down
7 changes: 4 additions & 3 deletions ui/app/(wrapped)/console/etablissements/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const PAGE_SIZE = 30;

type QueryResult = (typeof client.infer)["[GET]/etablissements"];

const ColonneFiltersSection = chakra(
const ColonneFilterSection = chakra(
({
colonneFilters,
forcedColonnes,
Expand Down Expand Up @@ -85,7 +85,7 @@ const ColonneFiltersSection = chakra(
color="bluefrance.113"
onClick={() => trackEvent("etablissements:affichage-colonnes")}
>
Modifier l'affichage des colonnes
Modifier les colonnes
</Button>
}
/>
Expand Down Expand Up @@ -406,10 +406,11 @@ export default function Etablissements() {
}}
value={searchFormationEtablissement}
onClick={onSearch}
width={{ base: "15rem", ["2xl"]: "25rem" }}
/>
}
ColonneFilter={
<ColonneFiltersSection
<ColonneFilterSection
colonneFilters={colonneFilters}
handleColonneFilters={handleColonneFilters}
forcedColonnes={["libelleEtablissement", "libelleFormation"]}
Expand Down
Loading

0 comments on commit 8679bc5

Please sign in to comment.