Skip to content

Commit

Permalink
Next 12 with personalized routes
Browse files Browse the repository at this point in the history
  • Loading branch information
Ashish-SA-CS committed Nov 5, 2024
0 parents commit 19ff275
Show file tree
Hide file tree
Showing 25 changed files with 5,955 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
NEXT_PUBLIC_CONTENTSTACK_API_KEY=bltf1a2bb701d537d95
NEXT_PUBLIC_CONTENTSTACK_DELIVERY_TOKEN=csa5b6b7935b22e4f08772c1d0
NEXT_PUBLIC_CONTENTSTACK_PREVIEW_TOKEN=csd1571d7d8b2386120730db85
NEXT_PUBLIC_CONTENTSTACK_ENVIRONMENT=preview
NEXT_PUBLIC_CONTENTSTACK_REGION=EU
NEXT_PUBLIC_CONTENTSTACK_PREVIEW=true
NEXT_PUBLIC_CONTENTSTACK_P13N_PROJECT_ID=671a14b9658bc90e1fa85cf5
3 changes: 3 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
35 changes: 35 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
3 changes: 3 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
13 changes: 13 additions & 0 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import "./globals.css";

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
14 changes: 14 additions & 0 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default function Home() {
return (
<div
style={{
maxWidth: 1280,
margin: '0 auto',
padding: '2rem',
textAlign: 'center',
}}
>
Start prompting.
</div>
);
}
20 changes: 20 additions & 0 deletions components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "app/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
}
}
47 changes: 47 additions & 0 deletions components/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@

import Link from 'next/link'
import { useState } from 'react'

export default function Header() {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)

return (
<header className="bg-white">
<nav aria-label="Global" className="mx-auto flex w-full items-center justify-between py-6">
<div className="flex lg:flex-1">
<a href="/" className="-m-1.5 p-1.5">
<span className="sr-only">Your Company</span>
<img
alt=""
src="https://images.contentstack.io/v3/assets/blt7359e2a55efae483/blt518e5105a0686696/663e30a08f19535905e50af2/Logo.svg"
className="h-8 w-auto"
/>
</a>
</div>
<div className="flex lg:hidden">
<button
type="button"
onClick={() => setMobileMenuOpen(true)}
className="-m-2.5 inline-flex items-center justify-center rounded-md p-2.5 text-gray-700"
>
<span className="sr-only">Open main menu</span>
{/* <Bars3Icon aria-hidden="true" className="h-6 w-6" /> */}
</button>
</div>

<div className='flex flex-row items-center justify-between gap-10'>

<a href="/" className="text-sm/6 font-semibold text-gray-900">
Home
</a>
<a href="/marketer" className="text-sm/6 font-semibold text-gray-900">
Marketer
</a>
<a href="/developer" className="text-sm/6 font-semibold text-gray-900">
Developer
</a>
</div>
</nav>
</header>
)
}
64 changes: 64 additions & 0 deletions components/PersonalizeButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"use client";

import React, { useContext, useState } from "react";
import { PersonalizeContext } from "../lib/contentstack";

type PersonalizeType = "Marketer" | "Developer" | "Reset";

interface PersonalizeButtonProps {
type: PersonalizeType;
}

const PersonalizeButton: React.FC<PersonalizeButtonProps> = ({ type }) => {
const [clicked, setClicked] = useState<boolean>(false);
const Personalize = useContext(PersonalizeContext);

const handleClick = async () => {
let p13nAttributes: { marketer: boolean; developer: boolean };
let eventName: string;

switch (type.toLowerCase()) {
case "marketer":
p13nAttributes = { marketer: true, developer: false };
eventName = "CtaMarketer";
break;
case "developer":
p13nAttributes = { marketer: false, developer: true };
eventName = "CtaDeveloper";
break;
case "reset":
p13nAttributes = { marketer: false, developer: false };
eventName = "CtaReset";
break;
default:
console.error("Invalid personalization type");
return;
}

setClicked(true);

// set Personalize attribute for p13nAttributes (marketer, CtaDeveloper, CtaReset).
// see: Contentstack Dashboard > Personalize project > Events
await Personalize.set(p13nAttributes);

// send Personalize event for the experience for eventName (CtaMarketer, CtaDeveloper, CtaReset).
// see: Contentstack Dashboard > Personalize project > Events
await Personalize.triggerEvent(eventName);

window.location.href = "http://localhost:3000";
};

return (
<div className="flex space-x-4 items-center">
<button
onClick={handleClick}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
{type === "Reset" ? <>Reset attributes</> : <>Set Attribute: {type}</>}
</button>
{clicked ? <p>Setting {type} Personalization</p> : null}
</div>
);
};

export default PersonalizeButton;
15 changes: 15 additions & 0 deletions components/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ReactNode } from 'react'

interface LayoutProps {
children: ReactNode
}

export function Layout({ children }: LayoutProps) {
return (
<div className="min-h-screen bg-white">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
{children}
</div>
</div>
)
}
78 changes: 78 additions & 0 deletions lib/contentstack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import contentstack, { Region, QueryOperation } from '@contentstack/delivery-sdk'
import ContentstackLivePreview, { IStackSdk } from '@contentstack/live-preview-utils';
import { Page } from './types';
import Personalize from '@contentstack/personalize-edge-sdk';
import { createContext } from 'react';

// Stack creation
export const stack = contentstack.stack({
apiKey: process.env.NEXT_PUBLIC_CONTENTSTACK_API_KEY as string,
deliveryToken: process.env.NEXT_PUBLIC_CONTENTSTACK_DELIVERY_TOKEN as string,
environment: process.env.NEXT_PUBLIC_CONTENTSTACK_ENVIRONMENT as string,
region: process.env.NEXT_PUBLIC_CONTENTSTACK_REGION === 'EU' ? Region.EU : Region.US,
live_preview: {
enable: process.env.NEXT_PUBLIC_CONTENTSTACK_PREVIEW === 'true',
preview_token: process.env.NEXT_PUBLIC_CONTENTSTACK_PREVIEW_TOKEN,
host: process.env.NEXT_PUBLIC_CONTENTSTACK_REGION === 'EU' ? 'eu-rest-preview.contentstack.com' : 'rest-preview.contentstack.com',
}
});

// Livepreview Init
ContentstackLivePreview.init({
ssr: false,
enable: process.env.NEXT_PUBLIC_CONTENTSTACK_PREVIEW === 'true',

stackSdk: stack.config as IStackSdk,
stackDetails: {
apiKey: process.env.NEXT_PUBLIC_CONTENTSTACK_API_KEY as string,
environment: process.env.NEXT_PUBLIC_CONTENTSTACK_ENVIRONMENT as string,
},
clientUrlParams: {
host:
process.env.NEXT_PUBLIC_CONTENTSTACK_REGION === 'EU'
? 'eu-app.contentstack.com'
: 'app.contentstack.com',
},
editButton: {
enable: true,
},
});

// Personalize context creation
const edgeApiUrl = process.env.NEXT_PUBLIC_CONTENTSTACK_REGION === 'EU' ? 'https://eu-personalize-edge.contentstack.com' : 'https://personalize-edge.contentstack.com'
const projectUid = process.env.NEXT_PUBLIC_CONTENTSTACK_P13N_PROJECT_ID as string;

Personalize.setEdgeApiUrl(edgeApiUrl);
Personalize.init(projectUid);

export const PersonalizeContext = createContext(Personalize);

// Query Pages with p13n variants
export async function getPage(url: string, variantParam: any) {
let result;

const pageQuery = await stack
.contentType('page')
.entry()

pageQuery.addParams({ include_all: true });
pageQuery.addParams({ include_dimension: true });
pageQuery.addParams({ include_applied_variants: true });

if (variantParam) {
const variantAlias = Personalize.variantParamToVariantAliases(variantParam).join(',');
result = await pageQuery.variants(variantAlias).query().where('url', QueryOperation.EQUALS, url).find<Page>();
} else {
result = await pageQuery.query().where('url', QueryOperation.EQUALS, url).find<Page>();
}

if (result.entries) {
const entry = result.entries[0]

if (process.env.NEXT_PUBLIC_CONTENTSTACK_PREVIEW === 'true') {
contentstack.Utils.addEditableTags(entry, 'page', true);
}

return entry;
}
}
37 changes: 37 additions & 0 deletions lib/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
export interface PublishDetails {
environment: string;
locale: string;
time: string;
user: string;
}

export interface File {
uid: string;
created_at: string;
updated_at: string;
created_by: string;
updated_by: string;
content_type: string;
file_size: string;
tags: string[];
filename: string;
url: string;
ACL: any[];
is_dir: boolean;
parent_uid: string;
_version: number;
title: string;
publish_details: PublishDetails;
$: any;
}

export interface Page {
uid: string;
$: any;
_version?: number;
title: string;
url?: string;
description?: string;
image?: File | null;
rich_text?: string;
}
6 changes: 6 additions & 0 deletions lib/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
29 changes: 29 additions & 0 deletions middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import Personalize from '@contentstack/personalize-edge-sdk';

export const config = {
matcher: [
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
}

export default async function middleware(request: NextRequest) {
const projectUid = process.env.NEXT_PUBLIC_CONTENTSTACK_P13N_PROJECT_ID as string;
const edgeApiUrl = process.env.NEXT_PUBLIC_CONTENTSTACK_REGION === 'EU' ? 'https://eu-personalize-edge.contentstack.com' : 'https://personalize-edge.contentstack.com'

Personalize.setEdgeApiUrl(edgeApiUrl);

await Personalize.init(projectUid, { request });

const variantParam = Personalize.getVariantParam();
const parsedUrl = new URL(request.url);

parsedUrl.searchParams.set(Personalize.VARIANT_QUERY_PARAM, variantParam);

const response = NextResponse.rewrite(parsedUrl);

await Personalize.addStateToResponse(response)

return response;
}
Loading

0 comments on commit 19ff275

Please sign in to comment.