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

publications page #165

Merged
merged 10 commits into from
Jan 22, 2025
11 changes: 11 additions & 0 deletions website/src/app/[lang]/publications/page-body.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.publications {
ul {
margin: 0;
padding: 0;
padding-inline-start: var(--spacing-8);
}

&__by-year:not(:last-of-type) {
margin-block-end: var(--spacing-6);
}
}
163 changes: 163 additions & 0 deletions website/src/app/[lang]/publications/page-body.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import styles from './page-body.module.scss';

import PageBody from '@components/page-body';
import { documentToReactComponents } from '@contentful/rich-text-react-renderer';
import { getClient } from '@graphql/client';
import {
GetPublicationsPageDocument,
type GetPublicationsPageQuery,
} from '@graphql/queries/publications-page/index.generated';
import type { AvailableLocale } from '@i18n/locales';
import {
geneResearchHeadingId,
patientResearchHeadingId,
} from '@models/headings';
import {
geneResearchContentId,
patientResearchContentId,
} from '@models/paragraphs';
import { createHashLink } from '@shared/utils/hash-links';

interface RegisterPageBodyProps {
lang: AvailableLocale;
}

const { query } = getClient();

interface CleanedPublication {
__typename: 'Publication';
title: string;
dateOfPublication: string;
link: string | null;
asset: {
__typename: 'Asset';
url: string;
} | null;
}

interface PublicationWithLink {
title: string;
year: number;
link: string;
}

interface PublicationsByYear {
year: number;
publications: PublicationWithLink[];
}

const getPublicationWithLink = (
publication: CleanedPublication,
): PublicationWithLink => {
const { title, dateOfPublication, link, asset } = publication;
return {
title: title,
year: new Date(dateOfPublication).getFullYear(),
link: link ?? asset?.url ?? '',
};
};

const getPublicationsByYear = (
acc: PublicationsByYear[],
{ title, year, link }: PublicationWithLink,
) => {
const existingYear = acc.find((publication) => publication.year === year);

if (existingYear) {
existingYear.publications.push({ title, link, year });
} else {
acc.push({ year, publications: [{ title, link, year }] });
}

return acc;
};

const sortByYear = (
{ year: yearA }: PublicationsByYear,
{ year: yearB }: PublicationsByYear,
) => {
return yearB - yearA;
};

interface PublicationsByYearProps {
publications: CleanedPublication[];
}

const PublicationsByYear: React.FC<PublicationsByYearProps> = ({
publications,
}) => {
const publicationsByYear = publications
.map(getPublicationWithLink)
.reduce(getPublicationsByYear, [] as PublicationsByYear[])
.sort(sortByYear);

return publicationsByYear.map(({ year, publications }) => (
<div className={styles['publications__by-year']} key={crypto.randomUUID()}>
<h3>{year}</h3>
<ul>
{publications.map(({ title, link }) => (
<li key={crypto.randomUUID()}>
<a href={link}>{title}</a>
</li>
))}
</ul>
</div>
));
};

const RegisterPageBody: React.FC<RegisterPageBodyProps> = async ({ lang }) => {
const {
data: {
patientResearchHeading,
patientResearchContent,
patientPublications,
geneResearchHeading,
geneResearchContent,
genePublications,
},
error,
} = await query<GetPublicationsPageQuery>({
query: GetPublicationsPageDocument,
variables: {
locale: lang,
patientResearchHeadingId,
patientResearchContentId,
geneResearchHeadingId,
geneResearchContentId,
},
});

if (error) {
return null;
}

const headings = [
patientResearchHeading?.content ?? '',
geneResearchHeading?.content ?? '',
];

return (
<PageBody lang={lang} headings={headings} className={styles.publications}>
<section>
<h2 id={createHashLink(patientResearchHeading?.content ?? '')}>
{patientResearchHeading?.content}
</h2>
{documentToReactComponents(patientResearchContent?.content?.json)}
<PublicationsByYear
publications={patientPublications?.items as CleanedPublication[]}
/>
</section>
<section>
<h2 id={createHashLink(geneResearchHeading?.content ?? '')}>
{geneResearchHeading?.content}
</h2>
{documentToReactComponents(geneResearchContent?.content?.json)}
<PublicationsByYear
publications={genePublications?.items as CleanedPublication[]}
/>
</section>
</PageBody>
);
};

export default RegisterPageBody;
57 changes: 57 additions & 0 deletions website/src/app/[lang]/publications/page-header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import PageHeader from '@components/page-header';
import { getClient } from '@graphql/client';
import {
GetPageHeaderDocument,
type GetPageHeaderQuery,
} from '@graphql/queries/page-header/index.generated';
import type { AvailableLocale } from '@i18n/locales';
import { publicationsPageHeaderId } from '@models/page-header';

interface RegisterPageHeaderProps {
lang: AvailableLocale;
}

const PublicationsPageHeader: React.FC<RegisterPageHeaderProps> = async ({
lang,
}) => {
const { query } = getClient();

// TODO: Change query
const { data, error } = await query<GetPageHeaderQuery>({
query: GetPageHeaderDocument,
variables: {
locale: lang,
id: publicationsPageHeaderId,
},
});

if (
error ||
!data.pageHeader ||
!data.pageHeader.title ||
!data.pageHeader.sectionTitle ||
!data.pageHeader.lastUpdated ||
!data.pageHeader.image
) {
return null;
}

const {
title,
sectionTitle,
lastUpdated,
image: { url },
} = data.pageHeader;

return (
<PageHeader
lang={lang}
pageTitle={title}
sectionTitle={sectionTitle}
lastUpdated={lastUpdated}
imageUrl={url ?? ''}
/>
);
};

export default PublicationsPageHeader;
102 changes: 102 additions & 0 deletions website/src/app/[lang]/publications/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import SupportBanner from '@components/support-banner';
import { getClient } from '@graphql/client';
import {
GetMetadataDocument,
type GetMetadataQuery,
} from '@graphql/queries/metadata/index.generated';
import { registerPatientPageMetadataId } from '@models/metadata';
import type { PagePropsWithLocale } from '@shared/types/page-with-locale-params';
import type { Metadata, NextPage } from 'next';
import type { Graph, MedicalStudy, WebPage, WithContext } from 'schema-dts';
import RegisterPageBody from './page-body';
import PublicationsPageHeader from './page-header';

const { query } = getClient();

const Page: NextPage<PagePropsWithLocale> = async ({ params }) => {
const { lang } = await params;

// TODO: Change query
const {
data: {
// @ts-ignore
htmlHeadMetadata: { title, description },
},
} = await query<GetMetadataQuery>({
query: GetMetadataDocument,
variables: {
locale: lang,
id: registerPatientPageMetadataId,
},
});

const medicalStudy: WithContext<MedicalStudy> = {
'@context': 'https://schema.org',
'@type': 'MedicalStudy',
name: title,
description,
potentialAction: [
{
'@type': 'RegisterAction',
target: `https://nr2f1.org/${lang}/register-patient`,
name: title,
},
],
};

// TODO: Change schema
const webPage: WithContext<WebPage> = {
'@context': 'https://schema.org',
'@type': 'WebPage',
url: `https://nr2f1.org/${lang}/register-a-patient`,
name: title,
inLanguage: lang,
};

const jsonLd: Graph = {
'@context': 'https://schema.org',
'@graph': [medicalStudy, webPage],
};

return (
<>
<script
type="application/ld+json"
// biome-ignore lint/security/noDangerouslySetInnerHtml: this is a safe usage
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<PublicationsPageHeader lang={lang} />
<RegisterPageBody lang={lang} />
<SupportBanner lang={lang} />
</>
);
};

export default Page;

export async function generateMetadata({
params,
}: PagePropsWithLocale): Promise<Metadata> {
const { lang } = await params;

// TODO: Change query
const {
data: {
// @ts-ignore
htmlHeadMetadata: { title, description, keywords },
},
error,
} = await query<GetMetadataQuery>({
query: GetMetadataDocument,
variables: {
locale: lang,
id: registerPatientPageMetadataId,
},
});

return {
title: `NR2F1 Foundation | ${title}`,
description,
keywords,
};
}
Loading
Loading