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 ( -
-
- Next.js logo -
    -
  1. - Get started by editing{" "} - - app/page.tsx - - . -
  2. -
  3. Save and see your changes instantly.
  4. -
- -
- - Vercel logomark - Deploy now - - - Read our docs - -
-
- -
+
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) => ( +
+ + {teaser.image.alt} +

{teaser.title}

+
+
+ ))} +
+ ); + } \ 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