Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
4eb192b
choose you vehicle component
kulsumansari-cstk Mar 30, 2026
9c6a1af
Add Hero Banner Component
chiragchavan06 Mar 31, 2026
b285e9d
Merge branch 'main' into feature/ui-components
chiragchavan06 Mar 31, 2026
acad03c
Add hero banner to homepage and prioritize video over image
chiragchavan06 Mar 31, 2026
914af13
added fonts, modified components
kulsumansari-cstk Apr 1, 2026
236156e
Add Text-And-Image and Card Component
chiragchavan06 Apr 1, 2026
d4719ad
Merge branch 'main' into feature/ui-components
chiragchavan06 Apr 1, 2026
2ba2721
Refine font styling
chiragchavan06 Apr 1, 2026
9795501
preview vehicle component, with getby model ref call
kulsumansari-cstk Apr 1, 2026
1c86d3d
Add Hero Banner Component
chiragchavan06 Mar 31, 2026
fd76e60
Add hero banner to homepage and prioritize video over image
chiragchavan06 Mar 31, 2026
829c83b
Add Text-And-Image and Card Component
chiragchavan06 Apr 1, 2026
1b0e9f3
Refine font styling
chiragchavan06 Apr 1, 2026
5363cf7
rebased with main
kulsumansari-cstk Apr 1, 2026
4491dec
hero CTA ref added
kulsumansari-cstk Apr 1, 2026
f781ce9
Merge branch 'main' into feature/ui-components
chiragchavan06 Apr 2, 2026
a94d221
Add navbar and buying tools component and fix link issue in cards and…
chiragchavan06 Apr 5, 2026
e712531
Add editableTags to navbar and buying tools; fix hero dots and card a…
chiragchavan06 Apr 6, 2026
28ed323
fix bgColor in buying tools
chiragchavan06 Apr 6, 2026
cadd517
Add text color to text and image component
chiragchavan06 Apr 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,5 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts

.vscode
257 changes: 233 additions & 24 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"@contentstack/live-preview-utils": "^4.2.1",
"@contentstack/personalize-edge-sdk": "^1.0.16",
"@contentstack/utils": "^1.4.4",
"@headlessui/react": "^2.2.9",
"@heroicons/react": "^2.2.0",
"contentstack": "^3.26.2",
"next": "15.5.4",
"next-intl": "^4.3.9",
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added public/fonts/rift/a
Binary file not shown.
Binary file not shown.
Binary file added public/fonts/rift/d.woff
Binary file not shown.
Binary file added public/fonts/rift/l.woff2
Binary file not shown.
47 changes: 47 additions & 0 deletions src/app/[locale]/[...slug]/layout.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@

import { cache } from "react";
import { headers } from "next/headers";
import ContentstackServer from "@/lib/cstack";
import DataContextProvider from "@/context/data.context";

const fetchData = cache(async (locale) => {
const headersList = await headers();
const variantParam = headersList.get('x-personalize-variants');
// example of how to fetch seo metadata from contentstack, replace "homepage" with the content type which contains the seo metadata
const data = await ContentstackServer.getElementByType("landing_pages", locale, {}, variantParam);
return data;
});

export const generateMetadata = async ({ params }) => {
const { locale } = await params;
const data = await fetchData(locale);
const entry = data?.[0];

return {
title: entry?.seo?.title,
description: entry?.seo?.description,
robots: {
index: entry?.seo?.no_index || false,
follow: entry?.seo?.no_follow || false,
},
openGraph: {
title: entry?.seo?.og_meta_tags?.title,
description: entry?.seo?.og_meta_tags?.description,
images: entry?.seo?.og_meta_tags?.image,
},
}
};

export default async function RootLayout({
children,
params,
}) {
const { locale } = await params;
const data = await fetchData(locale);

return (
<DataContextProvider data={data}>
{children}
</DataContextProvider>
);
}
96 changes: 96 additions & 0 deletions src/app/[locale]/[...slug]/page.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
"use client";
import { useDataContext } from "@/context/data.context";
import { ContentstackClient } from "@/lib/contentstack-client";
import UnlockAdventureSection from "@/components/UnlockAdventureSection";
import PreviewVehicle from "@/components/PreviewVehicle";
import HeroBanner from "@/components/HeroBanner";
import { useState, useEffect, use } from "react";
import TextAndImage from "@/components/TextAndImage";
import CardsCollection from "@/components/CardsCollection";
import BuyingTools from "@/components/BuyingTools";
import Navbar from "@/components/Navbar";

export default function Home({ params }) {
const { locale } = use(params);
const initialData = useDataContext();
const pageUrl = use(params).slug?.length > 0 ? "/"+use(params).slug.join('/') : null;

const [entry, setEntry] = useState(null);

const getContent = async () => {
const data = await ContentstackClient.getElementByUrlWithRefs(
"landing_pages",
pageUrl, locale,
[
'hero_banner',
'hero_banner.cta.internal_link',
'modular_blocks.unlock_adventure_section.reference',
'modular_blocks.unlock_adventure_section.reference.vehicles.internal_url',
'modular_blocks.preview_vehicle.vehicle_preview_reference',
'modular_blocks.preview_vehicle.vehicle_preview_reference.vehicle_models',
'modular_blocks.preview_vehicle.vehicle_preview_reference.link.internal_link',
'modular_blocks.buying_tools.reference',
'modular_blocks.buying_tools.reference.icon_list.internal_url',
],
// initialData
)

setEntry(data[0]);
};

useEffect(() => {
ContentstackClient.onEntryChange(() => {
getContent();
});
}, []);

return (
<div>
<div
data-pageref={entry?.uid}
data-contenttype="landing_pages"
data-locale={locale}
>
<Navbar />
<HeroBanner
content={entry?.hero_banner ?? []}
/>
<div
className={
entry?.modular_blocks?.length === 0
? "visual-builder__empty-block-parent"
: ""
}
{...entry?.$?.modular_blocks}
>
{entry?.modular_blocks.map((block, index) => (
<div key={index} {...entry?.$?.["modular_blocks__" + index]}>
{block.hasOwnProperty("unlock_adventure_section") && (
<UnlockAdventureSection key={index} content={block.unlock_adventure_section?.reference?.[0]} />
)}
{block.hasOwnProperty("preview_vehicle") && (
<PreviewVehicle key={index} content={block.preview_vehicle?.vehicle_preview_reference?.[0]} />
)}
{block.hasOwnProperty("text_and_image") && (
<TextAndImage
key={index}
content={block.text_and_image}
/>
)}
{block.hasOwnProperty("card_collection") && (
<CardsCollection
key={index}
content={block.card_collection}
/>
)}
{block.hasOwnProperty("buying_tools") && (
<BuyingTools key={index} content={block.buying_tools?.reference?.[0]} />
)}
</div>
))}
</div>
</div>
</div>
);
}

83 changes: 70 additions & 13 deletions src/app/[locale]/page.jsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,88 @@
"use client";
import { useDataContext } from "@/context/data.context";
import { ContentstackClient } from "@/lib/contentstack-client";
import HeroBanner from "@/components/HeroBanner";
import UnlockAdventureSection from "@/components/UnlockAdventureSection";
import { useState, useEffect, use } from "react";
import TextAndImage from "@/components/TextAndImage";
import CardsCollection from "@/components/CardsCollection";
import Navbar from "@/components/Navbar";
import BuyingTools from "@/components/BuyingTools";

export default function Home({ params }) {
const { locale } = use(params);
const initialData = useDataContext();

const [entry, setEntry] = useState(null);

useEffect(() => {
const fetchData = async () => {
// example of how to fetch data from contentstack, replace "homepage" with the content type you want to fetch
const data = await ContentstackClient.getElementByType("homepage", locale, initialData);
if(data) {
setEntry(data[0]);
} else {
setEntry(null);
}
}
const getContent = async () => {
const data = await ContentstackClient.getElementByTypeWithRefs(
"homepage",
locale,
[
'hero_carousel',
'hero_carousel.cta.internal_link',
'modular_blocks.unlock_adventure_section.reference',
'modular_blocks.unlock_adventure_section.reference.vehicles.internal_url',
'modular_blocks.buying_tools.reference',
'modular_blocks.buying_tools.reference.icon_list.internal_url',
],
// initialData
);
// console.log(data);

setEntry(data[0]);
};

ContentstackClient.onEntryChange(fetchData);
}, [locale, initialData]);
useEffect(() => {
ContentstackClient.onEntryChange(() => {
getContent();
});
}, []);

return (
<div>
<h1 {...entry?.$?.title}>{entry?.title}</h1>
<div
data-pageref={entry?.uid}
data-contenttype="homepage"
data-locale={locale}
>
<Navbar />
<HeroBanner
content={entry?.hero_carousel ?? []}
/>
<div
className={
entry?.modular_blocks?.length === 0
? "visual-builder__empty-block-parent"
: ""
}
{...entry?.$?.modular_blocks}
>
{entry?.modular_blocks.map((block, index) => (
<div key={index} {...entry?.$?.["modular_blocks__" + index]}>
{block.hasOwnProperty("unlock_adventure_section") && (
<UnlockAdventureSection key={index} content={block.unlock_adventure_section?.reference?.[0]} />
)}
{block.hasOwnProperty("text_and_image") && (
<TextAndImage
key={index}
content={block.text_and_image}
/>
)}
{block.hasOwnProperty("card_collection") && (
<CardsCollection
key={index}
content={block.card_collection}
/>
)}
{block.hasOwnProperty("buying_tools") && (
<BuyingTools key={index} content={block.buying_tools?.reference?.[0]} />
)}
</div>
))}
</div>
</div>
</div>
);
}
13 changes: 13 additions & 0 deletions src/app/api/contentstack/getElementByReference/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import ContentstackServer from "@/lib/cstack";

export async function POST(request) {
try {
const variantParam = request.headers.get('x-personalize-variants');
const { type, locale, referenceUids, live_preview } = await request.json();
const res = await ContentstackServer.getElementByReference(type, locale, referenceUids, live_preview, variantParam);
return Response.json(res || {});
} catch (error) {
console.error(error);
return Response.json({ error: "Failed to fetch data" }, { status: 500 });
}
}
Loading