diff --git a/.gitignore b/.gitignore
index fd3dbb5..8585166 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,3 +34,4 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
+.env
diff --git a/app/page.tsx b/app/page.tsx
index 433c8aa..fe722b3 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,101 +1,5 @@
-import Image from "next/image";
-
-export default function Home() {
+export default async function Home() {
return (
-
-
-
-
- -
- Get started by editing{" "}
-
- app/page.tsx
-
- .
-
- - Save and see your changes instantly.
-
-
-
-
-
-
+ Home
);
}
diff --git a/components/AliasRedirect.tsx b/components/AliasRedirect.tsx
new file mode 100644
index 0000000..439bcf4
--- /dev/null
+++ b/components/AliasRedirect.tsx
@@ -0,0 +1,18 @@
+import { useEffect } from "react";
+import { useRouter } from "next/router";
+
+interface AliasRedirectProps {
+ redirectUrl: string | null
+}
+
+export const AliasRedirect = ({ redirectUrl }: AliasRedirectProps) => {
+ const router = useRouter()
+
+ useEffect(() => {
+ if (redirectUrl) {
+ router.push(redirectUrl)
+ }
+ }, [redirectUrl])
+
+ return null
+}
\ No newline at end of file
diff --git a/components/Breadcrumbs.jsx b/components/Breadcrumbs.jsx
new file mode 100644
index 0000000..90f9d90
--- /dev/null
+++ b/components/Breadcrumbs.jsx
@@ -0,0 +1,24 @@
+import Link from "next/link";
+
+export default function Breadcrumbs({ breadcrumbs }) {
+ if (!breadcrumbs || breadcrumbs.length === 0) {
+ return null
+ }
+
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/components/DynamicComponent.jsx b/components/DynamicComponent.jsx
new file mode 100644
index 0000000..77b07cb
--- /dev/null
+++ b/components/DynamicComponent.jsx
@@ -0,0 +1,18 @@
+import dynamic from "next/dynamic";
+
+const components = {
+ node: dynamic(() => import('./Node')),
+ 'node-article-full': dynamic(() => import('./NodeArticleFull')),
+ 'layout-section': dynamic(() => import('./LayoutSection')),
+ 'teaser-list': dynamic(() => import('./TeaserList')),
+};
+
+export default function DynamicComponent({ element, content }) {
+ const ComponentToRender = components[element];
+
+ if (!ComponentToRender) {
+ return Component "{element}" not found
;
+ }
+
+ return ;
+ }
\ No newline at end of file
diff --git a/components/LayoutSection.jsx b/components/LayoutSection.jsx
new file mode 100644
index 0000000..1af3430
--- /dev/null
+++ b/components/LayoutSection.jsx
@@ -0,0 +1,12 @@
+import DynamicComponent from "./DynamicComponent";
+
+export default function LayoutSection({ content }) {
+ return (
+
+
{content.settings.label || 'Section'}
+ {content.content.map((childElement, index) => (
+
+ ))}
+
+ );
+ }
\ No newline at end of file
diff --git a/components/Node.jsx b/components/Node.jsx
new file mode 100644
index 0000000..5ad42e3
--- /dev/null
+++ b/components/Node.jsx
@@ -0,0 +1,11 @@
+import DynamicComponent from "./DynamicComponent";
+
+export default function Node({ content }) {
+ return (
+
+ {content.sections.map((section, index) => (
+
+ ))}
+
+ );
+ }
\ No newline at end of file
diff --git a/components/NodeArticleFull.jsx b/components/NodeArticleFull.jsx
new file mode 100644
index 0000000..b974529
--- /dev/null
+++ b/components/NodeArticleFull.jsx
@@ -0,0 +1,9 @@
+export default function NodeArticleFull({ content }) {
+ const { title, uid} = content
+ return (
+
+
Title: {title}
+
node id: {uid}
+
+ )
+}
\ No newline at end of file
diff --git a/components/NodePage.tsx b/components/NodePage.tsx
new file mode 100644
index 0000000..41c802c
--- /dev/null
+++ b/components/NodePage.tsx
@@ -0,0 +1,24 @@
+import Breadcrumbs from "./Breadcrumbs";
+import DynamicComponent from "./DynamicComponent";
+
+interface NodePageProps {
+ nodeData: {
+ breadcrumbs: [],
+ title: string,
+ content: {
+ element: string
+ }
+ }
+}
+
+export const NodePage = ({ nodeData }: NodePageProps) => {
+ if (!nodeData) return No data found
+
+ return (
+
+
+
{nodeData.title}
+
+
+ )
+}
\ No newline at end of file
diff --git a/components/TeaserList.jsx b/components/TeaserList.jsx
new file mode 100644
index 0000000..8861114
--- /dev/null
+++ b/components/TeaserList.jsx
@@ -0,0 +1,14 @@
+export default function TeaserList({ content }) {
+ return (
+
+ {content.teasers.map((teaser) => (
+
+ ))}
+
+ );
+ }
\ No newline at end of file
diff --git a/components/index.js b/components/index.js
new file mode 100644
index 0000000..6b5628c
--- /dev/null
+++ b/components/index.js
@@ -0,0 +1,7 @@
+import dynamic from "next/dynamic";
+
+const components = {
+ 'node-article-full': dynamic(() => import('./NodeArticleFull')),
+};
+
+export default components;
diff --git a/hooks/useDrupal.js b/hooks/useDrupal.js
new file mode 100644
index 0000000..bbd4c62
--- /dev/null
+++ b/hooks/useDrupal.js
@@ -0,0 +1,2 @@
+import { useState, useEffect } from "react"
+import { fetchPage, fetchMenu } from "../lib/drupalClient"
\ No newline at end of file
diff --git a/lib/drupalClient.ts b/lib/drupalClient.ts
new file mode 100644
index 0000000..090b457
--- /dev/null
+++ b/lib/drupalClient.ts
@@ -0,0 +1,43 @@
+import axios from "axios"
+
+const drupalBaseUrl = process.env.NEXT_PUBLIC_DRUPAL_BASE_URL;
+const ceApiEndpoint = '/ce-api'
+import { mockData, mockData2, redirectMockData } from "../mockData"
+
+const drupalClient = axios.create({
+ baseURL: `${drupalBaseUrl}${ceApiEndpoint}`,
+ withCredentials: true
+});
+
+export async function fetchMenu() {
+ try {
+ const response = await drupalClient.get('/api/menu_items/main');
+ return response.data;
+ } catch (error) {
+ console.error('Failed to fetch menu:', error);
+ throw error;
+ }
+}
+
+export async function fetchPage(nodeId: string) {
+ try {
+ const response = await drupalClient.get(`node/${nodeId}`);
+ // const response = mockData
+ return response.data;
+ // return response
+ } catch (error) {
+ console.error('Failed to fetch page:', error);
+ throw error;
+ }
+}
+
+export async function fetchAlias(alias: string) {
+ try {
+ const response = await drupalClient.get(`${drupalBaseUrl}/${alias}`)
+ // const response = redirectMockData
+ return response.data
+ } catch(error) {
+ console.error("Failed to fetch alias:", error)
+ throw error
+ }
+}
\ No newline at end of file
diff --git a/mockData.js b/mockData.js
new file mode 100644
index 0000000..67ae901
--- /dev/null
+++ b/mockData.js
@@ -0,0 +1,261 @@
+export const mockData2 = {
+ title:"LDP Example",
+ messages:[
+
+ ],
+ breadcrumbs:[
+
+ ],
+ metatags:{
+ meta:[
+ {
+ name:"title",
+ content:"LDP Example | Drush Site-Install"
+ },
+ {
+ name:"description",
+ content:"lupus.digital Publishing"
+ },
+ {
+ property:"og:site_name",
+ content:"Drush Site-Install"
+ },
+ {
+ property:"og:url",
+ content:"https:\/\/example_lupus-nuxt3-kickstart--1x.ci2.drunomics.com"
+ },
+ {
+ property:"og:title",
+ content:"LDP Example"
+ },
+ {
+ property:"og:description",
+ content:"lupus.digital Publishing"
+ }
+ ],
+ link:[
+ {
+ rel:"canonical",
+ href:"https:\/\/example_lupus-nuxt3-kickstart--1x.ci2.drunomics.com"
+ }
+ ]
+ },
+ content_format:"json",
+ content:{
+ element:"node",
+ type:"channel_page",
+ sections:[
+ {
+ element:"layout-section",
+ layout:"layout_onecol",
+ settings:{
+ label:"",
+ width:"regular"
+ },
+ content:[
+ {
+ element:"teaser-list",
+ contains:"teaser-square",
+ loadEndpoint:"\/lupus\/block\/ldp_channel_content_listing\/items?type=channel_page\u0026view_mode=teaser_square\u0026sort=1\u0026\u0026obey_topic_secondary=\u0026channel%5B0%5D=1\u0026channel_filter_depth=1\u0026rendered=33",
+ loadNumItems:"15",
+ initialNumItems:3,
+ moreType:"none",
+ buttonText:"Load more",
+ isEmpty:false,
+ type:"channel_page",
+ teasers:[
+ {
+ element:"teaser-square",
+ type:"channel_page",
+ href:"\/science",
+ title:"Channel: Science",
+ image:{
+ aspectRatio:100,
+ src:"https:\/\/admin--example_lupus-nuxt3-kickstart--1x.ci2.drunomics.com\/sites\/example\/files\/styles\/360_360\/public\/2024-10\/channel_science.jpg?itok=UrBsIWHp",
+ srcset:null,
+ alt:"Alt Placeholder",
+ title:"\u00a9 By NASA\/WMAP Science Team - Original version: NASA; modified by Cherkash, Public domain, https:\/\/commons.wikimedia.org\/wiki\/File:CMB_Timeline300_no_WMAP.jpg\nSource: https:\/\/upload.wikimedia.org\/wikipedia\/commons\/6\/6f\/CMB_Timeline300_no_WMAP.jpg"
+ },
+ publishedAt:"30. March 2021"
+ },
+ {
+ element:"teaser-square",
+ type:"channel_page",
+ href:"\/info",
+ title:"Channel: Info",
+ image:{
+ aspectRatio:100,
+ src:"https:\/\/admin--example_lupus-nuxt3-kickstart--1x.ci2.drunomics.com\/sites\/example\/files\/styles\/360_360\/public\/teaser_image_placeholder-39fb88d99f41a7f15bbee4c982ebbfdd.png?itok=GW_wY-_9",
+ srcset:null,
+ alt:"Fallback image",
+ title:""
+ },
+ publishedAt:"30. March 2021"
+ },
+ {
+ element:"teaser-square",
+ type:"channel_page",
+ href:"\/sport",
+ title:"Channel: Sport",
+ image:{
+ aspectRatio:100,
+ src:"https:\/\/admin--example_lupus-nuxt3-kickstart--1x.ci2.drunomics.com\/sites\/example\/files\/styles\/360_360\/public\/2024-10\/topics_sports.jpg?itok=7whOtFhH",
+ srcset:null,
+ alt:"Alt Placeholder",
+ title:"\u00a9 By Derek Jensen (Tysto), 2005-September-17, Public domain, https:\/\/commons.wikimedia.org\/wiki\/File:Youth-soccer-indiana.jpg\nSource: https:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/9\/92\/Youth-soccer-indiana.jpg\/1920px-Youth-soccer-indiana.jpg"
+ },
+ publishedAt:"30. March 2021"
+ }
+ ]
+ },
+ {
+ element:"teaser-list",
+ contains:"teaser-wide",
+ loadEndpoint:"\/lupus\/block\/ldp_channel_article_listing\/items?type=article\u0026view_mode=teaser_wide\u0026sort=1\u0026\u0026obey_topic_secondary=\u0026channel%5B0%5D=1\u0026channel_filter_depth=3\u0026rendered=33%2C34%2C35%2C36%2C15%2C9%2C26%2C25%2C24",
+ loadNumItems:"5",
+ initialNumItems:5,
+ moreType:"load-in",
+ buttonText:"Load more",
+ isEmpty:false,
+ teasers:[
+ {
+ element:"teaser-wide",
+ type:"article",
+ href:"\/info\/drunomics-gewinnt-zwei-splash-awards-2019-fuer-die-besten-oesterreichischen-websites-15",
+ title:"drunomics gewinnt zwei Splash Awards 2019 in den Kategorien Medien und Bildung",
+ category:"News",
+ excerpt:"Am 26. April 2019 wurden die Splash Awards zum dritten Mal in Deutschland vergeben. Das Team von drunomics wurde mit den Drupal-Projekten einfachbacken.de und TSNweb f\u00fcr die besten \u00f6sterreichischen Websites in zwei Kategorien ausgezeichnet.",
+ image:{
+ aspectRatio:133.33000000000001,
+ src:"https:\/\/admin--example_lupus-nuxt3-kickstart--1x.ci2.drunomics.com\/sites\/example\/files\/styles\/300_225\/public\/2024-10\/groupphoto.jpg?itok=Rjg27LYB",
+ srcset:null,
+ alt:"Alt Placeholder",
+ title:"\u00a9 Burda Magazine Holding GmbH\nSource: https:\/\/www.ots.at\/anhang\/2019\/04\/28\/OBS\/OBS_20190428_OBS0002.layout.jpg"
+ },
+ publishedAt:"11. January 2027"
+ },
+ {
+ element:"teaser-wide",
+ type:"article",
+ href:"\/science\/instagram-demo-paragraph-9",
+ title:"Demo article of instagram paragraph",
+ category:"Technology",
+ excerpt:"Article with instagram demo paragraph. Note: Keyword CBLqNvVRLpQloUf is here for automated testing of search results.",
+ image:{
+ aspectRatio:133.33000000000001,
+ src:"https:\/\/admin--example_lupus-nuxt3-kickstart--1x.ci2.drunomics.com\/sites\/example\/files\/styles\/300_225\/public\/2024-10\/d8rules_funded_0.png?itok=wWeyzUGx",
+ srcset:null,
+ alt:"Alt Placeholder",
+ title:"\u00a9 drunomics Gmbh\nSource: https:\/\/drunomics.com\/files\/d8rules_funded_0.png"
+ },
+ publishedAt:"11. January 2027"
+ },
+ {
+ element:"teaser-wide",
+ type:"article",
+ href:"\/sport\/demo-article-fallback-image-test-26",
+ title:"Demo article for fallback image test",
+ category:"Sports",
+ excerpt:"This article was made to help test the functionality of fallback image.",
+ image:{
+ aspectRatio:133.33000000000001,
+ src:"https:\/\/admin--example_lupus-nuxt3-kickstart--1x.ci2.drunomics.com\/sites\/example\/files\/styles\/300_225\/public\/teaser_image_placeholder-39fb88d99f41a7f15bbee4c982ebbfdd.png?itok=TauaIera",
+ srcset:null,
+ alt:"Fallback image",
+ title:""
+ },
+ publishedAt:"01. April 2021"
+ },
+ {
+ element:"teaser-wide",
+ type:"article",
+ href:"\/paragraph-showcase",
+ title:"Paragraphs showcase",
+ category:"News",
+ excerpt:"This article was made to help present usage of all paragraph types. Note: Keyword CBLqNvVRLpQloUf is here for automated testing of search results.",
+ image:{
+ aspectRatio:133.33000000000001,
+ src:"https:\/\/admin--example_lupus-nuxt3-kickstart--1x.ci2.drunomics.com\/sites\/example\/files\/styles\/300_225\/public\/2024-10\/new_logo.jpg?itok=fkX7Buim",
+ srcset:null,
+ alt:"Alt Placeholder",
+ title:"\u00a9 drupal.org\nSource: https:\/\/www.drupal.org\/files\/Wordmark2_blue_RGB%281%29.png"
+ },
+ publishedAt:"31. March 2021"
+ },
+ {
+ element:"teaser-wide",
+ type:"article",
+ href:"\/social-media-showcase",
+ title:"Social-media paragraphs test",
+ category:"News",
+ excerpt:"This article was made to help present usage of all paragraph types. Note: Keyword CBLqNvVRLpQloUf is here for automated testing of search results.",
+ image:{
+ aspectRatio:133.33000000000001,
+ src:"https:\/\/admin--example_lupus-nuxt3-kickstart--1x.ci2.drunomics.com\/sites\/example\/files\/styles\/300_225\/public\/2024-10\/new_logo.jpg?itok=fkX7Buim",
+ srcset:null,
+ alt:"Alt Placeholder",
+ title:"\u00a9 drupal.org\nSource: https:\/\/www.drupal.org\/files\/Wordmark2_blue_RGB%281%29.png"
+ },
+ publishedAt:"31. March 2021"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ page_layout:"full",
+ settings:{
+ "display_title":"1"
+ },
+ local_tasks:[
+
+ ]
+ }
+
+ export const mockData = {
+ title: "first article",
+ messages: [],
+ breadcrumbs: [
+ {
+ "frontpage": true,
+ "url": "/",
+ "label": "Home"
+ }
+ ],
+ metatags: {
+ meta: [
+ {
+ "name": "title",
+ "content": "first article | lupus decoupled"
+ }
+ ],
+ link: [
+ {
+ "rel": "canonical",
+ "href": "https://3000-drunomics-lupusdecouple-xeqrf6qqxj3.ws-eu116.gitpod.io/demo-first"
+ }
+ ]
+ },
+ content_format: "json",
+ content: {
+ element: "node-article-full",
+ uid: "1",
+ title: "first article",
+ created: "1728894039"
+ },
+ page_layout: "default",
+ local_tasks: []
+}
+
+export const redirectMockData = {
+ redirect: {
+ external: true,
+ url: "http://localhost:3000/node/1",
+ statusCode: 302
+ },
+ messages: [],
+ settings: {
+ display_title: 0
+ }
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index dcd1073..a849704 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,7 @@
"name": "lupus-decoupled-nextjs-demo",
"version": "0.1.0",
"dependencies": {
+ "axios": "^1.7.7",
"next": "14.2.15",
"react": "^18",
"react-dom": "^18"
@@ -479,9 +480,9 @@
}
},
"node_modules/@types/react-dom": {
- "version": "18.3.0",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz",
- "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==",
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz",
+ "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==",
"dev": true,
"dependencies": {
"@types/react": "*"
@@ -956,6 +957,11 @@
"integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==",
"dev": true
},
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
"node_modules/available-typed-arrays": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
@@ -980,6 +986,16 @@
"node": ">=4"
}
},
+ "node_modules/axios": {
+ "version": "1.7.7",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
+ "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
"node_modules/axobject-query": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
@@ -1078,9 +1094,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001667",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001667.tgz",
- "integrity": "sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==",
+ "version": "1.0.30001668",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001668.tgz",
+ "integrity": "sha512-nWLrdxqCdblixUO+27JtGJJE/txpJlyUy5YN1u53wLZkP0emYCo5zgS6QYft7VUYR42LGgi/S5hdLZTrnyIddw==",
"funding": [
{
"type": "opencollective",
@@ -1171,6 +1187,17 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
@@ -1364,6 +1391,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/didyoumean": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
@@ -1893,9 +1928,9 @@
}
},
"node_modules/eslint-plugin-react-hooks": {
- "version": "4.6.2",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz",
- "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==",
+ "version": "5.0.0-canary-7118f5dd7-20230705",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0-canary-7118f5dd7-20230705.tgz",
+ "integrity": "sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw==",
"dev": true,
"engines": {
"node": ">=10"
@@ -2144,6 +2179,25 @@
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
"dev": true
},
+ "node_modules/follow-redirects": {
+ "version": "1.15.9",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
+ "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
"node_modules/for-each": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
@@ -2169,6 +2223,19 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/form-data": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
+ "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -3158,6 +3225,25 @@
"node": ">=8.6"
}
},
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -3785,6 +3871,11 @@
"react-is": "^16.13.1"
}
},
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -4918,9 +5009,9 @@
"dev": true
},
"node_modules/yaml": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz",
- "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==",
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz",
+ "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==",
"dev": true,
"bin": {
"yaml": "bin.mjs"
diff --git a/package.json b/package.json
index 93ea74b..95b770c 100644
--- a/package.json
+++ b/package.json
@@ -9,18 +9,19 @@
"lint": "next lint"
},
"dependencies": {
+ "axios": "^1.7.7",
+ "next": "14.2.15",
"react": "^18",
- "react-dom": "^18",
- "next": "14.2.15"
+ "react-dom": "^18"
},
"devDependencies": {
- "typescript": "^5",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
+ "eslint": "^8",
+ "eslint-config-next": "14.2.15",
"postcss": "^8",
"tailwindcss": "^3.4.1",
- "eslint": "^8",
- "eslint-config-next": "14.2.15"
+ "typescript": "^5"
}
}
diff --git a/pages/[...alias].js b/pages/[...alias].js
new file mode 100644
index 0000000..a644834
--- /dev/null
+++ b/pages/[...alias].js
@@ -0,0 +1,6 @@
+import { getAliasRedirectProps } from "../ssrHelpers";
+import { AliasRedirect } from "../components/AliasRedirect"
+
+export default AliasRedirect;
+
+export const getServerSideProps = getAliasRedirectProps
\ No newline at end of file
diff --git a/pages/node/[id].js b/pages/node/[id].js
new file mode 100644
index 0000000..53b6bc1
--- /dev/null
+++ b/pages/node/[id].js
@@ -0,0 +1,6 @@
+import { getNodePageProps } from "../../ssrHelpers"
+import { NodePage } from "../../components/NodePage"
+
+export default NodePage
+
+export const getServerSideProps = getNodePageProps
\ No newline at end of file
diff --git a/ssrHelpers.ts b/ssrHelpers.ts
new file mode 100644
index 0000000..e998996
--- /dev/null
+++ b/ssrHelpers.ts
@@ -0,0 +1,48 @@
+import { fetchPage, fetchAlias } from "./lib/drupalClient";
+
+export async function getNodePageProps(context: any) {
+ const { id } = context.params
+
+ try {
+ const nodeData = await fetchPage(id)
+ return {
+ props: {
+ nodeData
+ }
+ }
+ } catch (error) {
+ return {
+ props: {
+ error: (error as Error).message
+ }
+ }
+ }
+}
+
+export async function getAliasRedirectProps(context: any) {
+ const { alias } = context.params
+
+ try {
+ const response = await fetchAlias(alias.join("/"))
+
+ if (response.redirect) {
+ return {
+ redirect: {
+ destination: response.redirect.url,
+ permanent: false
+ }
+ }
+ }
+
+ return {
+ notFound: true
+ }
+
+ } catch(error) {
+ return {
+ props: {
+ error: (error as Error).message
+ }
+ }
+ }
+}
\ No newline at end of file