Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Devrel 914/dynamic projects #40

Closed
wants to merge 35 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
53fb556
parametrize client with envid and previewApiKey
IvanKiral Sep 13, 2023
d6eba1d
add envId into the url path and update middleware
IvanKiral Sep 13, 2023
da23779
update middleware to handle articles
IvanKiral Sep 13, 2023
4ea0117
update pages to get envid from url params and adjust middleware to re…
IvanKiral Sep 13, 2023
09a8a2b
add auth0
IvanKiral Sep 13, 2023
33487d8
add callback
IvanKiral Sep 13, 2023
3a6125d
get api key from auth0
IvanKiral Sep 13, 2023
1425808
update pages and apis to use id and key from cookies
IvanKiral Sep 13, 2023
86ad402
add fallback to client
IvanKiral Sep 14, 2023
6740053
change callback url
IvanKiral Sep 14, 2023
27a2069
turn off ssr on getPreviewApiKey
IvanKiral Sep 14, 2023
5b6bb0c
fix callback url
IvanKiral Sep 14, 2023
1df415b
fix bugs when getting to same or default environment
IvanKiral Sep 15, 2023
35b66c3
try to fix css on web spotligh
IvanKiral Sep 15, 2023
7339183
remove unnecesary setCookie
IvanKiral Sep 15, 2023
aad3335
update getting cookie in api routes to not throw but return 400
IvanKiral Sep 15, 2023
ddcf0cc
add currentEnvId to cookie when empty
IvanKiral Sep 15, 2023
ac8f531
Fix exiting preview when envId is changed
IvanKiral Sep 15, 2023
a6aaa9e
try vercel url when deployed
IvanKiral Sep 18, 2023
ad3b345
update callback from auth0 to work with preview
IvanKiral Sep 18, 2023
7e932ca
refactor middleware, changed the way the first envId cookie is stored
IvanKiral Sep 19, 2023
a38fed2
remove unnecesary variable names
IvanKiral Sep 19, 2023
9f387b3
refactor getting envId from route or env variables
IvanKiral Sep 19, 2023
40d05a2
refactor getting previewApiKey in pages
IvanKiral Sep 19, 2023
b7f09f1
Fix redirect after login to keep state
IvanKiral Sep 19, 2023
8501511
extract auth0 variables and refactor
IvanKiral Sep 19, 2023
6c9d0aa
extract domain
IvanKiral Sep 19, 2023
ae90974
add redirect uri to enviroment variables
IvanKiral Sep 19, 2023
4fccd24
DEVREL-914 Use envId from cookie for smartLink
JiriLojda Sep 20, 2023
c471c03
DEVREL-914 Put cookie names into constants for better maintainability
JiriLojda Sep 20, 2023
3ac7c77
DEVREL-914 Rework domain-related env variables
JiriLojda Sep 20, 2023
3814e08
DEVREL-914 Add the new concepts into README
JiriLojda Sep 20, 2023
03b2430
DEVREL-914 Make siteCodename envVar public
JiriLojda Sep 21, 2023
f503430
Make getEnvIdFromRouteParams accept the whole context to ease removin…
JiriLojda Sep 21, 2023
0167e61
single-project setup codebase summary
Sep 21, 2023
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
13 changes: 13 additions & 0 deletions .env.local.template
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,16 @@ NEXT_PUBLIC_OTHER_COLLECTIONS_DOMAINS=
KONTENT_PREVIEW_API_KEY=
KONTENT_MANAGEMENT_API_KEY=

# Domains you need to set either NEXT_PUBLIC_KONTENT_DOMAIN or all the specific domains
NEXT_PUBLIC_KONTENT_DOMAIN=kontent.ai

NEXT_PUBLIC_KONTENT_AUTH_DOMAIN=
NEXT_PUBLIC_KONTENT_IAPI_DOMAIN=
NEXT_PUBLIC_KONTENT_MAPI_DOMAIN=
NEXT_PUBLIC_KONTENT_PREVIEW_DAPI_DOMAIN=
NEXT_PUBLIC_KONTENT_DAPI_DOMAIN=


# Variables used for login to the auth0 to allow dynamic projects
AUTH0_WEBAUTH_CLIENT_ID=
AUTH0_WEBAUTH_AUDIENCE=
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,28 @@ yarn create next-app --example https://github.com/kontent-ai-bot/kontent-ai-new-
```

🎉 Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
> If you want to use your app inside [web spotlight](https://kontent.ai/features/webspotlight/), you will need to run the project under the `https` scheme.
> To do that you can use a proxy like [Ngrok](https://ngrok.com/) or [write you own server](https://github.com/vercel/next.js/tree/canary/examples/custom-server).

You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.

> By default, the content is loaded from a shared Kontent.ai project. If you want to use your own clone of the project so that you can customize it and experiment with Kontent, continue to the next section.
> By default, the content is loaded from a shared Kontent.ai project.
> If you want to use your own clone of the project so that you can customize it and experiment with Kontent, change the relevant settings in your `.env.local` file.

To generate new models from Kontent.ai data, just run `npm run generateModels`. Make sure you have environment variables filled in properly.

### Use codebase as a starter

> ⚠ This project is not intended as a starter project. It is as a sample of the presentation channel showcasing Kontent.ai capabilities. The following hints help you to use this code base as a base for presentation channel for your project as a boilerplate. By doing it, you are accepting the fact you are changing the purpose of this code.

The app contains code to dynamically handle different Kontent.ai projects (e.g. the environment route prefix). To adjust the code to be used to single project as a starter, you want to remove the logic that is used solely for showcasing the sample project during evaluation.

Some of the parts responsible for handle different Kontent.ai projects that needs adjustments in case of transforming it into single-project setup.

* `middleware.ts` - Getting the Kontent.ai environment ID and storing it in cookie. For single-project setup use only the environment variable with environment ID should be used.
* `pages/callback.ts` & `pages/getPreviewApiKey.ts` & `constants/auth.ts` - Responsible for exchanging preview API keys for specified environment. For single-project setup use only the environment variable with preview API key should be used.
* `pages/[envid]` - folder responsible for the [dynamic segment](https://nextjs.org/docs/pages/building-your-application/routing/dynamic-routes) passing the environment ID for pages. For single-project setup remove the folder and move its content one level up.

## Create your own data source project in Kontent.ai

TBD - raise the issue, if you want the project backup
Expand Down
54 changes: 24 additions & 30 deletions components/landingPage/ui/heroImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import Image from "next/image";
import { FC, ReactNode } from "react";

import { mainColorMdBgClass } from "../../../lib/constants/colors";
import { siteCodename } from "../../../lib/utils/env";
import { createItemSmartLink } from "../../../lib/utils/smartLinkUtils";
import { useSiteCodename } from "../../shared/siteCodenameContext";

type Props = Readonly<{
url: string;
Expand All @@ -12,35 +12,29 @@ type Props = Readonly<{
itemId?: string;
}>;

export const HeroImage: FC<Props> = (props) => {
const siteCodename = useSiteCodename();

return (
<>
<figure
className={`relative m-0 justify-start not-prose w-screen h-screen max-h-[1200px] ${
props.className ?? ""
}`}
{...createItemSmartLink(props.itemId)}
export const HeroImage: FC<Props> = (props) => (
<>
<figure
className={`relative m-0 justify-start not-prose w-screen h-screen max-h-[1200px] ${props.className ?? ""}`}
{...createItemSmartLink(props.itemId)}
>
<div className="absolute inset-0 [margin-left:-38%]">
<Image
src={props.url}
alt="Hero image"
fill
className="object-cover"
sizes="(max-width: 1200px) 100vw, 80vw"
priority
/>
</div>
<div
className={`${mainColorMdBgClass[siteCodename]} relative w-fit h-full flex max-w-3xl flex-col items-center md:items-start justify-end min-[900px]:justify-center pt-20 pb-32 md:pb-12 pr-6 min-[900px]:px-6`}
>
<div className="absolute inset-0 [margin-left:-38%]">
<Image
src={props.url}
alt="Hero image"
fill
className="object-cover"
sizes="(max-width: 1200px) 100vw, 80vw"
priority
/>
</div>
<div
className={`${mainColorMdBgClass[siteCodename]} relative w-fit h-full flex max-w-3xl flex-col items-center md:items-start justify-end min-[900px]:justify-center pt-20 pb-32 md:pb-12 pr-6 min-[900px]:px-6`}
>
{props.children}
</div>
</figure>
</>
);
};
{props.children}
</div>
</figure>
</>
);

HeroImage.displayName = "HeroImage";
68 changes: 32 additions & 36 deletions components/listingPage/ArticleItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { FC } from "react";

import { mainColorButtonClass, mainColorGroupHoverClass } from "../../lib/constants/colors";
import { formatDate } from "../../lib/utils/dateTime";
import { useSiteCodename } from "../shared/siteCodenameContext";
import { siteCodename } from "../../lib/utils/env";
import { StandaloneSmartLinkButton } from "../shared/StandaloneSmartLinkButton";

type Props = Readonly<{
Expand All @@ -16,40 +16,36 @@ type Props = Readonly<{
itemId?: string;
}>;

export const ArticleItem: FC<Props> = props => {
const siteCodename = useSiteCodename()

return (
<li className="m-0 p-0 relative md:rounded-lg shadow hover:shadow-xl transition-shadow border border-gray-200 cursor-pointer">
<Link
href={props.detailUrl}
className="no-underline group"
>
<StandaloneSmartLinkButton itemId={props.itemId} />
<figure className="w-full relative m-0 h-40">
<Image
src={props.imageUrl}
alt={props.title}
fill
sizes="(max-width: 635px) 100vw, (max-width: 1275px) 50vw, 25vw"
className="object-cover h-full m-0 p-0 md:rounded-t-lg"
/>
</figure>
{props.publishingDate && (
<div className="w-fit p-2 bg-gray-800 text-white opacity-90 font-normal line-clamp-6 absolute right-0 translate-y-[-100%]">
<p className="m-0 w-fit">{formatDate(props.publishingDate)}</p>
</div>
)}
<div className="p-5">
<h5 className="mb-2 text-xl font-bold tracking-tight text-gray-900 no-underline line-clamp-2 ">{props.title}</h5>
<p className="mb-0 font-normal text-gray-700 line-clamp-6">{props.description}</p>
export const ArticleItem: FC<Props> = props => (
<li className="m-0 p-0 relative md:rounded-lg shadow hover:shadow-xl transition-shadow border border-gray-200 cursor-pointer">
<Link
href={props.detailUrl}
className="no-underline group"
>
<StandaloneSmartLinkButton itemId={props.itemId} />
<figure className="w-full relative m-0 h-40">
<Image
src={props.imageUrl}
alt={props.title}
fill
sizes="(max-width: 635px) 100vw, (max-width: 1275px) 50vw, 25vw"
className="object-cover h-full m-0 p-0 md:rounded-t-lg"
/>
</figure>
{props.publishingDate && (
<div className="w-fit p-2 bg-gray-800 text-white opacity-90 font-normal line-clamp-6 absolute right-0 translate-y-[-100%]">
<p className="m-0 w-fit">{formatDate(props.publishingDate)}</p>
</div>
<button className={`${mainColorGroupHoverClass[siteCodename]} ${mainColorButtonClass[siteCodename]} block ml-auto w-fit mb-3 mr-4 font-semibold line-clamp-6 border text-white py-2 px-4 md:rounded`}>
<span>Continue reading</span>
</button>
</Link>
</li>
);
}
)}
<div className="p-5">
<h5 className="mb-2 text-xl font-bold tracking-tight text-gray-900 no-underline line-clamp-2 ">{props.title}</h5>
<p className="mb-0 font-normal text-gray-700 line-clamp-6">{props.description}</p>
</div>
<button className={`${mainColorGroupHoverClass[siteCodename]} ${mainColorButtonClass[siteCodename]} block ml-auto w-fit mb-3 mr-4 font-semibold line-clamp-6 border text-white py-2 px-4 md:rounded`}>
<span>Continue reading</span>
</button>
</Link>
</li>
);

ArticleItem.displayName = "ListItem";
ArticleItem.displayName = "ListItem";
58 changes: 27 additions & 31 deletions components/listingPage/ProductItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Link from "next/link";
import { FC } from "react";

import { mainColorButtonClass, mainColorGroupHoverClass } from "../../lib/constants/colors";
import { useSiteCodename } from "../shared/siteCodenameContext";
import { siteCodename } from "../../lib/utils/env";
import { StandaloneSmartLinkButton } from "../shared/StandaloneSmartLinkButton";

type Props = Readonly<{
Expand All @@ -15,35 +15,31 @@ type Props = Readonly<{
itemId?: string;
}>;

export const ProductItem: FC<Props> = props => {
const siteCodename = useSiteCodename()
export const ProductItem: FC<Props> = props => (
<li className="min-w-full m-0 p-0 relative rounded-lg shadow hover:shadow-xl transition-shadow border border-gray-200 cursor-pointer min-h-full">
<Link
href={props.detailUrl}
className="group no-underline p-0 m-0"
>
<StandaloneSmartLinkButton itemId={props.itemId} />

return (
<li className="min-w-full m-0 p-0 relative rounded-lg shadow hover:shadow-xl transition-shadow border border-gray-200 cursor-pointer min-h-full">
<Link
href={props.detailUrl}
className="group no-underline p-0 m-0"
>
<StandaloneSmartLinkButton itemId={props.itemId} />
<div className="flex flex-col gap-2">
<h5 className="px-4 pt-2 mt-2 text-center text-xl tracking-wider font-semibold text-gray-900">{props.title}</h5>
<p className="m-0 text-center text-gray-500 text-base">{props.category}</p>
<figure className="w-full relative m-0 h-40">
<Image
src={props.imageUrl}
alt={props.title}
fill
sizes="(max-width: 635px) 100vw, (max-width: 1534px) 50vw, 25vw"
className="object-contain h-full w-full m-0 p-0 rounded-t-lg"
/>
</figure>
{props.price && <p className="m-0 text-center text-xl font-normal pb-2">{`${props.price}€`}</p>}
<button className={`${mainColorGroupHoverClass[siteCodename]} ${mainColorButtonClass[siteCodename]} text-white block ml-auto w-fit mb-3 mr-4 font-semibold line-clamp-6 hover:bg-transparent border py-2 px-4 rounded`}>Detail</button>
</div>
</Link>
</li>
)

<div className="flex flex-col gap-2">
<h5 className="px-4 pt-2 mt-2 text-center text-xl tracking-wider font-semibold text-gray-900">{props.title}</h5>
<p className="m-0 text-center text-gray-500 text-base">{props.category}</p>
<figure className="w-full relative m-0 h-40">
<Image
src={props.imageUrl}
alt={props.title}
fill
sizes="(max-width: 635px) 100vw, (max-width: 1534px) 50vw, 25vw"
className="object-contain h-full w-full m-0 p-0 rounded-t-lg"
/>
</figure>
{props.price && <p className="m-0 text-center text-xl font-normal pb-2">{`${props.price}€`}</p>}
<button className={`${mainColorGroupHoverClass[siteCodename]} ${mainColorButtonClass[siteCodename]} text-white block ml-auto w-fit mb-3 mr-4 font-semibold line-clamp-6 hover:bg-transparent border py-2 px-4 rounded`}>Detail</button>
</div>
</Link>
</li>
);
}

ProductItem.displayName = "ListItem";
ProductItem.displayName = "ListItem";
3 changes: 1 addition & 2 deletions components/shared/Fact.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import Image from "next/image";
import { FC } from "react";

import { mainColorAfterBgClass } from "../../lib/constants/colors";
import { siteCodename } from "../../lib/utils/env";
import {
createElementSmartLink,
createItemSmartLink,
} from "../../lib/utils/smartLinkUtils";
import { contentTypes, Fact } from "../../models";
import { CTAButton } from "./internalLinks/CTAButton";
import { useSiteCodename } from "./siteCodenameContext";

type Props = Readonly<{
item: Fact;
Expand All @@ -17,7 +17,6 @@ type Props = Readonly<{

export const FactComponent: FC<Props> = (props) => {
const image = props.item.elements.image.value[0];
const siteCodename = useSiteCodename();
const authorElements = props.item.elements.author.linkedItems[0]?.elements;
const { firstName, lastName, occupation } = authorElements ?? {};

Expand Down
5 changes: 2 additions & 3 deletions components/shared/internalLinks/CTAButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,17 @@ import {
mainColorHoverClass,
} from "../../../lib/constants/colors";
import { resolveReference } from "../../../lib/routing";
import { siteCodename } from "../../../lib/utils/env";
import { Action, Fact, Nav_NavigationItem } from "../../../models";
import { useSiteCodename } from "../siteCodenameContext";

type Props = {
reference: Fact | Action | Nav_NavigationItem;
};

export const CTAButton = (props: Props) => {
const siteCodename = useSiteCodename();
const factUrl =
props.reference.elements.referenceExternalUri.value ||
props.reference.elements.referenceContentItemLink.linkedItems.length > 0
props.reference.elements.referenceContentItemLink.linkedItems.length > 0
? resolveReference(props.reference)
: null;
return (
Expand Down
30 changes: 13 additions & 17 deletions components/shared/richText/Callout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,27 @@ import {
calloutTypeColor,
mainColorBorderClass,
} from "../../../lib/constants/colors";
import { siteCodename } from "../../../lib/utils/env";
import { Component_Callout } from "../../../models";
import { useSiteCodename } from "../siteCodenameContext";
import { RichTextElement } from "./RichTextElement";

type Props = Readonly<{
item: Component_Callout;
}>;

export const CalloutComponent: FC<Props> = (props) => {
const siteCodename = useSiteCodename();

return (
<div
className={`p-5 border-2 rounded-3xl ${mainColorBorderClass[siteCodename]}`}
>
<div className={`w-5 ${createIconColor(props.item)}`}>
{renderTypeIcon(props.item)}
</div>
<RichTextElement
element={props.item.elements.content}
isInsideTable={false}
/>
export const CalloutComponent: FC<Props> = (props) => (
<div
className={`p-5 border-2 rounded-3xl ${mainColorBorderClass[siteCodename]}`}
>
<div className={`w-5 ${createIconColor(props.item)}`}>
{renderTypeIcon(props.item)}
</div>
);
};
<RichTextElement
element={props.item.elements.content}
isInsideTable={false}
/>
</div>
);

const renderTypeIcon = (callout: Component_Callout) => {
switch (callout.elements.type.value[0]?.codename) {
Expand Down
Loading
Loading