Merged in feat/block-pages (pull request #12)

feat: graphql client with fetches for initial pages

Approved-by: Arvid Norlin
This commit is contained in:
Simon.Emanuelsson
2024-02-07 11:04:08 +00:00
committed by Arvid Norlin
56 changed files with 2580 additions and 1512 deletions

4
.env.example Normal file
View File

@@ -0,0 +1,4 @@
CMS_ACCESS_TOKEN=""
CMS_API_KEY=""
CMS_ENVIRONMENT="development"
CMS_URL="https://eu-graphql.contentstack.com/stacks/${CMS_API_KEY}?environment=${CMS_ENVIRONMENT}"

View File

@@ -1,10 +1,15 @@
import { notFound } from "next/navigation";
import fs from "node:fs/promises";
import path from "node:path";
import { request } from "@/lib/request"
import { GetCurrentBlockPage } from "@/lib/graphql/Query/CurrentBlockPage.graphql"
import Aside from "@/components/Current/Aside";
import Blocks from "@/components/Current/Blocks";
import Header from "@/components/Current/Header";
import Hero from "@/components/Current/Hero";
import type { PageArgs, LangParams, UriParams } from "@/types/params";
import type { GetCurrentBlockPageData } from "@/types/requests/currentBlockPage";
export default async function CurrentContentPage({
params,
@@ -15,39 +20,30 @@ export default async function CurrentContentPage({
throw new Error("Bad URI");
}
const filePath = path.join(
process.cwd(),
"mockCms",
params.lang,
searchParams.uri,
"data.json"
);
const response = await request<GetCurrentBlockPageData>(GetCurrentBlockPage, { locale: params.lang, url: searchParams.uri })
const data = await fs.readFile(filePath, { encoding: "utf-8" });
if (!data) {
throw new Error("No data");
if (!response.data?.all_current_blocks_page?.total) {
console.log("#### DATA ####")
console.log(response.data)
console.log("SearchParams URI: ", searchParams.uri)
throw new Error("Not found")
}
const json = JSON.parse(data);
const page = response.data.all_current_blocks_page.items[0]
const images = page.hero?.imagesConnection
return (
<>
<Header lang={params.lang} pathname={searchParams.uri} />
{json.hero ? (
<div
dangerouslySetInnerHTML={{ __html: json.hero }}
className="hero-content-overlay hero-content-widget"
/>
) : null}
{json.content ? (
<main
className="main l-sections-wrapper"
role="main"
id="maincontent"
dangerouslySetInnerHTML={{ __html: json.content }}
/>
) : null}
{images?.totalCount ? <Hero images={images.edges} /> : null}
<main
className="main l-sections-wrapper"
id="maincontent"
role="main"
>
<Blocks blocks={page.blocks} />
<Aside blocks={page.aside} />
</main>
</>
);
} catch (err) {

View File

@@ -32,10 +32,10 @@ export default function RootLayout({
id="Cookiebot"
src="https://consent.cookiebot.com/uc.js"
/>
<Script
{/* <Script
data-cookieconsent="ignore"
src="/Static/dist/js/head.js?85c84c9e24ae8da3e7af"
/>
/> */}
<Script
data-cookieconsent="ignore"
src="/Static/dist/js/inline.js?00133e5a37de35c51a5d"
@@ -56,11 +56,11 @@ export default function RootLayout({
data-cookieconsent="ignore"
src="/Static/dist/js/ng/main.js?1705409330990"
/>
<Script
{/* <Script
data-cookieconsent="ignore"
src="/Static/dist/js/main-ng.js?336b801d6b38eff10884"
strategy="lazyOnload"
/>
/> */}
</head>
<body>
<LangPopup lang={params.lang} />

8
app/[lang]/not-found.tsx Normal file
View File

@@ -0,0 +1,8 @@
export default function NotFound() {
return (
<main>
<h2>Not Found</h2>
<p>Could not find requested resource</p>
</main>
)
}

View File

@@ -1,3 +1,7 @@
export default function Home() {
return <main>Hello world!</main>;
export default async function Home() {
return (
<main>
<h1>Hello world!</h1>
</main>
);
}

View File

@@ -0,0 +1,9 @@
import type { AsideProps } from "@/types/components/current/aside"
export default function Aside({ }: AsideProps) {
return (
<>
</>
)
}

View File

@@ -0,0 +1,34 @@
import List from "./Blocks/List"
import Preamble from "./Blocks/Preamble"
import Puffs from "./Blocks/Puffs"
import Text from "./Blocks/Text"
import { BlocksTypenameEnum } from "@/types/requests/utils/typename"
import type { BlocksProps } from "@/types/components/current/blocks"
export default function Blocks({ blocks }: BlocksProps) {
if (!blocks?.length) {
return null
}
return (
<>
{blocks.map(block => {
const type = block.__typename
switch (type) {
case BlocksTypenameEnum.CurrentBlocksPageBlocksList:
return <List key={block.__typename} {...block} />
case BlocksTypenameEnum.CurrentBlocksPageBlocksPreamble:
return <Preamble key={block.__typename} {...block} />
case BlocksTypenameEnum.CurrentBlocksPageBlocksPuffs:
return <Puffs key={block.__typename} {...block} />
case BlocksTypenameEnum.CurrentBlocksPageBlocksText:
return <Text key={block.__typename} {...block} />
default:
console.log(`Unknown type: (${type})`)
return null
}
})}
</>
)
}

View File

@@ -0,0 +1,7 @@
export default function List() {
return (
<>
</>
)
}

View File

@@ -0,0 +1,7 @@
export default function Preamble() {
return (
<>
</>
)
}

View File

@@ -0,0 +1,7 @@
export default function Puffs() {
return (
<>
</>
)
}

View File

@@ -0,0 +1,32 @@
import { rteType } from "@/types/rte";
import Image from "next/image";
import type { TextProps } from "@/types/components/current/blocks/text"
export default function Text({ text }: TextProps) {
return (
<>
<pre>{JSON.stringify(text.content.json, null, 2)}</pre>
{text.content.json.children.map(block => {
switch (block.type) {
case rteType.reference: {
if (block.attrs.type === rteType.asset) {
// return (
// <Image
// alt={block.attrs.alt}
// src={block.attrs["asset-link"]}
// />
// )
}
return null
}
default:
break;
}
})}
</>
)
}

View File

@@ -0,0 +1,4 @@
.heroImage {
object-fit: cover;
width: 100%;
}

View File

@@ -0,0 +1,55 @@
"use client"
import Image from "next/image"
import styles from "./hero.module.css"
import type { HeroProps } from "@/types/components/current/hero"
export default function Hero({ images }: HeroProps) {
return (
<div className="hero-content-overlay hero-content-widget">
<div className="hero-content-overlay__img-container">
<div className="hero-fixed hero-fixed--deemphasized" style={{ marginTop: "0px" }}>
<div className="hero" style={{ top: "113px" }}>
<div className="hero__img-container">
<div id="full-width-slider" className="royalSlider royalSlider--hero rsDefault rsHor rsFade rsWithBullets" data-js="hero-slider" tabIndex={0} aria-label="Carousel. Change slides with keyboard left and right arrows." style={{ touchAction: "pan-y" }}>
<div className="rsOverflow" style={{ width: "1555px", height: "650px" }}>
<div className="rsContainer">
<div style={{ zIndex: 0 }} className="rsSlide ">
<picture style={{ visibility: "visible", opacity: 1, transition: "opacity 400ms ease-in-out 0s" }}>
{images.map(({ node: image }) => (
<Image
alt={image.title}
className={`rsImg-x slider-plchldr ${styles.heroImage}`}
data-aspectratio="1.50"
height={image.dimension.height}
key={image.title}
src={image.url}
unoptimized
width={image.dimension.width}
/>
))}
</picture>
</div>
</div>
<div className="rsArrow rsArrowLeft" style={{ display: "none" }}>
<div className="rsArrowIcn" tabIndex={0} />
</div>
<div className="rsArrow rsArrowRight" style={{ display: "none" }}>
<div className="rsArrowIcn" tabIndex={0} />
</div>
</div>
<div className="rsNav rsBullets">
<div className="rsNavItem rsBullet rsNavSelected">
<span></span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
)
}

36
env/client.mjs vendored Normal file
View File

@@ -0,0 +1,36 @@
// @ts-check
import { clientEnv, clientSchema } from './schema.mjs'
const _clientEnv = clientSchema.safeParse(clientEnv)
export const formatErrors = (
/** @type {import('zod').ZodFormattedError<Map<string,string>,string>} */
errors
) =>
Object.entries(errors)
.map(([name, value]) => {
if (value && '_errors' in value) {
return `${name}: ${value._errors.join(', ')}\n`
}
return null
})
.filter(Boolean)
if (!_clientEnv.success) {
const msg = [
'❌ Invalid environment variables [Client]:\n',
...formatErrors(_clientEnv.error.format()),
].join('')
console.error(msg)
throw new Error(msg)
}
for (let key of Object.keys(_clientEnv.data)) {
if (!key.startsWith('NEXT_PUBLIC_')) {
const msg = `❌ Invalid public environment variable name: ${key}. It must begin with 'NEXT_PUBLIC_'`
console.warn(msg)
throw new Error(msg)
}
}
export const env = _clientEnv.data

46
env/schema.mjs vendored Normal file
View File

@@ -0,0 +1,46 @@
// @ts-check
import { z } from "zod"
/**
* Specify your server-side environment variables schema here.
* This way you can ensure the app isn"t built with invalid env vars.
*/
export const serverSchema = z.object({
CMS_ACCESS_TOKEN: z.string(),
CMS_API_KEY: z.string(),
CMS_ENVIRONMENT: z.enum(["development", "production", "staging", "test"]),
CMS_URL: z.string(),
NODE_ENV: z.enum(["development", "test", "production"]),
PRINT_QUERY: z.boolean().default(false),
})
/**
* You can't destruct `process.env` as a regular object in the Next.js
* middleware, so you have to do it manually here.
* @type {{ [k in keyof z.input<typeof serverSchema>]: string | undefined }}
*/
export const serverEnv = {
CMS_ACCESS_TOKEN: process.env.CMS_ACCESS_TOKEN,
CMS_API_KEY: process.env.CMS_API_KEY,
CMS_ENVIRONMENT: process.env.CMS_ENVIRONMENT,
CMS_URL: process.env.CMS_URL,
NODE_ENV: process.env.NODE_ENV,
PRINT_QUERY: process.env.PRINT_QUERY,
}
/**
* Specify your client-side environment variables schema here.
* This way you can ensure the app isn"t built with invalid env vars.
* To expose them to the client, prefix them with `NEXT_PUBLIC_`.
*/
export const clientSchema = z.object({
})
/**
* You can't destruct `process.env` as a regular object, so you have to do
* it manually here. This is because Next.js evaluates this at build time,
* and only used environment variables are included in the build.
* @type {{ [k in keyof z.infer<typeof clientSchema>]: z.infer<typeof clientSchema>[k] | undefined }}
*/
export const clientEnv = {
}

33
env/server.mjs vendored Normal file
View File

@@ -0,0 +1,33 @@
// @ts-check
/**
* This file is included in `/next.config.mjs` which ensures the app isn't built with invalid env vars.
* It has to be a `.mjs`-file to be imported there.
*/
import { serverSchema, serverEnv } from './schema.mjs'
import { env as clientEnv, formatErrors } from './client.mjs'
const _serverEnv = serverSchema.safeParse(serverEnv)
if (!_serverEnv.success) {
const msg = [
'❌ Invalid environment variables [Server]:\n',
...formatErrors(_serverEnv.error.format()),
].join('')
console.error(msg)
throw new Error(msg)
}
for (let key of Object.keys(_serverEnv.data)) {
const msg = []
if (key.startsWith('NEXT_PUBLIC_')) {
msg.push(
`❌ You are exposing a server-side env-variable: ${key}\n`
)
}
if (msg.length) {
console.warn(msg.join(''))
throw new Error(msg.join(''))
}
}
export const env = { ..._serverEnv.data, ...clientEnv }

View File

@@ -0,0 +1,14 @@
#import "../Contact.graphql"
fragment ContactAside on CurrentBlocksPageAsideContact {
contact {
contactConnection {
edges {
node {
...Contact
}
}
totalCount
}
}
}

View File

@@ -0,0 +1,14 @@
#import "../Puff.graphql"
fragment PuffAside on CurrentBlocksPageAsidePuff {
puff {
puffConnection {
totalCount
edges {
node {
...Puff
}
}
}
}
}

View File

@@ -0,0 +1,49 @@
#import "../PageLinks.graphql"
fragment ListItem on CurrentBlocksPageBlocksListBlockListItemsListItem {
list_item {
list_item_style
subtitle
title
}
}
fragment ListItemExternalLink on CurrentBlocksPageBlocksListBlockListItemsListItemExternalLink {
list_item_external_link {
link {
href
title
}
list_item_style
subtitle
}
}
fragment ListItemInternalLink on CurrentBlocksPageBlocksListBlockListItemsListItemInternalLink {
list_item_internal_link {
link_text
list_item_style
subtitle
pageConnection {
totalCount
edges {
node {
...CurrentBlocksPageLink
...TempPageLink
}
}
}
}
}
fragment ListBlock on CurrentBlocksPageBlocksList {
list {
list_items {
__typename
...ListItem
...ListItemExternalLink
...ListItemInternalLink
}
title
}
}

View File

@@ -0,0 +1,21 @@
#import "../PageLinks.graphql"
fragment PreambleBlock on CurrentBlocksPageBlocksPreamble {
preamble {
text {
json
embedded_itemsConnection(limit: 30) {
edges {
node {
...CurrentBlocksPageLink
...TempPageLink
... on SysAsset {
title
url
}
}
}
}
}
}
}

View File

@@ -0,0 +1,15 @@
#import "../Puff.graphql"
fragment PuffBlock on CurrentBlocksPageBlocksPuffs {
puffs {
# We have to manually add a limit since Contentstack handles its complexity calculation in a certain way
puffsConnection(limit: 9) {
totalCount
edges {
node {
...Puff
}
}
}
}
}

View File

@@ -0,0 +1,22 @@
#import "../PageLinks.graphql"
fragment TextBlock on CurrentBlocksPageBlocksText {
text {
content {
embedded_itemsConnection {
totalCount
edges {
node {
...CurrentBlocksPageLink
...TempPageLink
... on SysAsset {
title
url
}
}
}
}
json
}
}
}

View File

@@ -0,0 +1,17 @@
fragment Contact on ContactBlock {
mailing_address {
city
country
name
street
zip
}
phone
title
visiting_address {
city
country
street
zip
}
}

View File

@@ -0,0 +1,17 @@
#import "./PageLinks.graphql"
fragment Hero on Hero {
imagesConnection {
totalCount
edges {
node {
dimension {
height
width
}
title
url
}
}
}
}

View File

@@ -0,0 +1,9 @@
fragment CurrentBlocksPageLink on CurrentBlocksPage {
title
url
}
fragment TempPageLink on TempPage {
title
url
}

View File

@@ -0,0 +1,43 @@
#import "./PageLinks.graphql"
fragment Puff on Puff {
imageConnection {
edges {
node {
title
url
}
}
}
is_internal
link {
href
title
}
link_text
pageConnection {
edges {
node {
__typename
...CurrentBlocksPageLink
...TempPageLink
}
}
}
text {
json
embedded_itemsConnection {
totalCount
edges {
node {
__typename
... on SysAsset {
title
url
}
}
}
}
}
title
}

View File

@@ -0,0 +1,32 @@
#import "../Fragments/Aside/Contact.graphql"
#import "../Fragments/Aside/Puff.graphql"
#import "../Fragments/Blocks/List.graphql"
#import "../Fragments/Blocks/Preamble.graphql"
#import "../Fragments/Blocks/Puff.graphql"
#import "../Fragments/Blocks/Text.graphql"
#import "../Fragments/Hero.graphql"
query GetCurrentBlockPage($locale: String!, $url: String!) {
all_current_blocks_page(limit: 1, locale: $locale, where: { url: $url }) {
items {
aside {
__typename
...ContactAside
...PuffAside
}
blocks {
__typename
...ListBlock
...PreambleBlock
...PuffBlock
...TextBlock
}
hero {
...Hero
}
title
url
}
total
}
}

52
lib/request.ts Normal file
View File

@@ -0,0 +1,52 @@
import "server-only"
import { request as graphqlRequest } from "graphql-request"
import { env } from "@/env/server.mjs"
import type { Data } from "@/types/request"
import type { DocumentNode } from "graphql";
export async function request<T>(query: string | DocumentNode, variables?: {}): Promise<Data<T>> {
try {
if (env.PRINT_QUERY) {
const graphqlRawRequest = (await import("graphql-request")).rawRequest
const print = (await import("graphql/language/printer")).print
const rawResponse = await graphqlRawRequest<T>(
env.CMS_URL,
print(query as DocumentNode),
variables,
{
"access_token": env.CMS_ACCESS_TOKEN,
"Content-Type": "application/json",
}
)
/**
* TODO: Send to Monitoring (Logging and Metrics)
*/
console.log({ complexityLimit: rawResponse.headers.get("x-query-complexity") })
console.log({ referenceDepth: rawResponse.headers.get("x-reference-depth") })
console.log({ resolverCost: rawResponse.headers.get("x-resolver-cost") })
return {
data: rawResponse.data,
}
}
const response = await graphqlRequest<T>({
document: query,
requestHeaders: {
"access_token": env.CMS_ACCESS_TOKEN,
"Content-Type": "application/json",
},
url: env.CMS_URL,
variables,
})
return { data: response }
} catch (error) {
console.error(error)
throw new Error("Something went wrong")
}
}

View File

@@ -1,4 +1,24 @@
/** @type {import('next').NextConfig} */
const nextConfig = {};
const nextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "eu-images.contentstack.com",
pathname: "/v3/assets/**",
},
],
},
webpack: function (config, options) {
config.module.rules.push({
test: /\.(graphql|gql)/,
exclude: /node_modules/,
loader: "graphql-tag/loader"
});
return config;
}
};
module.exports = nextConfig;

108
package-lock.json generated
View File

@@ -8,9 +8,14 @@
"name": "web",
"version": "0.1.0",
"dependencies": {
"graphql": "16.8.1",
"graphql-request": "6.1.0",
"graphql-tag": "2.12.6",
"next": "14.0.4",
"react": "^18",
"react-dom": "^18"
"react-dom": "^18",
"server-only": "0.0.1",
"zod": "3.22.4"
},
"devDependencies": {
"@types/node": "^20",
@@ -101,6 +106,14 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@graphql-typed-document-node/core": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz",
"integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==",
"peerDependencies": {
"graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.11.13",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
@@ -889,6 +902,14 @@
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
"node_modules/cross-fetch": {
"version": "3.1.8",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz",
"integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==",
"dependencies": {
"node-fetch": "^2.6.12"
}
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -1877,6 +1898,40 @@
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
"dev": true
},
"node_modules/graphql": {
"version": "16.8.1",
"resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz",
"integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==",
"engines": {
"node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
}
},
"node_modules/graphql-request": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-6.1.0.tgz",
"integrity": "sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==",
"dependencies": {
"@graphql-typed-document-node/core": "^3.2.0",
"cross-fetch": "^3.1.5"
},
"peerDependencies": {
"graphql": "14 - 16"
}
},
"node_modules/graphql-tag": {
"version": "2.12.6",
"resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz",
"integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==",
"dependencies": {
"tslib": "^2.1.0"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
}
},
"node_modules/has-bigints": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
@@ -2625,6 +2680,25 @@
}
}
},
"node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -3156,6 +3230,11 @@
"node": ">=10"
}
},
"node_modules/server-only": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz",
"integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA=="
},
"node_modules/set-function-length": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
@@ -3416,6 +3495,11 @@
"node": ">=8.0"
}
},
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/ts-api-utils": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz",
@@ -3589,6 +3673,20 @@
"node": ">=10.13.0"
}
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -3703,6 +3801,14 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/zod": {
"version": "3.22.4",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz",
"integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
}
}
}

View File

@@ -9,9 +9,14 @@
"lint": "next lint && tsc"
},
"dependencies": {
"graphql": "16.8.1",
"graphql-request": "6.1.0",
"graphql-tag": "2.12.6",
"next": "14.0.4",
"react": "^18",
"react-dom": "^18"
"react-dom": "^18",
"server-only": "0.0.1",
"zod": "3.22.4"
},
"devDependencies": {
"@types/node": "^20",

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,11 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@@ -19,9 +23,18 @@
}
],
"paths": {
"@/*": ["./*"]
"@/*": [
"./*"
]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}

View File

@@ -0,0 +1,5 @@
import type { Asides } from "@/types/requests/currentBlockPage"
export type AsideProps = {
blocks: Asides[]
}

View File

@@ -0,0 +1,5 @@
import type { Blocks } from "@/types/requests/currentBlockPage"
export type BlocksProps = {
blocks: Blocks[]
}

View File

@@ -0,0 +1,3 @@
import type { Text } from "@/types/requests/blocks/text"
export type TextProps = Text

View File

@@ -0,0 +1,7 @@
import type { Image } from "@/types/image"
export type HeroProps = {
images: {
node: Image
}[]
}

1
types/graphql.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
declare module '*.graphql'

8
types/image.ts Normal file
View File

@@ -0,0 +1,8 @@
export type Image = {
dimension: {
height: number
width: number
}
title: string
url: string
}

3
types/request.ts Normal file
View File

@@ -0,0 +1,3 @@
export type Data<T> = {
data: T
}

View File

@@ -0,0 +1,23 @@
import type { Edges } from "../utils/edges"
export type Contact = {
contact: {
contactConnection: Edges<{
mailing_address: {
city: string
country: string
name: string
street: string
zip: string
}
phone: string
title: string
visiting_address: {
city: string
country: string
street: string
zip: string
}
}>
}
}

View File

@@ -0,0 +1,8 @@
import type { Puff } from "../puff"
import type { Edges } from "../utils/edges"
export type PuffAside = {
puff: {
puffConnection: Edges<Puff>
}
}

View File

@@ -0,0 +1,46 @@
import type { Edges } from "../utils/edges"
import type { ExternalLink } from "../utils/externalLink"
import type { PageLink } from "../utils/pagelink"
enum ListItemStyleEnum {
checkmark = "checkmark",
default = "default",
}
type ListItemStyle = keyof typeof ListItemStyleEnum
type ExternalLinkListItem = {
list_item_external_link: {
link: {
href: string
title: string
}
list_item_style: ListItemStyle
subtitle?: string
}
}
type InternalLinkListItem = {
list_item_internal_link: {
link_text?: string
list_item_style: ListItemStyle
subtitle?: string
pageConnection: Edges<ExternalLink | PageLink>
}
}
type RegularListItem = {
list_item: {
list_item_style: ListItemStyle
subtitle?: string
title: string
}
}
type ListItem = ExternalLinkListItem | InternalLinkListItem | RegularListItem
export type List = {
list: {
list_items: ListItem
}
}

View File

@@ -0,0 +1,17 @@
import type { SysAsset } from "../utils/asset"
import type { Edges } from "../utils/edges"
import type { ExternalLink } from "../utils/externalLink"
import type { PageLink } from "../utils/pagelink"
export type Preamble = {
preamble: {
text: {
json: JSON
embedded_itemsConnection: Edges<
| ExternalLink
| PageLink
| SysAsset
>
}
}
}

View File

@@ -0,0 +1,8 @@
import type { Edges } from "../utils/edges"
import type { Puff } from "../puff"
export type PuffBlock = {
puffs: {
puffsConnection: Edges<Puff>
}
}

View File

@@ -0,0 +1,19 @@
import type { RTERootObject } from "@/types/rte"
import type { SysAsset } from "../utils/asset"
import type { Edges } from "../utils/edges"
import type { ExternalLink } from "../utils/externalLink"
import type { PageLink } from "../utils/pagelink"
export type Text = {
text: {
content: {
json: RTERootObject
embedded_itemsConnection: Edges<
| ExternalLink
| PageLink
| SysAsset
>
}
}
}

View File

@@ -0,0 +1,33 @@
import { BlocksTypenameEnum } from "./utils/typename"
import type { Contact } from "./asides/contact"
import type { PuffAside } from "./asides/puff"
import type { Hero } from "./hero"
import type { List } from "./blocks/list"
import type { PuffBlock } from "./blocks/puff"
import type { Preamble } from "./blocks/preamble"
import type { Text } from "./blocks/text"
import type { AllRequestResponse } from "./utils/all"
import type { AsideTypenameEnum, Typename } from "./utils/typename"
export type Asides =
| Typename<Contact, AsideTypenameEnum.CurrentBlocksPageAsideContact>
| Typename<PuffAside, AsideTypenameEnum.CurrentBlocksPageAsidePuff>
export type Blocks =
| Typename<List, BlocksTypenameEnum.CurrentBlocksPageBlocksList>
| Typename<Preamble, BlocksTypenameEnum.CurrentBlocksPageBlocksPreamble>
| Typename<PuffBlock, BlocksTypenameEnum.CurrentBlocksPageBlocksPuffs>
| Typename<Text, BlocksTypenameEnum.CurrentBlocksPageBlocksText>
export type BlockPage = {
aside: Asides[]
blocks: Blocks[]
hero: Hero
title: string
url: string
}
export type GetCurrentBlockPageData = {
all_current_blocks_page: AllRequestResponse<BlockPage>
}

6
types/requests/hero.ts Normal file
View File

@@ -0,0 +1,6 @@
import type { Image } from "../image"
import type { Edges } from "./utils/edges"
export type Hero = {
imagesConnection: Edges<Image>
}

26
types/requests/puff.ts Normal file
View File

@@ -0,0 +1,26 @@
import type { Edges } from "./utils/edges"
import type { ExternalLink } from "./utils/externalLink"
import type { PageLink } from "./utils/pagelink"
import type { Typename } from "./utils/typename"
export type Puff = {
imageConnection: Edges<{
title: string
url: string
}>
is_internal: boolean
link: {
href: string
title: string
}
link_text?: string
pageConnection: Edges<ExternalLink | PageLink>
text: {
json: JSON
embedded_itemsConnection: Edges<Typename<{
title: string
url: string
}, "SysAsset">>
}
title: string
}

View File

@@ -0,0 +1,4 @@
export type AllRequestResponse<T> = {
items: T[]
total: number
}

View File

@@ -0,0 +1,8 @@
import type { Typename } from "./typename"
export type Asset = {
title: string
url: string
}
export type SysAsset = Typename<Asset, "SysAsset">

View File

@@ -0,0 +1,8 @@
export type Node<T> = {
node: T
}
export type Edges<T> = {
edges: Node<T>[]
totalCount: number
}

View File

@@ -0,0 +1,5 @@
export type ExternalLink = {
__typename: "TempPage"
title: string
url: string
}

View File

@@ -0,0 +1,5 @@
export type PageLink = {
__typename: "CurrentBlocksPage"
title: string
url: string
}

View File

@@ -0,0 +1,31 @@
export const AsideTypenames = {
CurrentBlocksPageAsideContact: "CurrentBlocksPageAsideContact",
CurrentBlocksPageAsidePuff: "CurrentBlocksPageAsidePuff",
}
export enum AsideTypenameEnum {
CurrentBlocksPageAsideContact = "CurrentBlocksPageAsideContact",
CurrentBlocksPageAsidePuff = "CurrentBlocksPageAsidePuff",
}
export type AsideTypename = keyof typeof AsideTypenameEnum
export const blocksTypenameEnum = {
CurrentBlocksPageBlocksList: "CurrentBlocksPageBlocksList",
CurrentBlocksPageBlocksPreamble: "CurrentBlocksPageBlocksPreamble",
CurrentBlocksPageBlocksPuffs: "CurrentBlocksPageBlocksPuffs",
CurrentBlocksPageBlocksText: "CurrentBlocksPageBlocksText",
}
export type BlocksTypename = keyof typeof blocksTypenameEnum
export enum BlocksTypenameEnum {
CurrentBlocksPageBlocksList = "CurrentBlocksPageBlocksList",
CurrentBlocksPageBlocksPreamble = "CurrentBlocksPageBlocksPreamble",
CurrentBlocksPageBlocksPuffs = "CurrentBlocksPageBlocksPuffs",
CurrentBlocksPageBlocksText = "CurrentBlocksPageBlocksText",
}
export type Typename<T, K> = T & {
__typename: K
}

58
types/rte.ts Normal file
View File

@@ -0,0 +1,58 @@
export const rteType = {
asset: "asset",
p: "p",
reference: "reference",
}
export enum RTETypeEnum {
asset = "asset",
p = "p",
reference = "reference",
}
export type RTEType = keyof typeof rteType
export type RTEText = {
text: string
}
export type RTEAssetAttrs = {
"alt": string
"asset-alt": string
"asset-link": string
"asset-name": string
"asset-type": "image/png" | "image/jpg" | "image/jpeg"
"asset-uid": string
"display-type": "display"
"class-name": string
"content-type-uid": "sys_assets"
"inline": false
"type": RTETypeEnum.asset
}
type RTEBaseObject<T> = {
type: T
uid: string
}
type AssetReferenceObject = RTEBaseObject<RTETypeEnum.reference> & {
attrs: RTEAssetAttrs
children: RTEText[]
}
export type RTEObject =
| AssetReferenceObject
| {
attrs: Record<string, any>
children: (RTEObject | RTEText)[]
type: RTEType
uid: string
}
export type RTERootObject = {
attrs: RTEAssetAttrs | Record<string, any>
children: RTEObject[]
uid: string
type: "doc"
_version: number
}