diff --git a/app/[locale]/[[...slug]]/category-page-preview.tsx b/app/[locale]/[[...slug]]/category-page-preview.tsx
new file mode 100644
index 000000000..d2e8abea3
--- /dev/null
+++ b/app/[locale]/[[...slug]]/category-page-preview.tsx
@@ -0,0 +1,26 @@
+'use client'
+
+import PreviewBanner from 'components/ui/preview-banner'
+import { usePreview } from 'lib/sanity/sanity.preview'
+import CategoryPage from './category-page'
+
+export default function CategoryPagePreview({
+ query,
+ queryParams,
+}: {
+ query: string
+ queryParams: {
+ [key: string]: any
+ }
+}) {
+ const data = usePreview(null, query, queryParams)
+
+ const { title, _type } = data
+
+ return (
+ <>
+
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/app/[locale]/[[...slug]]/home-page-preview.tsx b/app/[locale]/[[...slug]]/home-page-preview.tsx
new file mode 100644
index 000000000..567f24f60
--- /dev/null
+++ b/app/[locale]/[[...slug]]/home-page-preview.tsx
@@ -0,0 +1,26 @@
+'use client'
+
+import PreviewBanner from 'components/ui/preview-banner'
+import { usePreview } from 'lib/sanity/sanity.preview'
+import HomePage from './home-page'
+
+export default function HomePagePreview({
+ query,
+ queryParams,
+}: {
+ query: string
+ queryParams: {
+ [key: string]: any
+ }
+}) {
+ const data = usePreview(null, query, queryParams)
+
+ const { title, _type } = data
+
+ return (
+ <>
+
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/app/[locale]/[[...slug]]/page.tsx b/app/[locale]/[[...slug]]/page.tsx
index 9ddfece67..35dcf0f51 100644
--- a/app/[locale]/[[...slug]]/page.tsx
+++ b/app/[locale]/[[...slug]]/page.tsx
@@ -1,19 +1,73 @@
+import PreviewSuspense from 'components/preview-suspense';
import getQueryFromSlug from 'helpers/getQueryFromSlug';
import { docQuery } from 'lib/sanity/queries';
import { client } from 'lib/sanity/sanity.client';
import type { Metadata } from 'next';
+import { draftMode } from 'next/headers';
import CategoryPage from './category-page';
+import CategoryPagePreview from './category-page-preview';
import HomePage from './home-page';
+import HomePagePreview from './home-page-preview';
import ProductPage from './product-page';
+import ProductPagePreview from './product-page-preview';
import SinglePage from './single-page';
+import SinglePagePreview from './single-page-preview';
+
+/**
+ * Render pages depending on type.
+ */
+export default async function Page({
+ params,
+}: {
+ params: { slug: string[], locale: string };
+}) {
+ const { isEnabled } = draftMode();
+
+ const { slug, locale } = params;
+
+ const { query = '', queryParams, docType } = getQueryFromSlug(slug, locale)
+
+ const pageData = await client.fetch(query, queryParams)
+
+ const data = filterDataToSingleItem(pageData, isEnabled)
+
+ if (isEnabled) {
+ return (
+
+ {docType === 'home' && (
+
+ )}
+ {docType === 'page' && (
+
+ )}
+ {docType === 'product' && (
+
+ )}
+ {docType === 'category' && (
+
+ )}
+
+ )
+ }
+
+ return (
+ <>
+ {docType === 'home' && }
+ {docType === 'product' && }
+ {docType === 'category' && }
+ {docType === 'page' && }
+ >
+ )
+}
+
+// Background revalidate once every day.
+export const revalidate = 86400;
/**
* Get paths for each page.
*/
export async function generateStaticParams() {
- const paths = await client.fetch(docQuery, {
- next: { revalidate: 10 },
- })
+ const paths = await client.fetch(docQuery)
return paths.map((path: {
slug: string,
@@ -79,30 +133,4 @@ export async function generateMetadata({ params }: {params: { slug: string[], lo
],
},
}
-}
-
-/**
- * Render pages depending on type.
- */
-export default async function Page({
- params,
-}: {
- params: { slug: string[], locale: string };
-}) {
- const { slug, locale } = params;
-
- const { query = '', queryParams, docType } = getQueryFromSlug(slug, locale)
-
- const pageData = await client.fetch(query, queryParams)
-
- const data = filterDataToSingleItem(pageData, false)
-
- return (
- <>
- {docType === 'home' && }
- {docType === 'product' && }
- {docType === 'category' && }
- {docType === 'page' && }
- >
- )
}
\ No newline at end of file
diff --git a/app/[locale]/[[...slug]]/product-page-preview.tsx b/app/[locale]/[[...slug]]/product-page-preview.tsx
new file mode 100644
index 000000000..21ed044dd
--- /dev/null
+++ b/app/[locale]/[[...slug]]/product-page-preview.tsx
@@ -0,0 +1,26 @@
+'use client'
+
+import PreviewBanner from 'components/ui/preview-banner'
+import { usePreview } from 'lib/sanity/sanity.preview'
+import ProductPage from './product-page'
+
+export default function ProductPagePreview({
+ query,
+ queryParams,
+}: {
+ query: string
+ queryParams: {
+ [key: string]: any
+ }
+}) {
+ const data = usePreview(null, query, queryParams)
+
+ const { title, _type } = data
+
+ return (
+ <>
+
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/app/[locale]/[[...slug]]/single-page-preview.tsx b/app/[locale]/[[...slug]]/single-page-preview.tsx
new file mode 100644
index 000000000..c09e74193
--- /dev/null
+++ b/app/[locale]/[[...slug]]/single-page-preview.tsx
@@ -0,0 +1,26 @@
+'use client'
+
+import PreviewBanner from 'components/ui/preview-banner'
+import { usePreview } from 'lib/sanity/sanity.preview'
+import SinglePage from './single-page'
+
+export default function SinglePagePreview({
+ query,
+ queryParams,
+}: {
+ query: string
+ queryParams: {
+ [key: string]: any
+ }
+}) {
+ const data = usePreview(null, query, queryParams)
+
+ const { title, _type } = data
+
+ return (
+ <>
+
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/app/api/draft/route.ts b/app/api/draft/route.ts
new file mode 100644
index 000000000..fa394649f
--- /dev/null
+++ b/app/api/draft/route.ts
@@ -0,0 +1,99 @@
+// route handler enabling draft mode
+import { categoryQuery, homePageQuery, pageQuery, productQuery } from 'lib/sanity/queries';
+import { client } from 'lib/sanity/sanity.client';
+import { draftMode } from 'next/headers';
+
+const draftSecret = process.env.NEXT_PUBLIC_SANITY_DRAFT_TOKEN
+
+export async function GET(request: Request) {
+// Enable Draft Mode by setting the cookie
+draftMode().enable();
+ // Parse query string parameters
+ const { searchParams } = new URL(request.url);
+ const secret = searchParams.get('secret');
+ const slug = searchParams.get('slug');
+ const locale = searchParams.get('locale');
+ const type = searchParams.get('type');
+
+ // Make sure there's a valid draft token.
+ if (secret !== draftSecret) {
+ return new Response('Invalid token', { status: 401 });
+ }
+
+ // Make sure there's a slug provided.
+ if (!slug) {
+ return new Response('No slug provided', { status: 401 });
+ }
+
+ // Make sure there's a locale provided.
+ if (!locale) {
+ return new Response('No locale provided', { status: 401 });
+ }
+
+ // Make sure there's a type provided.
+ if (!type) {
+ return new Response('No type provided', { status: 401 });
+ }
+
+ // Types available for preview - Check if the post with the given `slug` exists
+ const home = await client.fetch(homePageQuery, {
+ slug: slug,
+ locale: locale,
+ })
+
+ const page = await client.fetch(pageQuery, {
+ slug: slug,
+ locale: locale,
+ })
+
+ const product = await client.fetch(productQuery, {
+ slug: slug,
+ locale: locale,
+ })
+
+ const category = await client.fetch(categoryQuery, {
+ slug: slug,
+ locale: locale,
+ })
+
+
+ draftMode().enable();
+
+ // Redirect to the path from the fetched post
+ // We don't redirect to req.query.slug as that might lead to open redirect vulnerabilities
+ if (home && type === 'home') {
+ return new Response(null, {
+ status: 307,
+ headers: {
+ Location: `/${home.locale}/${home.slug}`,
+ },
+ })
+ }
+
+ if (page && type === 'page') {
+ return new Response(null, {
+ status: 307,
+ headers: {
+ Location: `/${page.locale}/${page.slug}`,
+ },
+ })
+ }
+
+ if (product && type === 'product') {
+ return new Response(null, {
+ status: 307,
+ headers: {
+ Location: `/${product.locale}/${product.slug}`,
+ },
+ })
+ }
+
+ if (category && type === 'category') {
+ return new Response(null, {
+ status: 307,
+ headers: {
+ Location: `/${category.locale}/${category.slug}`,
+ },
+ })
+ }
+}
\ No newline at end of file
diff --git a/app/api/exit-draft/route.ts b/app/api/exit-draft/route.ts
new file mode 100644
index 000000000..6cb3d88f0
--- /dev/null
+++ b/app/api/exit-draft/route.ts
@@ -0,0 +1,12 @@
+import { draftMode } from 'next/headers';
+
+export async function GET() {
+ draftMode().disable();
+
+ return new Response(null, {
+ status: 307,
+ headers: {
+ Location: `/`,
+ },
+ })
+}
\ No newline at end of file
diff --git a/components/preview-suspense.tsx b/components/preview-suspense.tsx
new file mode 100644
index 000000000..d45e24877
--- /dev/null
+++ b/components/preview-suspense.tsx
@@ -0,0 +1,4 @@
+'use client'
+
+// Once rollup supports 'use client' module directives then 'next-sanity' will include them and this re-export will no longer be necessary
+export { PreviewSuspense as default } from 'next-sanity/preview'
diff --git a/components/ui/preview-banner/index.tsx b/components/ui/preview-banner/index.tsx
new file mode 100644
index 000000000..f1e59a4c1
--- /dev/null
+++ b/components/ui/preview-banner/index.tsx
@@ -0,0 +1 @@
+export { default } from './preview-banner';
diff --git a/components/ui/preview-banner/preview-banner.tsx b/components/ui/preview-banner/preview-banner.tsx
new file mode 100644
index 000000000..d7f8ffe16
--- /dev/null
+++ b/components/ui/preview-banner/preview-banner.tsx
@@ -0,0 +1,29 @@
+'use client'
+
+import { useTranslations } from 'next-intl'
+import Link from 'next/link'
+
+interface PreviewBannerProps {
+ title?: string
+}
+
+const PreviewBanner = ({ title }: PreviewBannerProps) => {
+ const t = useTranslations('ui.previewBanner')
+ return (
+
+ {title && (
+
+ {t('titlePart')} {title}
+
+ )}
+
+ {t('exitPreviewLabel')}
+
+
+ )
+}
+
+export default PreviewBanner
\ No newline at end of file