web: render studio page

This commit is contained in:
2026-05-19 06:52:47 +02:00
parent 10c8ce194c
commit 0e5f9f7769
8 changed files with 225 additions and 0 deletions
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 32 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

+80
View File
@@ -0,0 +1,80 @@
import { Metadata, ResolvingMetadata } from "next";
import { graphql } from "@/gql";
import { type StudioFragment } from "@/gql/graphql";
import { getClient } from "@/app/client";
import { PageContent } from "@/components/general/PageContent";
import { BgPig } from "@/components/general/BgPig";
import { StudioHeader } from "@/components/studio/StudioHeader";
import { getSeoMetadata } from "@/lib/seo";
const StudioFragmentDefinition = graphql(`
fragment Studio on StudioPage {
__typename
id
title
seoTitle
searchDescription
lead
pig
logo {
url
width
height
alt
}
body {
...Blocks
}
}
`);
const studioPageQuery = graphql(`
query studio {
page: studioPage {
... on StudioPage {
...Studio
}
}
}
`);
export async function generateMetadata(
{ params }: { params: Promise<{}> },
parent: ResolvingMetadata
): Promise<Metadata | null> {
const { data, error } = await getClient().query(studioPageQuery, {});
if (error) {
throw new Error(error.message);
}
if (!data?.page) {
return null;
}
const page = data.page as StudioFragment;
const metadata = await getSeoMetadata(page, parent);
return metadata;
}
export default async function Page() {
const { data, error } = await getClient().query(studioPageQuery, {});
if (error) {
throw new Error(error.message);
}
if (!data?.page) {
throw new Error("Failed to render /studio");
}
const page = data.page as StudioFragment;
return (
<>
<main className="site-main" id="main">
<StudioHeader title={page.title} lead={page.lead} />
{page.body && <PageContent blocks={page.body} />}
</main>
{page.pig && <BgPig type={page.pig} color="white" />}
</>
);
}
@@ -0,0 +1,35 @@
import styles from "./studioHeader.module.scss";
export const StudioHeader = ({
title,
lead,
}: {
title: string;
lead?: string | null;
}) => {
return (
<div className={styles.studioHeader}>
<h1 className="sr-only">{title}</h1>
<div className={styles.logos}>
<img
className={styles.studentfestivalen}
src="/assets/graphics/studio-2026/studio-studentfestivalen-i-oslo.svg"
alt="Studentfestivalen i Oslo"
/>
<img
className={styles.mainLogo}
src="/assets/graphics/studio-2026/studio-logo-2026.svg"
alt="STUDiO"
/>
<img
className={styles.fadderuke}
src="/assets/graphics/studio-2026/studio-hele-oslos-fadderuke.svg"
alt="Hele oslos fadderuke"
/>
</div>
{lead && (
<div className="lead" dangerouslySetInnerHTML={{ __html: lead }} />
)}
</div>
);
};
@@ -0,0 +1,36 @@
.studioHeader {
position: relative;
width: var(--size-width-lead);
max-width: 100%;
margin: 0 auto var(--spacing-l);
}
.logos {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--spacing-xs);
margin-bottom: var(--spacing-m);
}
.mainLogo {
display: block;
width: 100%;
max-width: 16rem;
height: auto;
margin: var(--spacing-xs) 0;
}
.studentfestivalen {
display: block;
width: 100%;
max-width: 27rem;
height: auto;
}
.fadderuke {
display: block;
width: 100%;
max-width: 30rem;
height: auto;
}
+12
View File
@@ -38,6 +38,8 @@ type Documents = {
"\n query search($query: String) {\n results: search(query: $query) {\n __typename\n ... on PageInterface {\n slug\n }\n ... on NewsPage {\n id\n title\n }\n ... on EventPage {\n id\n title\n }\n ... on GenericPage {\n id\n title\n }\n ... on VenuePage {\n id\n title\n }\n ... on AssociationPage {\n id\n title\n associationType\n }\n }\n }\n ": typeof types.SearchDocument,
"\n query sponsors {\n page: sponsorsPage {\n ... on SponsorsPage {\n ...SponsorsPage\n }\n }\n }\n": typeof types.SponsorsDocument,
"\n fragment SponsorsPage on SponsorsPage {\n ... on SponsorsPage {\n title\n seoTitle\n searchDescription\n lead\n body {\n ...Blocks\n }\n sponsors {\n ... on SponsorBlock {\n ...Sponsor\n }\n }\n }\n }\n": typeof types.SponsorsPageFragmentDoc,
"\n fragment Studio on StudioPage {\n __typename\n id\n title\n seoTitle\n searchDescription\n lead\n pig\n logo {\n url\n width\n height\n alt\n }\n body {\n ...Blocks\n }\n }\n": typeof types.StudioFragmentDoc,
"\n query studio {\n page: studioPage {\n ... on StudioPage {\n ...Studio\n }\n }\n }\n": typeof types.StudioDocument,
"\n query venueRentalIndex {\n index: venueRentalIndex {\n ... on VenueRentalIndex {\n ...VenueRentalIndex\n }\n }\n venues: pages(contentType: \"venues.VenuePage\", limit: 100) {\n ... on VenuePage {\n ...Venue\n }\n }\n }\n": typeof types.VenueRentalIndexDocument,
"\n fragment VenueRentalIndex on VenueRentalIndex {\n ... on VenueRentalIndex {\n title\n seoTitle\n searchDescription\n lead\n body {\n ...Blocks\n }\n }\n }\n": typeof types.VenueRentalIndexFragmentDoc,
"\n fragment AccordionBlock on AccordionBlock {\n heading\n body {\n id\n blockType\n }\n }\n": typeof types.AccordionBlockFragmentDoc,
@@ -99,6 +101,8 @@ const documents: Documents = {
"\n query search($query: String) {\n results: search(query: $query) {\n __typename\n ... on PageInterface {\n slug\n }\n ... on NewsPage {\n id\n title\n }\n ... on EventPage {\n id\n title\n }\n ... on GenericPage {\n id\n title\n }\n ... on VenuePage {\n id\n title\n }\n ... on AssociationPage {\n id\n title\n associationType\n }\n }\n }\n ": types.SearchDocument,
"\n query sponsors {\n page: sponsorsPage {\n ... on SponsorsPage {\n ...SponsorsPage\n }\n }\n }\n": types.SponsorsDocument,
"\n fragment SponsorsPage on SponsorsPage {\n ... on SponsorsPage {\n title\n seoTitle\n searchDescription\n lead\n body {\n ...Blocks\n }\n sponsors {\n ... on SponsorBlock {\n ...Sponsor\n }\n }\n }\n }\n": types.SponsorsPageFragmentDoc,
"\n fragment Studio on StudioPage {\n __typename\n id\n title\n seoTitle\n searchDescription\n lead\n pig\n logo {\n url\n width\n height\n alt\n }\n body {\n ...Blocks\n }\n }\n": types.StudioFragmentDoc,
"\n query studio {\n page: studioPage {\n ... on StudioPage {\n ...Studio\n }\n }\n }\n": types.StudioDocument,
"\n query venueRentalIndex {\n index: venueRentalIndex {\n ... on VenueRentalIndex {\n ...VenueRentalIndex\n }\n }\n venues: pages(contentType: \"venues.VenuePage\", limit: 100) {\n ... on VenuePage {\n ...Venue\n }\n }\n }\n": types.VenueRentalIndexDocument,
"\n fragment VenueRentalIndex on VenueRentalIndex {\n ... on VenueRentalIndex {\n title\n seoTitle\n searchDescription\n lead\n body {\n ...Blocks\n }\n }\n }\n": types.VenueRentalIndexFragmentDoc,
"\n fragment AccordionBlock on AccordionBlock {\n heading\n body {\n id\n blockType\n }\n }\n": types.AccordionBlockFragmentDoc,
@@ -246,6 +250,14 @@ export function graphql(source: "\n query sponsors {\n page: sponsorsPage {\
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment SponsorsPage on SponsorsPage {\n ... on SponsorsPage {\n title\n seoTitle\n searchDescription\n lead\n body {\n ...Blocks\n }\n sponsors {\n ... on SponsorBlock {\n ...Sponsor\n }\n }\n }\n }\n"): (typeof documents)["\n fragment SponsorsPage on SponsorsPage {\n ... on SponsorsPage {\n title\n seoTitle\n searchDescription\n lead\n body {\n ...Blocks\n }\n sponsors {\n ... on SponsorBlock {\n ...Sponsor\n }\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment Studio on StudioPage {\n __typename\n id\n title\n seoTitle\n searchDescription\n lead\n pig\n logo {\n url\n width\n height\n alt\n }\n body {\n ...Blocks\n }\n }\n"): (typeof documents)["\n fragment Studio on StudioPage {\n __typename\n id\n title\n seoTitle\n searchDescription\n lead\n pig\n logo {\n url\n width\n height\n alt\n }\n body {\n ...Blocks\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query studio {\n page: studioPage {\n ... on StudioPage {\n ...Studio\n }\n }\n }\n"): (typeof documents)["\n query studio {\n page: studioPage {\n ... on StudioPage {\n ...Studio\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
File diff suppressed because one or more lines are too long