add titles, description and images for seo

This commit is contained in:
2024-08-10 18:29:26 +02:00
parent ed10692318
commit 385f17eea1
19 changed files with 598 additions and 241 deletions

View File

@ -1,10 +1,12 @@
import { graphql } from "@/gql";
import { GenericFragment } from "@/gql/graphql";
import { getClient } from "@/app/client";
import { Metadata, ResolvingMetadata } from "next";
import { notFound } from "next/navigation";
import { PageHeader } from "@/components/general/PageHeader";
import { PageContent } from "@/components/general/PageContent";
import { BgPig } from "@/components/general/BgPig";
import { getSeoMetadata } from "@/lib/seo";
export const dynamicParams = false;
@ -13,6 +15,8 @@ const GenericFragmentDefinition = graphql(`
__typename
id
urlPath
seoTitle
searchDescription
title
lead
pig
@ -22,6 +26,21 @@ const GenericFragmentDefinition = graphql(`
}
`);
const genericPageByUrlPathQuery = graphql(`
query genericPageByUrl($urlPath: String!) {
page: page(contentType: "generic.GenericPage", urlPath: $urlPath) {
... on GenericPage {
...Generic
}
}
}
`);
function getWagtailUrlPath(url: string[]): string {
// for the page /foo/bar we need to look for `/home/foo/bar/`
return `/home/${url.join("/")}/`;
}
export async function generateStaticParams() {
const allGenericSlugsQuery = graphql(`
query allGenericSlugs {
@ -50,21 +69,29 @@ export async function generateStaticParams() {
});
}
export async function generateMetadata(
{ params }: { params: { url: string[] } },
parent: ResolvingMetadata
): Promise<Metadata | null> {
const { url } = params;
const urlPath = getWagtailUrlPath(url);
const { data, error } = await getClient().query(genericPageByUrlPathQuery, {
urlPath: urlPath,
});
const page = (data?.page ?? []) as GenericFragment;
if (!page) {
return null;
}
const metadata = await getSeoMetadata(page, parent);
return metadata;
}
export default async function Page({ params }: { params: { url: string[] } }) {
const { url } = params;
// for the page /foo/bar we need to look for `/home/foo/bar/`
const urlPath = `/home/${url.join("/")}/`;
const genericPageByUrlPathQuery = graphql(`
query genericPageByUrl($urlPath: String!) {
page: page(contentType: "generic.GenericPage", urlPath: $urlPath) {
... on GenericPage {
...Generic
}
}
}
`);
const urlPath = getWagtailUrlPath(url);
const { data, error } = await getClient().query(genericPageByUrlPathQuery, {
urlPath: urlPath,
});

View File

@ -1,11 +1,23 @@
import { Metadata, ResolvingMetadata } from "next";
import { notFound } from "next/navigation";
import { getClient } from "@/app/client";
import { Breadcrumb } from "@/components/general/Breadcrumb";
import { ImageFigure } from "@/components/general/Image";
import { PageContent } from "@/components/general/PageContent";
import { graphql } from "@/gql";
import { NewsFragment } from "@/gql/graphql";
import { formatDate, formatExtendedDateTime } from "@/lib/date";
import { notFound } from "next/navigation";
import { formatDate } from "@/lib/date";
import { getSeoMetadata } from "@/lib/seo";
const newsBySlugQuery = graphql(`
query newsBySlug($slug: String!) {
news: page(contentType: "news.NewsPage", slug: $slug) {
... on NewsPage {
...News
}
}
}
`);
export async function generateStaticParams() {
const allNewsSlugsQuery = graphql(`
@ -27,17 +39,24 @@ export async function generateStaticParams() {
}));
}
export default async function Page({ params }: { params: { slug: string } }) {
const newsBySlugQuery = graphql(`
query newsBySlug($slug: String!) {
news: page(contentType: "news.NewsPage", slug: $slug) {
... on NewsPage {
...News
}
}
}
`);
export async function generateMetadata(
{ params }: { params: { slug: string } },
parent: ResolvingMetadata
): Promise<Metadata | null> {
const { data, error } = await getClient().query(newsBySlugQuery, {
slug: params.slug,
});
const news = (data?.news ?? []) as NewsFragment[];
if (!news) {
return null;
}
const metadata = await getSeoMetadata(news, parent);
return metadata;
}
export default async function Page({ params }: { params: { slug: string } }) {
const { data, error } = await getClient().query(newsBySlugQuery, {
slug: params.slug,
});
@ -52,7 +71,11 @@ export default async function Page({ params }: { params: { slug: string } }) {
return (
<main className="site-main" id="main">
<section className="news-header">
<Breadcrumb link="/aktuelt" text="Nyhet" date={formatDate(news.firstPublishedAt, "d. MMMM yyyy")} />
<Breadcrumb
link="/aktuelt"
text="Nyhet"
date={formatDate(news.firstPublishedAt, "d. MMMM yyyy")}
/>
<h1 className="news-title">{news.title}</h1>
{news.lead && (
<div

View File

@ -1,9 +1,24 @@
import { graphql } from "@/gql";
import { getClient } from "@/app/client";
import { NewsList } from "@/components/news/NewsList";
import Link from "next/link";
import { Metadata, ResolvingMetadata } from "next";
import { PageHeader } from "@/components/general/PageHeader";
import { newsQuery, NewsFragment, NewsIndexFragment } from "@/lib/news";
import { getSeoMetadata } from "@/lib/seo";
export async function generateMetadata(
{ params }: { params: { url: string[] } },
parent: ResolvingMetadata
): Promise<Metadata | null> {
const { data, error } = await getClient().query(newsQuery, {});
const index = (data?.index ?? []) as NewsIndexFragment;
if (!index) {
return null;
}
const metadata = await getSeoMetadata(index, parent);
return metadata;
}
export default async function Page() {
const { data, error } = await getClient().query(newsQuery, {});

View File

@ -1,3 +1,5 @@
import { Metadata, ResolvingMetadata } from "next";
import { notFound } from "next/navigation";
import { getClient } from "@/app/client";
import { EventDetails } from "@/components/events/EventDetails";
import { EventHeader } from "@/components/events/EventHeader";
@ -6,7 +8,17 @@ import { PageContent } from "@/components/general/PageContent";
import { graphql } from "@/gql";
import { EventFragment } from "@/gql/graphql";
import { getEventPig } from "@/lib/event";
import { notFound } from "next/navigation";
import { getSeoMetadata } from "@/lib/seo";
const eventBySlugQuery = graphql(`
query eventBySlug($slug: String!) {
event: page(contentType: "events.EventPage", slug: $slug) {
... on EventPage {
...Event
}
}
}
`);
export async function generateStaticParams() {
const allEventSlugsQuery = graphql(`
@ -28,17 +40,24 @@ export async function generateStaticParams() {
}));
}
export default async function Page({ params }: { params: { slug: string } }) {
const eventBySlugQuery = graphql(`
query eventBySlug($slug: String!) {
event: page(contentType: "events.EventPage", slug: $slug) {
... on EventPage {
...Event
}
}
}
`);
export async function generateMetadata(
{ params }: { params: { slug: string } },
parent: ResolvingMetadata
): Promise<Metadata | null> {
const { data, error } = await getClient().query(eventBySlugQuery, {
slug: params.slug,
});
const event = (data?.event ?? []) as EventFragment[];
if (!event) {
return null;
}
const metadata = await getSeoMetadata(event, parent);
return metadata;
}
export default async function Page({ params }: { params: { slug: string } }) {
const { data, error } = await getClient().query(eventBySlugQuery, {
slug: params.slug,
});

View File

@ -1,17 +1,36 @@
import { Suspense } from "react";
import { Metadata, ResolvingMetadata } from "next";
import { getClient } from "@/app/client";
import { EventContainer } from "@/components/events/EventContainer";
import {
futureEventsQuery,
eventsOverviewQuery,
eventIndexMetadataQuery,
EventFragment,
EventCategory,
EventOrganizer,
} from "@/lib/event";
import { PageHeader } from "@/components/general/PageHeader";
import { Suspense } from "react";
import { VenueFragment } from "@/gql/graphql";
import { EventIndexFragment, VenueFragment } from "@/gql/graphql";
import { getSeoMetadata } from "@/lib/seo";
export async function generateMetadata(
{ params }: { params: { url: string[] } },
parent: ResolvingMetadata
): Promise<Metadata | null> {
const { data, error } = await getClient().query(eventIndexMetadataQuery, {});
const index = (data?.index ?? []) as EventIndexFragment;
if (!index) {
return null;
}
const metadata = await getSeoMetadata(index, parent);
return metadata;
}
export default async function Page() {
const { data, error } = await getClient().query(futureEventsQuery, {});
const { data, error } = await getClient().query(eventsOverviewQuery, {});
const index = (data?.index ?? []) as EventIndexFragment;
const events = (data?.events?.futureEvents ?? []) as EventFragment[];
const eventCategories = (data?.eventCategories ?? []) as EventCategory[];
const eventOrganizers = (data?.eventOrganizers ?? []) as EventOrganizer[];

View File

@ -1,9 +1,41 @@
import { Metadata, ResolvingMetadata } from "next";
import { notFound } from "next/navigation";
import { getClient } from "@/app/client";
import { AssociationHeader } from "@/components/associations/AssociationHeader";
import { PageContent } from "@/components/general/PageContent";
import { graphql } from "@/gql";
import { AssociationFragment } from "@/gql/graphql";
import { notFound } from "next/navigation";
import { getSeoMetadata } from "@/lib/seo";
const associationBySlugQuery = graphql(`
query associationBySlug($slug: String!) {
association: page(
contentType: "associations.AssociationPage"
slug: $slug
) {
... on AssociationPage {
...Association
}
}
}
`);
export async function generateMetadata(
{ params }: { params: { slug: string } },
parent: ResolvingMetadata
): Promise<Metadata | null> {
const { data, error } = await getClient().query(associationBySlugQuery, {
slug: params.slug,
});
const association = (data?.association ?? []) as AssociationFragment[];
if (!association) {
return null;
}
const metadata = await getSeoMetadata(association, parent);
return metadata;
}
export async function generateStaticParams() {
const allAssociationSlugsQuery = graphql(`
@ -26,19 +58,6 @@ export async function generateStaticParams() {
}
export default async function Page({ params }: { params: { slug: string } }) {
const associationBySlugQuery = graphql(`
query associationBySlug($slug: String!) {
association: page(
contentType: "associations.AssociationPage"
slug: $slug
) {
... on AssociationPage {
...Association
}
}
}
`);
const { data, error } = await getClient().query(associationBySlugQuery, {
slug: params.slug,
});
@ -53,11 +72,11 @@ export default async function Page({ params }: { params: { slug: string } }) {
<main className="site-main" id="main">
<AssociationHeader association={association} />
{association.lead && (
<div
className="lead"
dangerouslySetInnerHTML={{ __html: association.lead }}
/>
)}
<div
className="lead"
dangerouslySetInnerHTML={{ __html: association.lead }}
/>
)}
<PageContent blocks={association.body} />
</main>
);

View File

@ -1,15 +1,51 @@
import { Metadata, ResolvingMetadata } from "next";
import { graphql } from "@/gql";
import { AssociationFragment, AssociationIndexFragment } from "@/gql/graphql";
import { getClient } from "@/app/client";
import { AssociationList } from "@/components/associations/AssociationList";
import Link from "next/link";
import { PageHeader } from "@/components/general/PageHeader";
import { PageContent } from "@/components/general/PageContent";
import { getSeoMetadata } from "@/lib/seo";
const allAssociationsQuery = graphql(`
query allAssociations {
index: associationIndex {
... on AssociationIndex {
...AssociationIndex
}
}
associations: pages(
contentType: "associations.AssociationPage"
limit: 1000
) {
... on AssociationPage {
...Association
}
}
}
`);
export async function generateMetadata(
{ params }: { params: { url: string[] } },
parent: ResolvingMetadata
): Promise<Metadata | null> {
const { data, error } = await getClient().query(allAssociationsQuery, {});
const index = (data?.index ?? []) as AssociationIndexFragment;
if (!index) {
return null;
}
const metadata = await getSeoMetadata(index, parent);
return metadata;
}
const AssociationIndexDefinition = graphql(`
fragment AssociationIndex on AssociationIndex {
... on AssociationIndex {
title
seoTitle
searchDescription
lead
body {
...Blocks
@ -24,6 +60,8 @@ const AssociationFragmentDefinition = graphql(`
id
slug
title
seoTitle
searchDescription
excerpt
lead
body {
@ -40,20 +78,6 @@ const AssociationFragmentDefinition = graphql(`
`);
export default async function Page() {
const allAssociationsQuery = graphql(`
query allAssociations {
index: associationIndex {
... on AssociationIndex {
...AssociationIndex
}
}
associations: pages(contentType: "associations.AssociationPage", limit: 1000) {
... on AssociationPage {
...Association
}
}
}
`);
const { data, error } = await getClient().query(allAssociationsQuery, {});
const associations = (data?.associations ?? []) as AssociationFragment[];
const index = (data?.index ?? []) as AssociationIndexFragment;
@ -62,7 +86,10 @@ export default async function Page() {
<main className="site-main" id="main">
<PageHeader heading={index.title} lead={index.lead} />
{index.body && <PageContent blocks={index.body} />}
<AssociationList associations={associations} heading="Foreninger og utvalg" />
<AssociationList
associations={associations}
heading="Foreninger og utvalg"
/>
</main>
);
}

View File

@ -1,14 +1,28 @@
import { Metadata, ResolvingMetadata } from "next";
import { graphql } from "@/gql";
import { ContactIndexFragment } from "@/gql/graphql";
import { getClient } from "@/app/client";
import { PageHeader } from "@/components/general/PageHeader";
import { PageContent } from "@/components/general/PageContent";
import { GeneralContactBlock } from "@/components/blocks/GeneralContactBlock";
import { getSeoMetadata } from "@/lib/seo";
const contactQuery = graphql(`
query contacts {
index: contactIndex {
... on ContactIndex {
...ContactIndex
}
}
}
`);
const ContactIndexDefinition = graphql(`
fragment ContactIndex on ContactIndex {
... on ContactIndex {
title
seoTitle
searchDescription
lead
body {
...Blocks
@ -17,18 +31,24 @@ const ContactIndexDefinition = graphql(`
}
`);
export default async function Page() {
const contactQuery = graphql(`
query contacts {
contactIndex {
... on ContactIndex {
...ContactIndex
}
}
}
`);
export async function generateMetadata(
{ params }: { params: { url: string[] } },
parent: ResolvingMetadata
): Promise<Metadata | null> {
const { data, error } = await getClient().query(contactQuery, {});
const index = (data?.contactIndex ?? []) as ContactIndexFragment;
const index = (data?.index ?? []) as ContactIndexFragment;
if (!index) {
return null;
}
const metadata = await getSeoMetadata(index, parent);
return metadata;
}
export default async function Page() {
const { data, error } = await getClient().query(contactQuery, {});
const index = (data?.index ?? []) as ContactIndexFragment;
return (
<main className="site-main" id="main">

View File

@ -3,10 +3,26 @@ import { Header } from "@/components/layout/Header";
import { Footer } from "@/components/layout/Footer";
import { Metadata } from "next";
const baseUrlMetadata = process.env.URL
? { metadataBase: new URL(process.env.URL) }
: {};
export const metadata: Metadata = {
title: "Chateau Neuf",
title: {
template: "%s | Chateau Neuf Det Norske Studentersamfund",
default: "Chateau Neuf Det Norske Studentersamfund",
},
alternates: {
canonical: "./",
},
description:
"Chateau Neuf er studenthuset i Oslo og tilholdsstedet for foreningene i Studentersamfundet.",
openGraph: {
siteName: "Chateau Neuf Det Norske Studentersamfund",
locale: "nb-NO",
type: "website",
},
...baseUrlMetadata,
};
export default function RootLayout({

View File

@ -1,14 +1,41 @@
import { Metadata, ResolvingMetadata } from "next";
import { notFound } from "next/navigation";
import { getClient } from "@/app/client";
import { Blocks } from "@/components/blocks/Blocks";
import { ImageSliderBlock } from "@/components/blocks/ImageSliderBlock";
import { Breadcrumb } from "@/components/general/Breadcrumb";
import { Image } from "@/components/general/Image";
import { PageContent } from "@/components/general/PageContent";
import { NeufMap } from "@/components/venues/NeufMap";
import { VenueInfo } from "@/components/venues/VenueInfo";
import { graphql } from "@/gql";
import { VenueFragment } from "@/gql/graphql";
import { notFound } from "next/navigation";
import { getSeoMetadata } from "@/lib/seo";
const venueBySlugQuery = graphql(`
query venueBySlug($slug: String!) {
venue: page(contentType: "venues.VenuePage", slug: $slug) {
... on VenuePage {
...Venue
}
}
}
`);
export async function generateMetadata(
{ params }: { params: { slug: string } },
parent: ResolvingMetadata
): Promise<Metadata | null> {
const { data, error } = await getClient().query(venueBySlugQuery, {
slug: params.slug,
});
const venue = (data?.venue ?? []) as VenueFragment[];
if (!venue) {
return null;
}
const metadata = await getSeoMetadata(venue, parent);
return metadata;
}
export async function generateStaticParams() {
const allVenueSlugsQuery = graphql(`
@ -31,16 +58,6 @@ export async function generateStaticParams() {
}
export default async function Page({ params }: { params: { slug: string } }) {
const venueBySlugQuery = graphql(`
query venueBySlug($slug: String!) {
venue: page(contentType: "venues.VenuePage", slug: $slug) {
... on VenuePage {
...Venue
}
}
}
`);
const { data, error } = await getClient().query(venueBySlugQuery, {
slug: params.slug,
});

View File

@ -1,14 +1,33 @@
import { Metadata, ResolvingMetadata } from "next";
import { graphql } from "@/gql";
import { VenueFragment, VenueIndexFragment } from "@/gql/graphql";
import { getClient } from "@/app/client";
import { VenueList } from "@/components/venues/VenueList";
import { PageHeader } from "@/components/general/PageHeader";
import { PageContent } from "@/components/general/PageContent";
import { getSeoMetadata } from "@/lib/seo";
const venueIndexQuery = graphql(`
query venueIndex {
index: venueIndex {
... on VenueIndex {
...VenueIndex
}
}
venues: pages(contentType: "venues.VenuePage", limit: 100) {
... on VenuePage {
...Venue
}
}
}
`);
const VenueIndexDefinition = graphql(`
fragment VenueIndex on VenueIndex {
... on VenueIndex {
title
seoTitle
searchDescription
lead
body {
...Blocks
@ -23,6 +42,8 @@ const VenueFragmentDefinition = graphql(`
id
slug
title
seoTitle
searchDescription
images {
...Blocks
}
@ -48,21 +69,22 @@ const VenueFragmentDefinition = graphql(`
}
`);
export async function generateMetadata(
{ params }: { params: { url: string[] } },
parent: ResolvingMetadata
): Promise<Metadata | null> {
const { data, error } = await getClient().query(venueIndexQuery, {});
const index = (data?.index ?? []) as VenueIndexFragment;
if (!index) {
return null;
}
const metadata = await getSeoMetadata(index, parent);
return metadata;
}
export default async function Page() {
const venueIndexQuery = graphql(`
query venueIndex {
index: venueIndex {
... on VenueIndex {
...VenueIndex
}
}
venues: pages(contentType: "venues.VenuePage", limit: 100) {
... on VenuePage {
...Venue
}
}
}
`);
const { data, error } = await getClient().query(venueIndexQuery, {});
const index = (data?.index ?? []) as VenueIndexFragment;
const venues = (data?.venues ?? []) as VenueFragment[];

View File

@ -3,6 +3,8 @@ import { getClient } from "@/app/client";
import { SearchContainer } from "@/components/search/SearchContainer";
import { Suspense } from "react";
// TODO: seo metadata?
export default async function Page({
searchParams,
}: {

View File

@ -1,3 +1,4 @@
import { Metadata, ResolvingMetadata } from "next";
import { graphql } from "@/gql";
import { VenueFragment, VenueRentalIndexFragment } from "@/gql/graphql";
import { getClient } from "@/app/client";
@ -5,11 +6,29 @@ import { VenueList } from "@/components/venues/VenueList";
import { PageHeader } from "@/components/general/PageHeader";
import { BgPig } from "@/components/general/BgPig";
import { PageContent } from "@/components/general/PageContent";
import { getSeoMetadata } from "@/lib/seo";
const venueRentalIndexQuery = graphql(`
query venueRentalIndex {
index: venueRentalIndex {
... on VenueRentalIndex {
...VenueRentalIndex
}
}
venues: pages(contentType: "venues.VenuePage", limit: 100) {
... on VenuePage {
...Venue
}
}
}
`);
const VenueRentalIndexDefinition = graphql(`
fragment VenueRentalIndex on VenueRentalIndex {
... on VenueRentalIndex {
title
seoTitle
searchDescription
lead
body {
...Blocks
@ -18,21 +37,22 @@ const VenueRentalIndexDefinition = graphql(`
}
`);
export async function generateMetadata(
{ params }: { params: { url: string[] } },
parent: ResolvingMetadata
): Promise<Metadata | null> {
const { data, error } = await getClient().query(venueRentalIndexQuery, {});
const index = (data?.index ?? []) as VenueRentalIndexFragment;
if (!index) {
return null;
}
const metadata = await getSeoMetadata(index, parent);
return metadata;
}
export default async function Page() {
const venueRentalIndexQuery = graphql(`
query venueRentalIndex {
index: venueRentalIndex {
... on VenueRentalIndex {
...VenueRentalIndex
}
}
venues: pages(contentType: "venues.VenuePage", limit: 100) {
... on VenuePage {
...Venue
}
}
}
`);
const { data, error } = await getClient().query(venueRentalIndexQuery, {});
const index = (data?.index ?? []) as VenueRentalIndexFragment;
const venues = (data?.venues ?? []) as VenueFragment[];