Skip to content

Commit

Permalink
feat: ajout des titles dynamiques en fonction des pages
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasDetre committed Nov 21, 2024
1 parent ef03bb4 commit 4a0043c
Show file tree
Hide file tree
Showing 5 changed files with 249 additions and 8 deletions.
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
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
3 changes: 0 additions & 3 deletions ui/app/(wrapped)/declaration-accessibilite/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Box, Container, Heading, Link, ListItem, Text, UnorderedList, VStack } from "@chakra-ui/react";
import NextLink from "next/link";

import { LandingFooter } from "@/app/(wrapped)/components/LandingFooter";

export default function DeclarationAccessibilite() {
return (
<>
Expand Down Expand Up @@ -144,7 +142,6 @@ export default function DeclarationAccessibilite() {
</Text>
</VStack>
</Container>
<LandingFooter />
</>
);
}
22 changes: 18 additions & 4 deletions ui/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,29 @@ import type { Metadata } from "next";
import { headers } from "next/headers";

import { serverClient } from "@/api.client";
import { getMetadata } from "@/utils/handleMetadata";

import RootLayoutClient from "./layoutClient";

export const metadata: Metadata = {
title: "Orion",
robots: "none",
description: "Pilotage de la carte des formations",
type Props = {
params: Promise<Record<string, string>>;
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
};

export async function generateMetadata(
_: Props,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
state: any
): Promise<Metadata> {
const { title, description } = getMetadata(state);

return {
title,
description,
robots: "none",
};
}

const fetchAuth = async () => {
const headersList = Object.fromEntries(headers().entries());
try {
Expand Down
219 changes: 219 additions & 0 deletions ui/utils/handleMetadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
const METADATA_MAP = {
"/": {
title: "Accueil - Orion",
description: "Pilotage de la carte des formations",
},
"/auth/login": {
title: "Connexion - Orion",
description: "Connexion à votre compte Orion",
},
"/panorama/region": {
title: "Panorama régional - Orion",
description: "Panorama des formations enseignées dans votre région",
},
"/panorama/departement": {
title: "Panorama départemental - Orion",
description: "Panorama des formations enseignées dans votre département",
},
"/panorama/etablissement": {
title: "Panorama établissement - Orion",
description: "Panorama des formations enseignées dans votre établissement",
},
"/panorama/lien-metier-formation/formation": {
title: "Lien formation-métier - Orion",
description: "Liens entre une formation et les métiers qu'elle prépare",
},
"/panorama/lien-metier-formation/metier": {
title: "Lien métier-formation - Orion",
description: "Liens entre un métier et les formations qui y mènent",
},
"/console/formations": {
title: "Console des formations - Orion",
description: "Console des formations dispensées",
},
"/console/etablissements": {
title: "Console des formations par établissement - Orion",
description:
"Console des offres de formation dispensées par les établissements",
},
"/intentions/saisie": {
title: "Demandes - Orion",
description: "Demandes de transformation de formation",
},
"/intentions/saisie/new": {
title: "Nouvelle demande - Orion",
description: "Nouvelle demande de transformation de formation",
},
"/intentions/saisie/": {
title: "Modification de la demande - Orion",
description: "Modification de la demande de transformation de formation",
},
"/intentions/synthese": {
title: "Synthèse de la demande - Orion",
description: "Synthèse de la demande de transformation de formation",
},
"/intentions/perdir/saisie": {
title: "Demandes - Orion",
description: "Demandes de transformation de formation (expérimentation)",
},
"/intentions/perdir/saisie/new": {
title: "Nouvelle demande - Orion",
description:
"Nouvelle demande de transformation de formation (expérimentation)",
},
"/intentions/perdir/saisie/": {
title: "Modification de la demande - Orion",
description:
"Modification de la demande de transformation de formation (expérimentation)",
},
"/intentions/perdir/synthese": {
title: "Synthèse de la demande - Orion",
description:
"Synthèse de la demande de transformation de formation (expérimentation)",
},
"/intentions/pilotage": {
title: "Pilotage de la transformation - Orion",
description: "Pilotage de la transformation de la carte de formations",
},
"/intentions/restitution": {
title: "Restitution des demandes de transformation - Orion",
description: "Restitution des demandes de transformation de formation",
},
"/pilotage-reforme": {
title: "Pilotage de la réforme - Orion",
description: "Pilotage de la réforme de la carte de formations",
},
"/admin/campagnes": {
title: "Administration des campagnes - Orion",
description: "Pilotage de la réforme de la carte de formations",
},
"/admin/users": {
title: "Administration des utilisateurs - Orion",
description: "Administration des utilisateurs",
},
"/admin/roles": {
title: "Administration des rôles - Orion",
description: "Administration des rôles",
},
"/mentions-legales": {
title: "Mentions légales - Orion",
description: "Mentions légales",
},
"/declaration-accessibilite": {
title: "Déclaration d'accessibilité - Orion",
description: "Déclaration d'accessibilité",
},
"/changelog": {
title: "Journal des modifications - Orion",
description: "Journal des modifications",
},
"/glossaire": {
title: "Glossaire - Orion",
description: "Glossaire",
},
"/politique-de-confidentialite": {
title: "Politique de confidentialité - Orion",
description: "Politique de confidentialité",
},
"/cgu": {
title: "Conditions générales d'utilisation - Orion",
description: "Conditions générales d'utilisation",
},
"/statistiques": {
title: "Statistiques - Orion",
description: "Statistiques",
},
"/ressources": {
title: "Ressources - Orion",
description: "Ressources et données d'Orion",
},
"/maintenance": {
title: "Maintenance - Orion",
description: "Maintenance",
},
// Autres chemins possibles
};

/**
* Get the pathname from the metadata state
* This dives into async storage of promise state to get the pathname
*
* This is much more performant that using headers() from next as this doesn't opt out from the cache
* @param state
*
* cf https://github.com/vercel/next.js/discussions/50189#discussioncomment-9224262
*/
const getPathnameFromMetadataState = (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
state: any
): string | undefined => {
const res = Object.getOwnPropertySymbols(state || {})
.map((p) => state[p])
.find((state) =>
Object.prototype.hasOwnProperty.call(state, "urlPathname")
);

return res?.urlPathname.replace(/\?.+/, "");
};

/**
*
* @param pathname
* @returns
*/
const extractBasePathname = (pathname: string) => {
if (pathname.includes("/panorama/region")) {
return "/panorama/region";
}
if (pathname.includes("/panorama/departement")) {
return "/panorama/departement";
}
if (pathname.includes("/panorama/etablissement")) {
return "/panorama/etablissement";
}
if (pathname.includes("/panorama/lien-metier-formation/formation")) {
return "/panorama/lien-metier-formation/formation";
}
if (pathname.includes("/panorama/lien-metier-formation/metier")) {
return "/panorama/lien-metier-formation/metier";
}
if (pathname.includes("/intentions/perdir/synthese")) {
return "/intentions/perdir/synthese";
}
if (pathname.includes("/intentions/synthese")) {
return "/intentions/synthese";
}
if (pathname.includes("/intentions/saisie/new")) {
return "/intentions/saisie/new";
}
if (pathname.includes("/intentions/perdir/saisie/new")) {
return "/intentions/perdir/saisie/new";
}
if (pathname.includes("/intentions/saisie/")) {
return "/intentions/saisie/";
}
if (pathname.includes("/intentions/perdir/saisie/")) {
return "/intentions/perdir/saisie/";
}
if (pathname.includes("/intentions/saisie")) {
return "/intentions/saisie";
}
if (pathname.includes("/intentions/perdir/saisie")) {
return "/intentions/perdir/saisie";
}
return pathname;
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function getMetadata(state: any) {
const basePathname = extractBasePathname(
getPathnameFromMetadataState(state) ?? ""
) as keyof typeof METADATA_MAP;
// Renvoie les métadonnées correspondantes au chemin ou un fallback
return (
METADATA_MAP[basePathname] || {
title: "Orion",
description: "Pilotage de la carte des formations",
}
);
}

0 comments on commit 4a0043c

Please sign in to comment.