Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6479d1c
Updated package files
codebravotech Jul 31, 2025
ce3b4b8
Updated lockfile
codebravotech Jul 31, 2025
f969f97
Shared utils
codebravotech Jul 31, 2025
406d219
Updates to the CTA
codebravotech Jul 31, 2025
f8919a6
Add image support for blocks
codebravotech Jul 31, 2025
3388da1
Fix the viewport width/height based cover image (changed it so that d…
codebravotech Jul 31, 2025
5c740c2
Fix issue in the media hook
codebravotech Jul 31, 2025
95f14b2
Remove mux video package (unneeded accidental addition from my other …
codebravotech Jul 31, 2025
b5abfe6
Update collapsability of button
codebravotech Jul 31, 2025
067933d
Update package versions and move to JSX based page renderer to fix us…
codebravotech Aug 15, 2025
201d3a9
Merge pull request #1 from codebravotech/jp/feat/updated-page-building
codebravotech Aug 15, 2025
e945adf
Merge remote-tracking branch 'upstream/main' into jp/feat/updated-pag…
codebravotech Aug 15, 2025
bf2700b
Merge remote-tracking branch 'origin/jp/feat/updated-page-building'
codebravotech Aug 15, 2025
b9ab740
Updated CTA with color theming options
codebravotech Aug 15, 2025
eb2eddf
Clickable overlay for the page builder when its empty
codebravotech Aug 15, 2025
f85615d
collapsable layout section
codebravotech Aug 15, 2025
f7c76b9
iterate on the changes to the page builder blocks and image component.
kenjonespizza Sep 9, 2025
98aee66
remove un-needed h3-h6 from <PortableText>
kenjonespizza Sep 9, 2025
e0474c8
swap Avatar image component. update studio icons. various small rev…
kenjonespizza Sep 10, 2025
46e3bdf
make imports consistent
kenjonespizza Sep 10, 2025
9231d5f
Merge branch 'main' into feat/updates-images-and-page-builder
kenjonespizza Sep 11, 2025
aeaa0ec
extract types
kenjonespizza Sep 11, 2025
b89b03f
attempt Vercel build
kenjonespizza Sep 11, 2025
e63c9ad
update broken 'open in studio link' and simplify stega config in the …
kenjonespizza Sep 12, 2025
a284a6f
update sample data for new stucture
kenjonespizza Sep 12, 2025
13ea172
modify createDataAttribute. Remove un-needed Suspense
kenjonespizza Sep 22, 2025
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
4 changes: 2 additions & 2 deletions frontend/app/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ export default async function Page(props: Props) {
<div className="container">
<div className="pb-6 border-b border-gray-100">
<div className="max-w-3xl">
<h2 className="text-4xl font-bold tracking-tight text-gray-900 sm:text-5xl lg:text-7xl">
<h1 className="text-4xl text-gray-900 sm:text-5xl lg:text-7xl">
{page.heading}
</h2>
</h1>
<p className="mt-4 text-base lg:text-lg leading-relaxed text-gray-600 uppercase font-light">
{page.subheading}
</p>
Expand Down
2 changes: 1 addition & 1 deletion frontend/app/client-utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client'

import {isCorsOriginError} from 'next-sanity'
import {isCorsOriginError} from 'next-sanity/live'
import {toast} from 'sonner'

export function handleError(error: unknown) {
Expand Down
23 changes: 9 additions & 14 deletions frontend/app/components/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import {Image} from 'next-sanity/image'

import {urlForImage} from '@/sanity/lib/utils'
import DateComponent from '@/app/components/Date'
import Image from "@/app/components/SanityImage";
import DateComponent from "@/app/components/Date";

type Props = {
person: {
Expand All @@ -21,25 +19,22 @@ export default function Avatar({person, date, small = false}: Props) {
{picture?.asset?._ref ? (
<div className={`${small ? 'h-6 w-6 mr-2' : 'h-9 w-9 mr-4'}`}>
<Image
alt={picture?.alt || ''}
className="h-full rounded-full object-cover"
id={picture.asset._ref}
alt={picture?.alt || ""}
className="h-full rounded-full"
height={small ? 32 : 48}
width={small ? 32 : 48}
src={
urlForImage(picture)
?.height(small ? 64 : 96)
.width(small ? 64 : 96)
.fit('crop')
.url() as string
}
hotspot={picture.hotspot}
crop={picture.crop}
mode="cover"
/>
</div>
) : (
<div className="mr-1">By </div>
)}
<div className="flex flex-col">
{firstName && lastName && (
<div className={`font-bold ${small ? 'text-sm' : ''}`}>
<div className={`${small ? "text-sm" : ""}`}>
{firstName} {lastName}
</div>
)}
Expand Down
24 changes: 11 additions & 13 deletions frontend/app/components/BlockRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
import React from 'react'

import Cta from '@/app/components/Cta'
import Info from '@/app/components/InfoSection'
import {dataAttr} from '@/sanity/lib/utils'
import Cta from "@/app/components/Cta";
import Info from "@/app/components/InfoSection";
import { dataAttr } from "@/sanity/lib/utils";
import { PageBuilderSection } from "@/sanity/lib/types";

type BlocksType = {
[key: string]: React.FC<any>
}

type BlockType = {
_type: string
_key: string
}

type BlockProps = {
index: number
block: BlockType
pageId: string
pageType: string
}
index: number;
block: PageBuilderSection;
pageId: string;
pageType: string;
};

const Blocks: BlocksType = {
callToAction: Cta,
Expand All @@ -44,6 +40,8 @@ export default function BlockRenderer({block, index, pageId, pageType}: BlockPro
key: block._key,
block: block,
index: index,
pageId: pageId,
pageType: pageType,
})}
</div>
)
Expand Down
25 changes: 0 additions & 25 deletions frontend/app/components/CoverImage.tsx

This file was deleted.

77 changes: 56 additions & 21 deletions frontend/app/components/Cta.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,72 @@
import {Suspense} from 'react'
import {PortableTextBlock} from 'next-sanity'

import ResolvedLink from '@/app/components/ResolvedLink'
import {CallToAction} from '@/sanity.types'
import PortableText from '@/app/components/PortableText'
import Image from '@/app/components/SanityImage'
import {stegaClean} from '@sanity/client/stega'
import {ExtractPageBuilderType} from '@/sanity/lib/types'

type CtaProps = {
block: CallToAction
block: ExtractPageBuilderType<'callToAction'>
index: number
// Needed if you want to createDataAttributes to do non-text overlays in Presentation (Visual Editing)
pageType: string
pageId: string
}

export default function CTA({block}: CtaProps) {
const {heading, eyebrow, body = [], button, image, theme, contentAlignment} = block

const isDark = theme === 'dark'
const isImageFirst = stegaClean(contentAlignment) === 'imageFirst'

return (
<div className="container my-12">
<div className="bg-gray-50 border border-gray-100 rounded-2xl max-w-3xl">
<div className="px-12 py-12 flex flex-col gap-6">
<div className="max-w-xl flex flex-col gap-3">
<h2 className="text-3xl font-bold tracking-tight text-black sm:text-4xl">
{block.heading}
</h2>
<p className="text-lg leading-8 text-gray-600">{block.text}</p>
<section className={isDark ? 'relative dark dark:bg-black' : 'relative dark:bg-black'}>
<div className="absolute inset-0 bg-size-[5px] bg-[url(/images/tile-1-black.png)] dark:bg-[url(/images/tile-1-white.png)] opacity-25" />
<div className="container relative">
<div className="grid lg:grid-cols-2 gap-12 py-12">
<div
className={`${isImageFirst && image ? 'row-start-2 lg:row-start-1 lg:col-start-2' : ''} flex flex-col gap-2 `}
>
{eyebrow && (
<span className="text-sm uppercase dark:text-white font-mono tracking-tight opacity-70">
{eyebrow}
</span>
)}
{heading && (
<h2 className="text-2xl md:text-3xl lg:text-4xl dark:text-white">{heading}</h2>
)}
{body && (
<div className="lg:text-left">
<PortableText value={body as PortableTextBlock[]} className="dark:prose-invert" />
</div>
)}

{button?.buttonText && button?.link && (
<div className="flex mt-4">
<ResolvedLink
link={button?.link}
className="rounded-full flex gap-2 font-mono text-sm whitespace-nowrap items-center bg-black dark:bg-white hover:bg-blue focus:bg-blue py-3 px-6 text-white dark:text-black dark:hover:text-white transition-colors duration-200"
>
{button?.buttonText}
</ResolvedLink>
</div>
)}
</div>

<Suspense fallback={null}>
<div className="flex items-center gap-x-6 lg:mt-0 lg:flex-shrink-0">
<ResolvedLink
link={block.link}
className="rounded-full flex gap-2 mr-6 items-center bg-black hover:bg-blue focus:bg-blue py-3 px-6 text-white transition-colors duration-200"
>
{block.buttonText}
</ResolvedLink>
</div>
</Suspense>
{image?.asset?._ref && (
<Image
id={image.asset._ref}
alt="Demo image"
width={704}
crop={image.crop}
mode="cover"
className="rounded-sm"
/>
)}
</div>
</div>
</div>
</section>
)
}
11 changes: 7 additions & 4 deletions frontend/app/components/InfoSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@ import PortableText from '@/app/components/PortableText'
import {InfoSection} from '@/sanity.types'

type InfoProps = {
block: InfoSection
index: number
}
block: InfoSection;
index: number;
// Needed if you want to createDataAttributes to do non-text overlays in Presentation (Visual Editing)
pageId: string;
pageType: string;
};

export default function CTA({block}: InfoProps) {
return (
<div className="container my-12">
<div className="max-w-3xl">
{block?.heading && (
<h2 className="text-2xl md:text-3xl lg:text-4xl font-bold">{block.heading}</h2>
<h2 className="text-2xl md:text-3xl lg:text-4xl">{block.heading}</h2>
)}
{block?.subheading && (
<span className="block mt-4 mb-8 text-lg uppercase font-light text-gray-900/70">
Expand Down
58 changes: 27 additions & 31 deletions frontend/app/components/PageBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,16 @@

import {SanityDocument} from 'next-sanity'
import {useOptimistic} from 'next-sanity/hooks'
import Link from 'next/link'

import BlockRenderer from '@/app/components/BlockRenderer'
import {GetPageQueryResult} from '@/sanity.types'
import {dataAttr} from '@/sanity/lib/utils'
import {studioUrl} from '@/sanity/lib/api'
import {PageBuilderSection} from '@/sanity/lib/types'

type PageBuilderPageProps = {
page: GetPageQueryResult
}

type PageBuilderSection = {
_key: string
_type: string
}

type PageData = {
_id: string
_type: string
Expand All @@ -28,7 +22,13 @@ type PageData = {
* The PageBuilder component is used to render the blocks from the `pageBuilder` field in the Page type in your Sanity Studio.
*/

function renderSections(pageBuilderSections: PageBuilderSection[], page: GetPageQueryResult) {
function RenderSections({
pageBuilderSections,
page,
}: {
pageBuilderSections: PageBuilderSection[]
page: GetPageQueryResult
}) {
if (!page) {
return null
}
Expand All @@ -40,7 +40,7 @@ function renderSections(pageBuilderSections: PageBuilderSection[], page: GetPage
path: `pageBuilder`,
}).toString()}
>
{pageBuilderSections.map((block: any, index: number) => (
{pageBuilderSections.map((block: PageBuilderSection, index: number) => (
<BlockRenderer
key={block._key}
index={index}
Expand All @@ -53,25 +53,23 @@ function renderSections(pageBuilderSections: PageBuilderSection[], page: GetPage
)
}

function renderEmptyState(page: GetPageQueryResult) {
function RenderEmptyState({page}: {page: GetPageQueryResult}) {
if (!page) {
return null
}

return (
<div className="container">
<h1 className="text-4xl font-extrabold text-gray-900 tracking-tight sm:text-5xl">
This page has no content!
</h1>
<p className="mt-2 text-base text-gray-500">Open the page in Sanity Studio to add content.</p>
<div className="mt-10 flex">
<Link
className="rounded-full flex gap-2 mr-6 items-center bg-black hover:bg-brand focus:bg-blue py-3 px-6 text-white transition-colors duration-200"
href={`${studioUrl}/structure/intent/edit/template=page;type=page;path=pageBuilder;id=${page._id}`}
target="_blank"
rel="noopener noreferrer"
>
Add content to this page
</Link>
<div
className="container mt-10"
data-sanity={dataAttr({
id: page._id,
type: 'page',
path: `pageBuilder`,
}).toString()}
>
<div className="prose">
<h2 className="">This page has no content!</h2>
<p className="">Open the page in Sanity Studio to add content.</p>
</div>
</div>
)
Expand Down Expand Up @@ -102,11 +100,9 @@ export default function PageBuilder({page}: PageBuilderPageProps) {
return currentSections
})

if (!page) {
return renderEmptyState(page)
}

return pageBuilderSections && pageBuilderSections.length > 0
? renderSections(pageBuilderSections, page)
: renderEmptyState(page)
return pageBuilderSections && pageBuilderSections.length > 0 ? (
<RenderSections pageBuilderSections={pageBuilderSections} page={page} />
) : (
<RenderEmptyState page={page} />
)
}
Loading