Skip to content

Commit 5c825af

Browse files
authored
Merge pull request #3 from contentstack/chore/security-fixes
chore: update dependencies and add postcss configuration
2 parents dd6d3c0 + a4aea7d commit 5c825af

File tree

8 files changed

+774
-91
lines changed

8 files changed

+774
-91
lines changed

app/favicon.ico

25.3 KB
Binary file not shown.

app/globals.css

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/* add Tailwind base styles, components, and utilities */
2+
@import "tailwindcss";
3+
4+
/*
5+
The default border color has changed to `currentColor` in Tailwind CSS v4,
6+
so we've added these compatibility styles to make sure everything still
7+
looks the same as it did with Tailwind CSS v3.
8+
9+
If we ever want to remove these styles, we need to add an explicit border
10+
color utility to any element that depends on these defaults.
11+
*/
12+
@layer base {
13+
*,
14+
::after,
15+
::before,
16+
::backdrop,
17+
::file-selector-button {
18+
border-color: var(--color-gray-200, currentColor);
19+
}
20+
21+
body {
22+
background: #e9e9e9;
23+
}
24+
25+
a {
26+
text-decoration: underline;
27+
}
28+
}

app/layout.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import type { Metadata } from "next";
2+
import "./globals.css";
3+
import { ContentstackLivePreview } from "@/components/ContentstackLivePreview";
4+
5+
export const metadata: Metadata = {
6+
title: "Create Next App",
7+
description: "Generated by create next app",
8+
};
9+
10+
export default function RootLayout({
11+
children,
12+
}: Readonly<{
13+
children: React.ReactNode;
14+
}>) {
15+
return (
16+
<html lang="en">
17+
<body>
18+
{children}
19+
<ContentstackLivePreview />
20+
</body>
21+
</html>
22+
);
23+
}

app/page.tsx

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import { JSDOM } from "jsdom";
2+
import createDOMPurify from "dompurify";
3+
4+
import { getPage, stack } from "@/lib/contentstack";
5+
import { headers } from "next/headers";
6+
import Image from "next/image";
7+
8+
export default async function Home({
9+
searchParams,
10+
}: {
11+
searchParams: Promise<any>;
12+
}) {
13+
await headers();
14+
const { live_preview, entry_uid, content_type_uid } = await searchParams;
15+
16+
if (live_preview) {
17+
stack.livePreviewQuery({
18+
live_preview,
19+
contentTypeUid: content_type_uid || "",
20+
entryUid: entry_uid || "",
21+
});
22+
}
23+
24+
const page = await getPage("/");
25+
26+
const { window } = new JSDOM("");
27+
const DOMPurify = createDOMPurify(window);
28+
29+
return (
30+
<main className="max-w-(--breakpoint-md) mx-auto">
31+
<section className="p-4">
32+
{live_preview ? (
33+
<ul className="mb-8 text-sm">
34+
<li>
35+
live_preview_hash: <code>{live_preview}</code>
36+
</li>
37+
<li>
38+
content_type_uid: <code>{content_type_uid}</code>
39+
</li>
40+
<li>
41+
entry_uid: <code>{entry_uid}</code>
42+
</li>
43+
</ul>
44+
) : null}
45+
46+
{page?.title ? (
47+
<h1
48+
className="text-4xl font-bold mb-4"
49+
{...(page?.$ && page?.$.title)}
50+
>
51+
{page?.title}
52+
</h1>
53+
) : null}
54+
55+
{page?.description ? (
56+
<p className="mb-4" {...(page?.$ && page?.$.description)}>
57+
{page?.description}
58+
</p>
59+
) : null}
60+
61+
{page?.image ? (
62+
<Image
63+
className="mb-4"
64+
width={768}
65+
height={414}
66+
src={page?.image.url}
67+
alt={page?.image.title}
68+
{...(page?.image?.$ && page?.image?.$.url)}
69+
/>
70+
) : null}
71+
72+
{page?.rich_text ? (
73+
<div
74+
{...(page?.$ && page?.$.rich_text)}
75+
dangerouslySetInnerHTML={{
76+
__html: DOMPurify.sanitize(page?.rich_text),
77+
}}
78+
/>
79+
) : null}
80+
81+
<div className="space-y-8 max-w-full mt-4">
82+
{page?.blocks?.map((item, index) => {
83+
const { block } = item;
84+
const isImageLeft = block.layout === "image_left";
85+
86+
return (
87+
<div
88+
key={block._metadata.uid}
89+
{...(page?.$ && page?.$[`blocks__${index}`])}
90+
className={`flex flex-col md:flex-row items-center space-y-4 md:space-y-0 bg-slate-100 ${
91+
isImageLeft ? "md:flex-row" : "md:flex-row-reverse"
92+
}`}
93+
>
94+
<div className="w-full md:w-1/2">
95+
{block.image ? (
96+
<Image
97+
src={block.image.url}
98+
alt={block.image.title}
99+
width={200}
100+
height={112}
101+
className="w-full"
102+
{...(block?.$ && block?.$.image)}
103+
/>
104+
) : null}
105+
</div>
106+
<div className="w-full md:w-1/2 p-4">
107+
{block.title ? (
108+
<h2
109+
className="text-2xl font-bold"
110+
{...(block?.$ && block?.$.title)}
111+
>
112+
{block.title}
113+
</h2>
114+
) : null}
115+
{block.copy ? (
116+
<div
117+
{...(block?.$ && block?.$.copy)}
118+
dangerouslySetInnerHTML={{
119+
__html: DOMPurify.sanitize(block.copy),
120+
}}
121+
className="prose"
122+
/>
123+
) : null}
124+
</div>
125+
</div>
126+
);
127+
})}
128+
</div>
129+
</section>
130+
</main>
131+
);
132+
}

next.config.mjs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/** @type {import('next').NextConfig} */
2+
const nextConfig = {
3+
images: {
4+
remotePatterns: [
5+
{
6+
hostname: "eu-images.contentstack.com",
7+
},
8+
],
9+
},
10+
};
11+
12+
export default nextConfig;

0 commit comments

Comments
 (0)