Skip to content

Commit

Permalink
feat: atc page, main menu and breadcrumbs links
Browse files Browse the repository at this point in the history
Signed-off-by: Maud Royer <[email protected]>
  • Loading branch information
jillro committed Nov 4, 2024
1 parent 56fa51a commit 61fa27d
Show file tree
Hide file tree
Showing 6 changed files with 322 additions and 86 deletions.
110 changes: 110 additions & 0 deletions src/app/(container)/atc/[code]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import Badge from "@codegouvfr/react-dsfr/Badge";
import { fr } from "@codegouvfr/react-dsfr";
import { ATC, getAtc1, getAtc2, getSubstancesByAtc } from "@/data/atc";
import Card from "@codegouvfr/react-dsfr/Card";
import Breadcrumb from "@codegouvfr/react-dsfr/Breadcrumb";
import { notFound } from "next/navigation";
import React from "react";
import Link from "next/link";
import { SubstanceNom } from "@/db/pdbmMySQL/types";

export const dynamic = "error";
export const dynamicParams = true;

const SubstanceItem = ({ item }: { item: SubstanceNom }) => (
<li key={item.NomId} className={fr.cx("fr-mb-1w")}>
<Link className={fr.cx("fr-link")} href={`/substance/${item.NomId}`}>
{item.NomLib}
</Link>
</li>
);

const SubClassItem = ({ item }: { item: ATC }) => (
<li key={item.code} className={fr.cx("fr-mb-1w")}>
<Link className={fr.cx("fr-link")} href={`/atc/${item.code}`}>
{item.label}
</Link>
</li>
);

export default async function Page({
params: { code },
}: {
params: { code: string };
}) {
const atc1 = await getAtc1(code);
const atc2 = code.length === 3 && (await getAtc2(code));
const currentAtc = atc2 || atc1;

const items = atc2
? await getSubstancesByAtc(atc2)
: (
await Promise.all(
atc1.children.map(
async (atc2): Promise<[ATC, SubstanceNom[] | undefined]> => [
atc2,
await getSubstancesByAtc(atc2),
],
),
)
)
.filter(([_, substances]) => !!substances)
.map(([atc2]) => atc2);

if (!items) notFound();

const ItemComponent = (atc2 ? SubstanceItem : SubClassItem) as ({
item,
}: {
item: SubstanceNom | ATC;
}) => React.JSX.Element;

return (
<>
<Breadcrumb
segments={[
{
label: "Accueil",
linkProps: { href: "/" },
},
...(atc2
? [
{
label: atc1.label,
linkProps: { href: `/atc/${atc1.code}` },
},
]
: []),
]}
currentPageLabel={currentAtc.label}
/>
<div className={fr.cx("fr-grid-row")}>
<div className={fr.cx("fr-col-md-8")}>
<Badge className={fr.cx("fr-badge--purple-glycine")}>
Classe de médicament
</Badge>

<h1 className={fr.cx("fr-h2", "fr-mt-2w")}>{currentAtc.label}</h1>

<div className={fr.cx("fr-grid-row")}>
<Card
title="Définition"
titleAs={"h6"}
desc={currentAtc.description}
/>
</div>
<h2 className={fr.cx("fr-h6", "fr-mt-4w")}>
{items.length}{" "}
{atc2 ? "substances actives" : "sous-classes de médicament"}
</h2>

<ul className={fr.cx("fr-raw-list")}>
{items.map((item: SubstanceNom | ATC, index) => (
<ItemComponent item={item} key={index} />
))}
</ul>
</div>
</div>
</>
);
}
107 changes: 53 additions & 54 deletions src/app/(container)/medicament/[CIS]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,19 @@ import JSZIP from "jszip";
import * as windows1252 from "windows-1252";
import HTMLParser, { HTMLElement } from "node-html-parser";
import { Nullable, sql } from "kysely";
import { parse as csvParse } from "csv-parse/sync";

import { pdbmMySQL } from "@/db/pdbmMySQL";
import liste_CIS_MVP from "@/liste_CIS_MVP.json";
import DsfrLeafletSection from "@/app/(container)/medicament/[CIS]/DsfrLeafletSection";
import { isHtmlElement } from "@/app/(container)/medicament/[CIS]/leafletUtils";
import {
dateShortFormat,
displayComposants,
displayCompleteComposants,
displaySimpleComposants,
formatSpecName,
getSpecialiteGroupName,
} from "@/displayUtils";
import Breadcrumb from "@codegouvfr/react-dsfr/Breadcrumb";
import { readFileSync } from "node:fs";
import {
Presentation,
PresentationComm,
Expand All @@ -37,7 +36,7 @@ import {
Specialite,
SubstanceNom,
} from "@/db/pdbmMySQL/types";
import { getAtcLabels } from "@/data/atc";
import { atcData, getAtc1, getAtc2 } from "@/data/atc";
import { notFound } from "next/navigation";

export const dynamic = "error";
Expand Down Expand Up @@ -156,14 +155,14 @@ const getSpecialite = cache(async (CIS: string) => {
};
});

const atcData = csvParse(
readFileSync(
path.join(process.cwd(), "src", "data", "CIS-ATC_2024-04-07.csv"),
),
) as string[][];
function getAtc(CIS: string) {
function getAtcCode(CIS: string) {
const atc = atcData.find((row) => row[0] === CIS);
return atc ? atc[1] : null;

if (!atc) {
throw new Error(`Could not find ATC code for CIS ${CIS}`);
}

return atc[1];
}

/**
Expand Down Expand Up @@ -313,57 +312,57 @@ export default async function Page({
const { specialite, composants, presentations, delivrance } =
await getSpecialite(CIS);
const leaflet = await getLeaflet(CIS);
const atc = getAtc(CIS);
const atcLabels = atc ? await getAtcLabels(atc) : null;
const [, subClass, substance] = atcLabels ? atcLabels : [null, null, null];
const atcCode = getAtcCode(CIS);
const atc1 = await getAtc1(atcCode);
const atc2 = await getAtc2(atcCode);

return (
<>
{atcLabels && (
<Breadcrumb
segments={[
{ label: "Accueil", linkProps: { href: "/" } },
...[
...atcLabels,
formatSpecName(getSpecialiteGroupName(specialite)),
].map((label) => ({
label,
linkProps: { href: `/rechercher?s=${label}` },
})),
]}
currentPageLabel={formatSpecName(specialite.SpecDenom01).replace(
formatSpecName(getSpecialiteGroupName(specialite)),
"",
)}
/>
)}
<Breadcrumb
segments={[
{ label: "Accueil", linkProps: { href: "/" } },
{ label: atc1.label, linkProps: { href: `/atc/${atc1.code}` } },
{ label: atc2.label, linkProps: { href: `/atc/${atc2.code}` } },
{
label: formatSpecName(getSpecialiteGroupName(specialite)),
linkProps: {
href: `/rechercher?s=${formatSpecName(getSpecialiteGroupName(specialite))}`,
},
},
]}
currentPageLabel={formatSpecName(specialite.SpecDenom01).replace(
formatSpecName(getSpecialiteGroupName(specialite)),
"",
)}
/>
<h1 className={fr.cx("fr-h2")}>
{formatSpecName(specialite.SpecDenom01)}
</h1>
<section className={"fr-mb-4w"}>
<div className={"fr-mb-1w"}>
<ul className={fr.cx("fr-tags-group", "fr-mb-n1v")}>
{subClass && (
<Tag
small
linkProps={{
href: `/rechercher?s=${subClass}`,
className: cx("fr-tag--custom-alt-class"),
}}
>
{subClass}
</Tag>
)}
{substance && (
<Tag
small
linkProps={{
href: `/rechercher?s=${substance}`,
className: cx("fr-tag--custom-alt-substance"),
}}
>
{substance}
</Tag>
<Tag
small
linkProps={{
href: `/atc/${atc2.code}`,
className: cx("fr-tag--custom-alt-class"),
}}
>
{atc2.label}
</Tag>
{displaySimpleComposants(composants).map(
(substance: SubstanceNom) => (
<Tag
key={substance.NomId}
small
linkProps={{
href: `/substance/${substance.NomId}`,
className: cx("fr-tag--custom-alt-substance"),
}}
>
{substance.NomLib}
</Tag>
),
)}
{specialite.SpecGeneId ? (
<Tag
Expand Down Expand Up @@ -395,7 +394,7 @@ export default async function Page({
" ",
)}
/>
<b>Substance active</b> {displayComposants(composants)}
<b>Substance active</b> {displayCompleteComposants(composants)}
</div>
<ul className={fr.cx("fr-raw-list")}>
{presentations.map((p) => (
Expand Down
3 changes: 0 additions & 3 deletions src/app/(container)/parcourir/medicaments/[letter]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ import liste_CIS_MVP from "@/liste_CIS_MVP.json";
import { MedGroupSpecListList } from "@/components/MedGroupSpecList";
import { groupSpecialites } from "@/displayUtils";

export const dynamic = "error";
export const dynamicParams = true;

const getLetters = unstable_cache(async function () {
return (
await pdbmMySQL
Expand Down
11 changes: 10 additions & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,19 @@ import "@/customIcons/customIcons.css";
import "@/components/dsfr-custom-alt.css";
import MuiDsfrThemeProvider from "@codegouvfr/react-dsfr/mui";
import { headerFooterDisplayItem } from "@codegouvfr/react-dsfr/Display";
import { getAtc } from "@/data/atc";
import { StartHotjar } from "@/app/StartHotjar";

export const metadata: Metadata = {
title: "Info Médicament",
};

export default function RootLayout({
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const atcs = await getAtc();
const lang = "fr";
return (
<html {...getHtmlAttributes({ defaultColorScheme, lang })}>
Expand Down Expand Up @@ -75,6 +77,13 @@ export default function RootLayout({
serviceTagline="La référence officielle sur les données des médicaments"
quickAccessItems={[headerFooterDisplayItem]}
navigation={[
{
text: "Parcourir",
menuLinks: atcs.map((atc) => ({
linkProps: { href: `/atc/${atc.code}` },
text: atc.label,
})),
},
{
text: "Par ordre alphabétique",
menuLinks: [
Expand Down
Loading

0 comments on commit 61fa27d

Please sign in to comment.