Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
42 changes: 20 additions & 22 deletions .env.local.sample
Original file line number Diff line number Diff line change
@@ -1,31 +1,29 @@

# This is a settings file for our application.

# Contentstack is the tool we use to manage our website's content.
# You need to replace 'your_stack_api_key', 'your_delivery_token', and 'your_environment_name' with the actual information.
CONTENTSTACK_API_KEY=your_stack_api_key
CONTENTSTACK_DELIVERY_TOKEN=your_delivery_token
CONTENTSTACK_ENVIRONMENT=your_environment_name
# Contentstack — use NEXT_PUBLIC_* only (available in browser + server via Next.js).
NEXT_PUBLIC_CONTENTSTACK_API_KEY=your_stack_api_key
NEXT_PUBLIC_CONTENTSTACK_DELIVERY_TOKEN=your_delivery_token
NEXT_PUBLIC_CONTENTSTACK_ENVIRONMENT=your_environment_name

# Live Preview lets us see changes before they are shown on the website.
# Replace 'your_live_preview_token' with the actual information.
CONTENTSTACK_PREVIEW_HOST=rest-preview.contentstack.com
CONTENTSTACK_PREVIEW_TOKEN=your_live_preview_token
CONTENTSTACK_APP_HOST=app.contentstack.com
CONTENTSTACK_LIVE_PREVIEW=true
CONTENTSTACK_LIVE_EDIT_TAGS=false
# Live Preview
NEXT_PUBLIC_CONTENTSTACK_PREVIEW_HOST=rest-preview.contentstack.com
NEXT_PUBLIC_CONTENTSTACK_PREVIEW_TOKEN=your_live_preview_token
NEXT_PUBLIC_CONTENTSTACK_APP_HOST=app.contentstack.com
NEXT_PUBLIC_CONTENTSTACK_LIVE_PREVIEW=true
NEXT_PUBLIC_CONTENTSTACK_LIVE_EDIT_TAGS=false

# These are extra settings. You can remove the '#' at the start of the line and fill these if needed.
# CONTENTSTACK_API_HOST= api.contentstack.io
# CONTENTSTACK_REGION=us
# CONTENTSTACK_BRANCH=main
# Optional (defaults in next.config.js if unset)
# NEXT_PUBLIC_CONTENTSTACK_API_HOST=api.contentstack.io
# NEXT_PUBLIC_CONTENTSTACK_REGION=us
# NEXT_PUBLIC_CONTENTSTACK_BRANCH=main

#site-map
# Site map
NEXT_PUBLIC_HOSTED_URL=http://localhost:3000

# Notes:
# - CONTENTSTACK_API_HOST: This is for setting a custom address for the Contentstack tool.
# - CONTENTSTACK_REGION: This is for setting a custom region for the Contentstack tool (default is 'us').
# - CONTENTSTACK_BRANCH: This is for setting a custom branch for the Contentstack tool (default is 'main').
# - CONTENTSTACK_PREVIEW_HOST: If you're in the EU just append "eu-" to "rest-preview.contentstack.com"
# - example eu-rest-preview.contentstack.com
# - NEXT_PUBLIC_CONTENTSTACK_API_HOST: custom Contentstack API host if needed.
# - NEXT_PUBLIC_CONTENTSTACK_REGION: default is us.
# - NEXT_PUBLIC_CONTENTSTACK_BRANCH: default is main.
# - NEXT_PUBLIC_CONTENTSTACK_PREVIEW_HOST: EU example — eu-rest-preview.contentstack.com
# - Delivery + preview tokens are exposed in the client bundle; use preview-scoped tokens only.
28 changes: 0 additions & 28 deletions .eslintrc.json

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ yarn-error.log*
.pnpm-debug.log*

# local env files
.env
.env*.local

# vercel
Expand Down
2 changes: 1 addition & 1 deletion .npmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
timeout=60000
fetch-timeout=60000
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2025 Contentstack
Copyright (c) 2026 Contentstack

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
82 changes: 41 additions & 41 deletions components/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,41 @@
import React, { useState, useEffect } from 'react';
import React, { useMemo } from 'react';
import Header from './header';
import Footer from './footer';
import DevTools from './devtools';
import { HeaderProps, FooterProps, PageProps, Posts, ChilderenProps, Entry, NavLinks, Links } from "../typescript/layout";

function buildNavigation(ent: Entry, hd: HeaderProps, ft: FooterProps) {
let newHeader = { ...hd };
let newFooter = { ...ft };
if (ent.length !== newHeader.navigation_menu.length) {
ent.forEach((entry) => {
const hFound = newHeader?.navigation_menu.find(
(navLink: NavLinks) => navLink.label === entry.title
);
if (!hFound) {
newHeader.navigation_menu?.push({
label: entry.title,
page_reference: [
{ title: entry.title, url: entry.url, $: entry.$ },
],
$: {},
});
}
const fFound = newFooter?.navigation.link.find(
(nlink: Links) => nlink.title === entry.title
);
if (!fFound) {
newFooter.navigation.link?.push({
title: entry.title,
href: entry.url,
$: entry.$,
});
}
});
}
return [newHeader, newFooter];
}

export default function Layout({
header,
footer,
Expand All @@ -14,51 +46,19 @@ export default function Layout({
children,
}: { header: HeaderProps, footer: FooterProps, page: PageProps, blogPost: Posts, blogList: Posts, entries: Entry, children: ChilderenProps }) {

const [getLayout, setLayout] = useState({ header, footer });
const getLayout = useMemo(() => {
if (footer && header && entries) {
const [newHeader, newFooter] = buildNavigation(entries, header, footer);
return { header: newHeader, footer: newFooter };
}
return { header, footer };
}, [header, footer, entries]);

const jsonObj: any = { header, footer };
page && (jsonObj.page = page);
blogPost && (jsonObj.blog_post = blogPost);
blogList && (jsonObj.blog_post = blogList);

function buildNavigation(ent: Entry, hd: HeaderProps, ft: FooterProps) {
let newHeader = { ...hd };
let newFooter = { ...ft };
if (ent.length !== newHeader.navigation_menu.length) {
ent.forEach((entry) => {
const hFound = newHeader?.navigation_menu.find(
(navLink: NavLinks) => navLink.label === entry.title
);
if (!hFound) {
newHeader.navigation_menu?.push({
label: entry.title,
page_reference: [
{ title: entry.title, url: entry.url, $: entry.$ },
],
$: {},
});
}
const fFound = newFooter?.navigation.link.find(
(nlink: Links) => nlink.title === entry.title
);
if (!fFound) {
newFooter.navigation.link?.push({
title: entry.title,
href: entry.url,
$: entry.$,
});
}
});
}
return [newHeader, newFooter];
}

useEffect(() => {
if (footer && header && entries) {
const [newHeader, newFooter] = buildNavigation(entries, header, footer);
setLayout({ header: newHeader, footer: newFooter });
}
}, [header, footer]);

return (
<>
{header ? <Header header={getLayout.header} entries={entries} /> : ''}
Expand Down
19 changes: 14 additions & 5 deletions components/tool-tip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,37 @@ type TooltipProps = {
}

const Tooltip = (props: TooltipProps) => {
let timeout: any;
const hideTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const toolTipRef = useRef() as MutableRefObject <HTMLDivElement>;

const showTip = () => {
timeout = setTimeout(() => {
hideTimeoutRef.current = setTimeout(() => {
toolTipRef.current.style.display = "block";
}, props.delay || 400);
};

const hideTip = () => {
clearInterval(timeout);
if (hideTimeoutRef.current !== null) {
clearTimeout(hideTimeoutRef.current);
hideTimeoutRef.current = null;
}
toolTipRef.current.style.display = "none";
};

useEffect(() => {
if (props.dynamic) {
props.status !== 0 && (toolTipRef.current.style.display = "block");
timeout = setTimeout(() => {
hideTimeoutRef.current = setTimeout(() => {
toolTipRef.current.style.display = "none";
}, props.delay || 400);
}
}, [props.content]);
return () => {
if (hideTimeoutRef.current !== null) {
clearTimeout(hideTimeoutRef.current);
hideTimeoutRef.current = null;
}
};
}, [props.content, props.delay, props.dynamic, props.status]);

return (
<div
Expand Down
19 changes: 8 additions & 11 deletions contentstack-sdk/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as Utils from "@contentstack/utils";
import ContentstackLivePreview from "@contentstack/live-preview-utils";
import getConfig from "next/config";
import {
customHostUrl,
initializeContentStackSdk,
Expand All @@ -20,14 +19,12 @@ type GetEntryByUrl = {
jsonRtePath: string[] | undefined;
};

const { publicRuntimeConfig } = getConfig();
const envConfig = process.env.CONTENTSTACK_API_KEY
? process.env
: publicRuntimeConfig;
let customHostBaseUrl = process.env
.NEXT_PUBLIC_CONTENTSTACK_API_HOST as string;

let customHostBaseUrl = envConfig.CONTENTSTACK_API_HOST as string;

customHostBaseUrl = customHostBaseUrl? customHostUrl(customHostBaseUrl): '';
customHostBaseUrl = customHostBaseUrl
? customHostUrl(customHostBaseUrl)
: "";

// SDK initialization
const Stack = initializeContentStackSdk();
Expand All @@ -41,10 +38,10 @@ if (!!customHostBaseUrl && isValidCustomHostUrl(customHostBaseUrl)) {
ContentstackLivePreview.init({
//@ts-ignore
stackSdk: Stack,
clientUrlParams:{
host: envConfig.CONTENTSTACK_APP_HOST,
clientUrlParams: {
host: process.env.NEXT_PUBLIC_CONTENTSTACK_APP_HOST,
},
ssr:false,
ssr: false,
})?.catch((err) => console.error(err));

export const { onEntryChange } = ContentstackLivePreview;
Expand Down
Loading
Loading