diff --git a/.gitignore b/.gitignore
index a92e72d..7836fb1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,4 +36,11 @@ lib/
yalc.lock
.idea/
-dist/
\ No newline at end of file
+dist/
+/.env
+/package-lock.json
+
+certificates
+
+/.next
+/.env.sandbox
diff --git a/README.md b/README.md
index 0f633af..c745377 100644
--- a/README.md
+++ b/README.md
@@ -1,57 +1,83 @@
-# Adobe Universal Editor Sample App
+# Adobe Universal Editor Sample App (Next.js)
-## Using the Sample App
-The Sample App is hosted at https://ue-remote-app.adobe.net.
-Per Default the content is retrieved and written back to the Adobe Experience Manager as a Cloud Service ( Production ) Demo Environment:
+This is a sample Next.js application demonstrating how to integrate and use the Adobe Universal Editor with an AEM headless backend.
-The default settings from [.env](.env) can be overwritten using Query parameters:
-* `authorHost`: host to retrieve data from and update content to; default=https://author-p7452-e12433.adobeaemcloud.com
-* `service`: Universal Editor Service endpoint; default Universal Editor default
-* `protocol`: protocol to use with backend, can be `aem`, `aem65`, `aemcsLegacy`; default: `aem`
-* `cors`: defining which cors.js - connection between Universal Editor and application shall be used. Can be `stage` or empty; default `null/empty`. `stage` will use the cors library hosted on stage, else it will use the production version
+## Local Development with Universal Editor
-To retrieve content from another environment add `authorHost` as query parameters, e.g.
+To run this application locally and edit it using the Universal Editor, follow these steps:
-[https://ue-remote-app.adobe.net?authorHost=https://author-p7452-e12433.adobeaemcloud.com](https://ue-remote-app.adobe.net?authorHost=https://author-p7452-e12433.adobeaemcloud.com)
+### Prerequisites
-Similarly, if running the Universal Editor App on local dev environment, add `authorHost` as query parameters like this:
+1. **AEM Local Instance**: An AEM 6.5 or AEM as a Cloud Service (AEMCS) local SDK instance running locally.
+2. **HTTPS Configuration**: AEM must be configured to run on HTTPS (e.g., `https://localhost:8443`).
+3. **Content**: Ensure you have the latest WKND Site or the appropriate headless models/content installed on your local AEM instance.
+4. **Universal Editor CORS Proxy**: You must install the AEM Universal Editor CORS proxy package (bundle/jar) on your local AEM instance. You can download the latest Universal Editor local proxy package from the [Adobe Software Distribution portal](https://experience.adobe.com/#/downloads/content/software-distribution/en/aem.html). This is required to bypass CORS restrictions when the editor runs locally.
-[https://localhost:3000?authorHost=https://localhost:8443&service=https://localhost:8443/universal-editor](https://localhost:3000?authorHost=https://localhost:8443&service=https://localhost:8443/universal-editor)
+### Environment Configuration
-## Run locally
+For local development, the application uses the `.env.local` file. Ensure it contains the appropriate variables for your local setup. Example:
-- AEM 6.5 or AEMCS instance
-- Latest WKND Content installed on the AEM instance[https://github.com/adobe/aem-guides-wknd/releases/latest](https://github.com/adobe/aem-guides-wknd/releases/latest)
-- AEM configured to run on HTTPS [https://experienceleague.adobe.com/en/docs/experience-manager-learn/foundation/security/use-the-ssl-wizard](https://experienceleague.adobe.com/en/docs/experience-manager-learn/foundation/security/use-the-ssl-wizard)
-- `Adobe Granite Token Authentication Handler` configured to set `token.samesite.cookie.attr=Partitioned`
-- Remove `X-FRAME-Options=SAMEORIGIN` from `Apache Sling Main Servlet`'s `sling.additional.response.headers` attribute if run locally
-- Add policy for `https://localhost:3000` to `Adobe Granite Cross-Origin Resource Sharing Policy`. The default `adobe` configuraiton can be used as blueprint if run local copy of the app
-- Follow configuration on [https://github.com/maximilianvoss/universal-editor-service-proxy](https://github.com/maximilianvoss/universal-editor-service-proxy) for local development set up
-- Open Universal Editor either
- - under AEM domain for AEMCS, e.g. [https://author-p7452-e12433.adobeaemcloud.com/ui#/aem/universal-editor/canvas/](https://author-p7452-e12433.adobeaemcloud.com/ui#/aem/universal-editor/canvas/)
- - or on [https://experience.adobe.com/#/aem/editor/canvas/](https://experience.adobe.com/#/aem/editor/canvas/)
-- For experience.adobe.com use the `Local Developer Login` to authenticate against your local AEM instance when using a local SDK or AEM 6.5
+```env
+NEXT_PUBLIC_AEM_ACCESS_TOKEN="admin:admin"
+NEXT_PUBLIC_AEM_HOST="https://localhost:8443"
+NEXT_PUBLIC_UE_SERVICE="https://localhost:8000"
+NODE_TLS_REJECT_UNAUTHORIZED=0
+```
-## Available Scripts
+- `NEXT_PUBLIC_AEM_HOST`: Points to your local AEM author instance.
+- `NEXT_PUBLIC_AEM_ACCESS_TOKEN`: The credentials (e.g., Basic Auth or Bearer token) needed to fetch content from your local AEM instance.
+- `NEXT_PUBLIC_UE_SERVICE`: Points to the local Universal Editor service if you are running it locally.
-In the project directory, you can run:
+### Running the App Locally
+
+To start the Next.js development server specifically configured for a local AEM instance:
+
+```bash
+npm run dev:local
+```
+
+**Note:** The `dev:local` script automatically includes a local CA certificate (`NODE_EXTRA_CA_CERTS=certificates/localhost.pem`) and enables experimental HTTPS in Next.js (`--experimental-https`). This is required to resolve local SSL certificate errors when fetching data from AEM over HTTPS on localhost.
+
+The app will be available at [https://localhost:3000](https://localhost:3000).
-### `yarn start`
+### Local Universal Editor Service Proxy
-Runs the app in the development mode.\
-Open [https://localhost:3000](https://localhost:3000) to view it in your browser.
+If you are running the Universal Editor service proxy locally, you must create and trust a local certificate before starting the service:
-The page will reload when you make changes.\
-You may also see any lint errors in the console.
+1. **Generate a local certificate:**
+ ```bash
+ openssl req -x509 -newkey rsa:2048 -keyout key.pem -out certificate.pem -days 365 -nodes -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
+ ```
+2. **Trust the certificate on your system (macOS):**
+ ```bash
+ sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain certificate.pem
+ ```
+3. **Start the local Universal Editor service:**
+ ```bash
+ node universal-editor-service.cjs
+ ```
-### `yarn build`
+### Opening in Universal Editor
+
+1. Open the Universal Editor. Ensure your local Universal Editor service proxy is running (e.g., at `https://localhost:8000`).
+2. Point the Universal Editor to your local Next.js app URL: `https://localhost:3000`.
+3. You can now edit the Next.js application in context. The changes will be pushed back to your local AEM instance at `https://localhost:8443`.
+
+## Available Scripts
+
+In the project directory, you can run:
-Builds the app for production to the `dist` folder.
+### `npm run dev:local`
+Runs the app in development mode, tailored for localhost. It injects local certificates to bypass SSL connection errors when communicating with local AEM over HTTPS.
-### `yarn preview`
+### `npm run dev:sandbox`
+Runs the app using configurations defined in `.env.sandbox`. Useful when connecting to a remote sandbox AEM environment instead of localhost.
-Run the built app in production mode locally to verify the build.
+### `npm run build`
+Builds the Next.js app for production to the `.next` folder.
-### `yarn deploy`
+### `npm run start`
+Starts the built Next.js application in production mode.
-Build the application and push it to GitHub pages
\ No newline at end of file
+### `npm run deploy`
+Builds the application and deploys it to GitHub Pages.
\ No newline at end of file
diff --git a/index.html b/index.html
deleted file mode 100644
index a3b9a54..0000000
--- a/index.html
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
{
- data.articleList.items.map((article, index) => {
+ data?.articleList?.items?.map((article, index) => {
return (
);
diff --git a/src/components/Articles.scss b/src/components/Articles.scss
index 03d3eb9..93936bc 100644
--- a/src/components/Articles.scss
+++ b/src/components/Articles.scss
@@ -5,7 +5,7 @@ NOTICE: Adobe permits you to use, modify, and distribute this file in
accordance with the terms of the Adobe license agreement accompanying
it.
*/
-@import '../styles/variables';
+@use '../styles/variables' as *;
.articles {
padding: 70px;
diff --git a/src/components/Home.jsx b/src/components/Home.jsx
index 678c7c2..2e29746 100644
--- a/src/components/Home.jsx
+++ b/src/components/Home.jsx
@@ -6,7 +6,7 @@ accordance with the terms of the Adobe license agreement accompanying
it.
*/
import React from 'react';
-import { Link } from 'react-router-dom';
+import Link from 'next/link';
import { getQueryStringForHashRouting } from '../utils/commons';
import Container from './base/Container';
import Title from './base/Title';
@@ -34,7 +34,7 @@ function Home() {
-
+
diff --git a/src/components/Home.scss b/src/components/Home.scss
index 7d42d0d..d7b74e0 100644
--- a/src/components/Home.scss
+++ b/src/components/Home.scss
@@ -5,7 +5,7 @@ NOTICE: Adobe permits you to use, modify, and distribute this file in
accordance with the terms of the Adobe license agreement accompanying
it.
*/
-@import '../styles/variables';
+@use '../styles/variables' as *;
section {
padding: 80px;
diff --git a/src/components/Teaser.jsx b/src/components/Teaser.jsx
index b0effdc..16964b9 100644
--- a/src/components/Teaser.jsx
+++ b/src/components/Teaser.jsx
@@ -7,17 +7,18 @@ accordance with the terms of the Adobe license agreement accompanying
it.
*/
import React from 'react';
-import { Link } from 'react-router-dom';
-import useGraphQL from '../api/useGraphQL';
+import Link from 'next/link';
+import { fetchPersistedQuery } from '../api/graphqlServer';
import { getArticle, getQueryStringForHashRouting } from '../utils/commons';
import { mapJsonRichText } from '../utils/renderRichText';
import Loading from './base/Loading';
import "./Teaser.scss";
import {getImageURL} from "../utils/fetchData";
-const Teaser = () => {
+const Teaser = async () => {
const persistentQuery = `wknd-shared/article-by-slug;slug=aloha-spirits-in-northern-norway`;
- const {data, errorMessage} = useGraphQL(persistentQuery);
+ const {data, errors} = await fetchPersistedQuery(persistentQuery);
+ const errorMessage = errors ? errors.map(e => e.message || e).join(', ') : null;
//If there is an error with the GraphQL query
if (errorMessage) return;
@@ -42,7 +43,7 @@ const Teaser = () => {
Latest article
{title}
{main &&
{mapJsonRichText(main.json)}
}
-
+
diff --git a/src/components/Teaser.scss b/src/components/Teaser.scss
index 7a24f3b..981385c 100644
--- a/src/components/Teaser.scss
+++ b/src/components/Teaser.scss
@@ -5,7 +5,7 @@ NOTICE: Adobe permits you to use, modify, and distribute this file in
accordance with the terms of the Adobe license agreement accompanying
it.
*/
-@import '../styles/variables';
+@use '../styles/variables' as *;
.Teaser {
background-color: #141414;
diff --git a/src/components/base/Accordion.jsx b/src/components/base/Accordion.jsx
index 25d6be1..175a00e 100644
--- a/src/components/base/Accordion.jsx
+++ b/src/components/base/Accordion.jsx
@@ -1,3 +1,4 @@
+"use client";
import React from 'react';
import Container from './Container';
import './Accordion.scss';
diff --git a/src/components/base/BackButton.jsx b/src/components/base/BackButton.jsx
new file mode 100644
index 0000000..7777612
--- /dev/null
+++ b/src/components/base/BackButton.jsx
@@ -0,0 +1,13 @@
+"use client";
+import { useRouter } from 'next/navigation';
+import backIcon from '../../images/Back.svg';
+
+export default function BackButton({ label = "Back", className = "adventure-detail-back-nav dark" }) {
+ const router = useRouter();
+
+ return (
+
+ );
+}
diff --git a/src/components/base/Carousel.jsx b/src/components/base/Carousel.jsx
new file mode 100644
index 0000000..2ab809e
--- /dev/null
+++ b/src/components/base/Carousel.jsx
@@ -0,0 +1,198 @@
+"use client";
+import React from "react";
+import Container from "./Container";
+import "./Carousel.scss";
+
+const AUTOPLAY_INTERVAL_MS = 5000;
+
+const readBool = (value, fallback) => {
+ if (value === undefined || value === null || value === "") return fallback;
+ if (typeof value === "boolean") return value;
+ return value === "true" || value === true;
+};
+
+const CarouselSlide = (props) => {
+ const { resource, data, isActive, index, total, extraClassName = "" } = props;
+
+ const className = [
+ "carousel-slide",
+ isActive ? "is-active" : "",
+ extraClassName,
+ ]
+ .filter(Boolean)
+ .join(" ");
+
+ return (
+