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

[PCC-1535] Homepage & grid #298

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
12 changes: 0 additions & 12 deletions starters/nextjs-starter-approuter-ts/app/articles/client.tsx

This file was deleted.

19 changes: 11 additions & 8 deletions starters/nextjs-starter-approuter-ts/app/articles/page.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import { PCCConvenienceFunctions } from "@pantheon-systems/pcc-react-sdk/server";
import { ArticleGrid } from "../../components/grid";
import Layout from "../../components/layout";
import PageHeader from "../../components/page-header";
import { Client } from "./client";

export default async function ArticlesListTemplate() {
const articles = await PCCConvenienceFunctions.getAllArticles();
const { data: articles } = await PCCConvenienceFunctions.getPaginatedArticles(
{
pageSize: 20,
},
);

return (
<Layout>
<div className="max-w-screen-lg mx-auto">
<section>
<PageHeader title="Articles" />
<Client articles={articles} />
</section>
</div>
<section className="max-w-screen-3xl mx-auto px-4 pt-16 sm:w-4/5 md:w-3/4 lg:w-4/5 2xl:w-3/4">
<PageHeader title="Articles" />

<ArticleGrid articles={articles} />
</section>
</Layout>
);
}
Expand Down
84 changes: 48 additions & 36 deletions starters/nextjs-starter-approuter-ts/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,57 @@
import { PCCConvenienceFunctions } from "@pantheon-systems/pcc-react-sdk/server";
import Image from "next/image";
import { Suspense } from "react";
import { SkeletonPageGrid } from "../components/grid";
import HomepageArticleGrid from "../components/homepage-article-grid";
import Link from "next/link";
import { HomepageArticleGrid } from "../components/grid";
import Layout from "../components/layout";

const HomepageHeader = () => (
<div className="flex flex-col mx-auto mt-20 prose sm:prose-xl max-w-fit">
<h1 className="h-full text-4xl prose text-center">
Welcome to{" "}
<a
className="text-blue-600 no-underline hover:underline"
href="https://nextjs.org"
>
Next.js!
</a>
</h1>
<div className="text-2xl">
<div className="flex items-center justify-center p-4 text-white bg-black rounded">
Decoupled PCC on{" "}
<Image
src="/pantheon.png"
alt="Pantheon Logo"
style={{
margin: 0,
}}
width={191}
height={60}
/>
</div>
</div>
</div>
);
import { Button } from "../components/ui/button";

export default async function Home() {
const { data: articles } = await PCCConvenienceFunctions.getPaginatedArticles(
{
pageSize: 3,
},
);

return (
<Layout>
<HomepageHeader />
<section>
<Suspense fallback={<SkeletonPageGrid data={[0, 1, 2, 3, 4, 5]} />}>
<HomepageArticleGrid />
</Suspense>
<section className="bg-neutral-100">
<div className="max-w-screen-3xl 3xl:px-12 mx-auto flex flex-col py-0 xl:flex-row xl:items-center xl:gap-[139px]">
<div className="mx-auto px-6 py-24 sm:max-w-[533px] sm:px-0 lg:mx-0 lg:pl-24 xl:ml-32 xl:max-w-max xl:py-0 xl:pl-0">
<p>WELCOME</p>
<h1 className="my-3 text-5xl font-bold">
Time to make this site your own.
</h1>
<p>
Your new website is waiting to be built for something amazing.
<br />
What are you waiting for?
</p>
<div className="mt-8 flex flex-wrap gap-4 xl:flex-col 2xl:flex-row">
<Link href="/examples">
<Button size="large" className="w-fit">
Discover examples
</Button>
</Link>
<a href="https://pcc.pantheon.io/docs">
<Button size="large" className="w-fit" variant="secondary">
Read our Docs
</Button>
</a>
</div>
</div>
<div className="relative h-[490px] w-full sm:h-[640px] xl:max-w-[900px]">
<Image
src="/images/globe.png"
alt="Image of the earth at night illuminated by lights on the ground"
fill
className="object-cover"
/>
</div>
</div>
</section>

<section className="max-w-screen-3xl mx-auto mt-32 flex justify-center px-4 sm:px-6 lg:px-0">
<HomepageArticleGrid articles={articles} />
</section>
</Layout>
);
Expand Down
207 changes: 98 additions & 109 deletions starters/nextjs-starter-approuter-ts/components/grid.tsx
Original file line number Diff line number Diff line change
@@ -1,126 +1,115 @@
import type { ArticleWithoutContent } from "@pantheon-systems/pcc-react-sdk";
import Link from "next/link";
import Skeleton from "react-loading-skeleton";
import { Tags } from "./tags";
import "react-loading-skeleton/dist/skeleton.css";
import { cn } from "../lib/utils";
import { Button } from "./ui/button";

const GradientPlaceholder = () => (
<div className="w-full h-full bg-gradient-to-b from-blue-100 to-blue-500" />
);

interface Props {
href: string;
imgSrc: string;
altText?: string;
tags?: string[];
title: string;
}

const SkeletonGridItem = () => {
export function HomepageArticleGrid({
articles,
}: {
articles: ArticleWithoutContent[];
}) {
return (
<>
<div className="flex flex-col h-full overflow-hidden rounded-lg shadow-lg">
<div className="relative flex-shrink-0 h-40 cursor-pointer hover:border-indigo-500 border-2s">
<Skeleton height={"100%"} />
</div>
<div className="mx-6 my-4 text-xl font-semibold leading-7 text-gray-900">
<div className="hover:scale-105">
<Skeleton />
</div>
</div>
</div>
</>
<div
className={cn(
"grid grid-cols-1 gap-6 sm:grid-cols-2 lg:w-2/3 2xl:w-full 2xl:grid-cols-[repeat(auto-fit,minmax(300px,438px))] 2xl:justify-center",
)}
>
{articles.map((article, index) => (
<ArticleGridCard
key={article.id}
article={article}
isWide={articles.length === 1 || (articles.length > 2 && index === 2)}
/>
))}
</div>
);
};
}

const GridItem = ({ href, imgSrc, altText, tags, title }: Props) => {
export function ArticleGrid({
articles,
}: {
articles: ArticleWithoutContent[];
}) {
return (
<>
<div className="flex flex-col h-full overflow-hidden rounded-lg shadow-lg">
<Link passHref href={href}>
<div className="relative flex-shrink-0 h-40 cursor-pointer hover:border-indigo-500 border-2s">
{imgSrc != null ? (
<img
src={imgSrc}
alt={altText || title}
className="object-cover w-full h-full"
/>
) : (
<GradientPlaceholder />
)}
</div>
</Link>
<div className="mx-6 my-4 text-xl font-semibold leading-7 text-gray-900">
<Link passHref href={href}>
<div className="hover:scale-105">{title} &rarr;</div>
</Link>
<Tags tags={tags || []} />
</div>
</div>
</>
<div className={cn("grid grid-cols-1 gap-8 lg:grid-cols-2 xl:grid-cols-3")}>
{articles.map((article) => (
<ArticleGridCard key={article.id} article={article} />
))}
</div>
);
};
}

const PostGridItem = ({ content: article }) => {
return (
<GridItem
href={`/articles/${article.slug || article.id}`}
imgSrc={article.metadata["Hero Image"]}
title={article.title}
tags={article.tags}
/>
);
};
interface ArticleGridCardProps {
article: ArticleWithoutContent;
basePath?: string;
imageAltText?: string;
isWide?: boolean;
}

const PageGridItem = ({ content: article }) => {
return (
<GridItem
href={`/articles/${article.slug || article.id}`}
imgSrc={article.metadata["Hero Image"]}
title={article.title}
tags={article.tags}
/>
);
};
export function ArticleGridCard({
article,
basePath = "/articles",
imageAltText,
isWide = false,
}: ArticleGridCardProps) {
const targetHref = `${basePath}/${article.slug || article.id}`;
const imageSrc = article.metadata?.["Hero Image"] || null;

export const Grid = ({ children }) => {
return (
<div
className={`mt-12 grid gap-5 max-w-content mx-auto lg:max-w-screen-lg lg:grid-cols-3`}
className={cn(
"group flex h-full flex-col overflow-clip rounded-xl shadow-lg ring-1 ring-gray-300/50",
isWide
? "sm:col-span-2 sm:flex-row 2xl:col-span-1 2xl:flex-col 2xl:only:col-span-2 2xl:only:flex-row"
: "",
)}
>
{children}
<div
className={cn(
"aspect-video w-full flex-shrink-0 overflow-hidden sm:h-[196px]",
isWide
? "sm:h-full sm:max-w-[49%] 2xl:h-[196px] 2xl:max-w-[100%] 2xl:group-only:h-full 2xl:group-only:max-w-[49%]"
: "max-w-[100%]",
)}
>
<GridItemCoverImage
imageSrc={imageSrc}
imageAltText={imageAltText || `Cover image for ${article.title}`}
/>
</div>
<div
className={cn(
"flex flex-grow flex-col justify-between p-8",
isWide && "sm:py-24 2xl:py-8 2xl:group-only:py-24",
)}
>
<div>
<h1 className="mb-3 text-xl font-semibold leading-7">
{article.title}
</h1>
{article.metadata?.["Description"] && (
<p className="line-clamp-3 min-h-[4.5rem] text-gray-600">
{article.metadata?.["Description"]?.toString() || ""}
</p>
)}
</div>
<Link href={targetHref} className="mt-8">
<Button size="large">View</Button>
</Link>
</div>
</div>
);
};

interface GriddedComponentProps {
data: any[] | null;
FallbackComponent?: any | null | undefined;
}

export const withGrid = (Component) => {
const GriddedComponents = ({
data,
FallbackComponent,
...props
}: GriddedComponentProps & any) => {
return (
<>
{data ? (
<Grid>
{data.map((content, i) => {
return <Component key={i} content={content} {...props} />;
})}
</Grid>
) : FallbackComponent ? (
FallbackComponent
) : null}
</>
);
};

return GriddedComponents;
};

export const PostGrid = withGrid(PostGridItem);
export const PageGrid = withGrid(PageGridItem);
export const SkeletonPageGrid = withGrid(SkeletonGridItem);
function GridItemCoverImage({ imageSrc, imageAltText }) {
return imageSrc != null ? (
// eslint-disable-next-line @next/next/no-img-element
<img
src={imageSrc}
alt={imageAltText}
className="h-full w-full object-cover"
/>
) : (
<div className="h-full w-full bg-gradient-to-t from-neutral-800 to-neutral-100" />
);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export default function PageHeader({ title }) {
return (
<header className="prose text-2xl mx-auto mt-20">
<h1 className="text-center mx-auto">{title}</h1>
<header className="mb-8">
<h1 className="text-5xl font-bold">{title}</h1>
</header>
);
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading