diff --git a/src/app/(pages)/authors/[slug]/page.tsx b/src/app/(pages)/authors/[slug]/page.tsx index f94606c..3a48386 100644 --- a/src/app/(pages)/authors/[slug]/page.tsx +++ b/src/app/(pages)/authors/[slug]/page.tsx @@ -9,6 +9,7 @@ import { Header } from "../../../_components/Header"; import { fetchContentBySlug } from "@/app/_utilities/contentFetchers"; import { Author } from "@/payload-types"; import { Metadata } from "next"; +import { generateMeta } from "@/utilities/generateMeta"; export const dynamic = 'force-dynamic' @@ -43,9 +44,7 @@ export async function generateMetadata({ params: paramsPromise}): Promise {// @ts-ignore - blogpost.relatedPosts?.length > 0 && + blogpost.related?.length > 0 && } @@ -68,13 +69,11 @@ export default async function BlogpostPage({ params: paramsPromise }) { export async function generateMetadata({ params: paramsPromise}): Promise { const { slug } = await paramsPromise - const blogpost = await fetchContentBySlug({ + const post = await fetchContentBySlug({ slug: slug, type: "blogposts", }) + // @ts-ignore + return generateMeta({doc: post}) - return { - // @ts-ignore - title: `Subvisual | ${blogpost.title}` - } } diff --git a/src/app/(pages)/case-studies/[slug]/page.tsx b/src/app/(pages)/case-studies/[slug]/page.tsx index 2e5139d..d71260a 100644 --- a/src/app/(pages)/case-studies/[slug]/page.tsx +++ b/src/app/(pages)/case-studies/[slug]/page.tsx @@ -3,6 +3,10 @@ // // import { fetchDoc } from '../../../_api/fetchDoc' +import { Metadata } from "next"; +import { fetchContentBySlug } from "@/app/_utilities/contentFetchers"; +import { generateMeta } from "@/utilities/generateMeta"; + export const dynamic = 'force-dynamic' export default async function CaseStudiesPage() { @@ -25,3 +29,13 @@ export default async function CaseStudiesPage() { ) } + +export async function generateMetadata({ params: paramsPromise}): Promise { + const { slug } = await paramsPromise + const caseStudy = await fetchContentBySlug({ + slug: slug, + type: "podcasts", + }) + // @ts-ignore + return generateMeta({doc: caseStudy}) +} diff --git a/src/app/(pages)/podcasts/[slug]/page.tsx b/src/app/(pages)/podcasts/[slug]/page.tsx index 125b0b0..dffda51 100644 --- a/src/app/(pages)/podcasts/[slug]/page.tsx +++ b/src/app/(pages)/podcasts/[slug]/page.tsx @@ -8,6 +8,7 @@ import { Subscribe } from '../../../_blocks/Subscribe' import { Header } from '@/app/_components/Header' import { fetchContentBySlug } from "@/app/_utilities/contentFetchers"; import { Metadata } from "next"; +import { generateMeta } from "@/utilities/generateMeta"; export const dynamic = 'force-dynamic' @@ -33,7 +34,8 @@ export default async function PodcastEpisodesPage({params: paramsPromise}) {
- + {/* @ts-ignore */} + {episode.related?.length > 0 && } ) @@ -41,14 +43,10 @@ export default async function PodcastEpisodesPage({params: paramsPromise}) { export async function generateMetadata({ params: paramsPromise}): Promise { const { slug } = await paramsPromise - const podcast = await fetchContentBySlug({ + const episode = await fetchContentBySlug({ slug: slug, type: "podcasts", }) - - return { - // @ts-ignore - title: `Subvisual | ${podcast.title}`, - - } + // @ts-ignore + return generateMeta({doc: episode}) } diff --git a/src/app/(pages)/talks-and-roundtables/[slug]/page.tsx b/src/app/(pages)/talks-and-roundtables/[slug]/page.tsx index 9610fb0..ded374b 100644 --- a/src/app/(pages)/talks-and-roundtables/[slug]/page.tsx +++ b/src/app/(pages)/talks-and-roundtables/[slug]/page.tsx @@ -2,6 +2,10 @@ // // import { fetchDoc } from '../../../_api/fetchDoc' +import { Metadata } from "next"; +import { fetchContentBySlug } from "@/app/_utilities/contentFetchers"; +import { generateMeta } from "@/utilities/generateMeta"; + export const dynamic = 'force-dynamic' export default async function TalksAndRoundTablesPage() { @@ -25,3 +29,13 @@ export default async function TalksAndRoundTablesPage() { ) } + +export async function generateMetadata({ params: paramsPromise}): Promise { + const { slug } = await paramsPromise + const talk = await fetchContentBySlug({ + slug: slug, + type: "talks-and-roundtables", + }) + // @ts-ignore + return generateMeta({doc: talk}) +} diff --git a/src/app/(payload)/admin/importMap.js b/src/app/(payload)/admin/importMap.js index 6e49013..c7b684b 100644 --- a/src/app/(payload)/admin/importMap.js +++ b/src/app/(payload)/admin/importMap.js @@ -3,19 +3,19 @@ import { RichTextField as RichTextField_1 } from '@payloadcms/richtext-lexical/c import { getGenerateComponentMap as getGenerateComponentMap_2 } from '@payloadcms/richtext-lexical/generateComponentMap' import { InlineToolbarFeatureClient as InlineToolbarFeatureClient_3 } from '@payloadcms/richtext-lexical/client' import { FixedToolbarFeatureClient as FixedToolbarFeatureClient_4 } from '@payloadcms/richtext-lexical/client' -import { HeadingFeatureClient as HeadingFeatureClient_5 } from '@payloadcms/richtext-lexical/client' -import { UnderlineFeatureClient as UnderlineFeatureClient_6 } from '@payloadcms/richtext-lexical/client' -import { BoldFeatureClient as BoldFeatureClient_7 } from '@payloadcms/richtext-lexical/client' -import { ItalicFeatureClient as ItalicFeatureClient_8 } from '@payloadcms/richtext-lexical/client' -import { LinkFeatureClient as LinkFeatureClient_9 } from '@payloadcms/richtext-lexical/client' +import { UnderlineFeatureClient as UnderlineFeatureClient_5 } from '@payloadcms/richtext-lexical/client' +import { BoldFeatureClient as BoldFeatureClient_6 } from '@payloadcms/richtext-lexical/client' +import { ItalicFeatureClient as ItalicFeatureClient_7 } from '@payloadcms/richtext-lexical/client' +import { LinkFeatureClient as LinkFeatureClient_8 } from '@payloadcms/richtext-lexical/client' +import { SlugComponent as SlugComponent_9 } from '@/fields/slug/SlugComponent' import { OverviewComponent as OverviewComponent_10 } from '@payloadcms/plugin-seo/client' import { MetaTitleComponent as MetaTitleComponent_11 } from '@payloadcms/plugin-seo/client' import { MetaImageComponent as MetaImageComponent_12 } from '@payloadcms/plugin-seo/client' import { MetaDescriptionComponent as MetaDescriptionComponent_13 } from '@payloadcms/plugin-seo/client' import { PreviewComponent as PreviewComponent_14 } from '@payloadcms/plugin-seo/client' -import { SlugComponent as SlugComponent_15 } from '@/fields/slug/SlugComponent' -import { HorizontalRuleFeatureClient as HorizontalRuleFeatureClient_16 } from '@payloadcms/richtext-lexical/client' -import { BlocksFeatureClient as BlocksFeatureClient_17 } from '@payloadcms/richtext-lexical/client' +import { HorizontalRuleFeatureClient as HorizontalRuleFeatureClient_15 } from '@payloadcms/richtext-lexical/client' +import { BlocksFeatureClient as BlocksFeatureClient_16 } from '@payloadcms/richtext-lexical/client' +import { HeadingFeatureClient as HeadingFeatureClient_17 } from '@payloadcms/richtext-lexical/client' import { UploadFeatureClient as UploadFeatureClient_18 } from '@payloadcms/richtext-lexical/client' import { BlockquoteFeatureClient as BlockquoteFeatureClient_19 } from '@payloadcms/richtext-lexical/client' import { RelationshipFeatureClient as RelationshipFeatureClient_20 } from '@payloadcms/richtext-lexical/client' @@ -29,9 +29,8 @@ import { InlineCodeFeatureClient as InlineCodeFeatureClient_27 } from '@payloadc import { SuperscriptFeatureClient as SuperscriptFeatureClient_28 } from '@payloadcms/richtext-lexical/client' import { SubscriptFeatureClient as SubscriptFeatureClient_29 } from '@payloadcms/richtext-lexical/client' import { StrikethroughFeatureClient as StrikethroughFeatureClient_30 } from '@payloadcms/richtext-lexical/client' -import { LinkToDoc as LinkToDoc_31 } from '@payloadcms/plugin-search/client' -import { default as default_32 } from '@/components/BeforeDashboard' -import { default as default_33 } from '@/components/BeforeLogin' +import { default as default_31 } from '@/components/BeforeDashboard' +import { default as default_32 } from '@/components/BeforeLogin' export const importMap = { "@payloadcms/richtext-lexical/client#RichTextCell": RichTextCell_0, @@ -39,19 +38,19 @@ export const importMap = { "@payloadcms/richtext-lexical/generateComponentMap#getGenerateComponentMap": getGenerateComponentMap_2, "@payloadcms/richtext-lexical/client#InlineToolbarFeatureClient": InlineToolbarFeatureClient_3, "@payloadcms/richtext-lexical/client#FixedToolbarFeatureClient": FixedToolbarFeatureClient_4, - "@payloadcms/richtext-lexical/client#HeadingFeatureClient": HeadingFeatureClient_5, - "@payloadcms/richtext-lexical/client#UnderlineFeatureClient": UnderlineFeatureClient_6, - "@payloadcms/richtext-lexical/client#BoldFeatureClient": BoldFeatureClient_7, - "@payloadcms/richtext-lexical/client#ItalicFeatureClient": ItalicFeatureClient_8, - "@payloadcms/richtext-lexical/client#LinkFeatureClient": LinkFeatureClient_9, + "@payloadcms/richtext-lexical/client#UnderlineFeatureClient": UnderlineFeatureClient_5, + "@payloadcms/richtext-lexical/client#BoldFeatureClient": BoldFeatureClient_6, + "@payloadcms/richtext-lexical/client#ItalicFeatureClient": ItalicFeatureClient_7, + "@payloadcms/richtext-lexical/client#LinkFeatureClient": LinkFeatureClient_8, + "@/fields/slug/SlugComponent#SlugComponent": SlugComponent_9, "@payloadcms/plugin-seo/client#OverviewComponent": OverviewComponent_10, "@payloadcms/plugin-seo/client#MetaTitleComponent": MetaTitleComponent_11, "@payloadcms/plugin-seo/client#MetaImageComponent": MetaImageComponent_12, "@payloadcms/plugin-seo/client#MetaDescriptionComponent": MetaDescriptionComponent_13, "@payloadcms/plugin-seo/client#PreviewComponent": PreviewComponent_14, - "@/fields/slug/SlugComponent#SlugComponent": SlugComponent_15, - "@payloadcms/richtext-lexical/client#HorizontalRuleFeatureClient": HorizontalRuleFeatureClient_16, - "@payloadcms/richtext-lexical/client#BlocksFeatureClient": BlocksFeatureClient_17, + "@payloadcms/richtext-lexical/client#HorizontalRuleFeatureClient": HorizontalRuleFeatureClient_15, + "@payloadcms/richtext-lexical/client#BlocksFeatureClient": BlocksFeatureClient_16, + "@payloadcms/richtext-lexical/client#HeadingFeatureClient": HeadingFeatureClient_17, "@payloadcms/richtext-lexical/client#UploadFeatureClient": UploadFeatureClient_18, "@payloadcms/richtext-lexical/client#BlockquoteFeatureClient": BlockquoteFeatureClient_19, "@payloadcms/richtext-lexical/client#RelationshipFeatureClient": RelationshipFeatureClient_20, @@ -65,7 +64,6 @@ export const importMap = { "@payloadcms/richtext-lexical/client#SuperscriptFeatureClient": SuperscriptFeatureClient_28, "@payloadcms/richtext-lexical/client#SubscriptFeatureClient": SubscriptFeatureClient_29, "@payloadcms/richtext-lexical/client#StrikethroughFeatureClient": StrikethroughFeatureClient_30, - "@payloadcms/plugin-search/client#LinkToDoc": LinkToDoc_31, - "@/components/BeforeDashboard#default": default_32, - "@/components/BeforeLogin#default": default_33 + "@/components/BeforeDashboard#default": default_31, + "@/components/BeforeLogin#default": default_32 } diff --git a/src/app/(payload)/custom.scss b/src/app/(payload)/custom.scss index e69de29..04de43b 100644 --- a/src/app/(payload)/custom.scss +++ b/src/app/(payload)/custom.scss @@ -0,0 +1,19 @@ +.rich-text-lexical * { + max-width: 600px; + margin: 0 auto; + line-height: 2em; +} + +.rich-text-lexical h1 { + font-size: var(--size-40); +} + +.rich-text-lexical h2, +.rich-text-lexical h3, +.rich-text-lexical h4, +.rich-text-lexical h5, +.rich-text-lexical h6 { + font-size: var(--size-28); + +} + diff --git a/src/app/_blocks/AuthorContentGrid/index.tsx b/src/app/_blocks/AuthorContentGrid/index.tsx index e050fef..374c4ea 100644 --- a/src/app/_blocks/AuthorContentGrid/index.tsx +++ b/src/app/_blocks/AuthorContentGrid/index.tsx @@ -8,7 +8,7 @@ import ContentCard from "../../_components/ContentCard"; import { calculateTotalArticles } from "../../_utilities/calculateTotalArticles"; import styles from "./styles.module.css"; -export default function AuthorContentGrid({content}) { +export default function AuthorContentGrid({ content }) { return (
@@ -18,9 +18,7 @@ export default function AuthorContentGrid({content}) {
{Object.keys(content).map(key => content[key].map((contentPiece, i) => ( -
- -
+ )), )}
diff --git a/src/app/_blocks/BlogpostContent/styles.module.css b/src/app/_blocks/BlogpostContent/styles.module.css index 2835055..c72292b 100644 --- a/src/app/_blocks/BlogpostContent/styles.module.css +++ b/src/app/_blocks/BlogpostContent/styles.module.css @@ -51,7 +51,10 @@ @media (min-width: 1024px) { - .content h1, + .content h1 { + font-size: var(--size-40); + } + .content h2, .content h3, .content h4, diff --git a/src/app/_blocks/EpisodeContent/Contributors/index.tsx b/src/app/_blocks/EpisodeContent/Contributors/index.tsx index 9b2aa3a..3856085 100644 --- a/src/app/_blocks/EpisodeContent/Contributors/index.tsx +++ b/src/app/_blocks/EpisodeContent/Contributors/index.tsx @@ -1,15 +1,16 @@ import { AuthorPill } from "@/app/_components/AuthorPill"; - +import SocialLinks from "@/app/_components/SocialLinks" export default function Contributors({ className, authors }) { return (
-

CONTRIBUTORS

+

CONTRIBUTORS

{authors.map((author, i) => (
{/* Author has to be passed as an Array for compatibility reasons */} +
))}
- ); + ) } diff --git a/src/app/_blocks/EpisodeContent/index.tsx b/src/app/_blocks/EpisodeContent/index.tsx index 6842ebe..c3afc37 100644 --- a/src/app/_blocks/EpisodeContent/index.tsx +++ b/src/app/_blocks/EpisodeContent/index.tsx @@ -33,7 +33,7 @@ export default function EpisodeContent({ episode }) {

- + {categories && }
diff --git a/src/app/_blocks/HubContentGrid/index.tsx b/src/app/_blocks/HubContentGrid/index.tsx index 64e5823..fbc6d48 100644 --- a/src/app/_blocks/HubContentGrid/index.tsx +++ b/src/app/_blocks/HubContentGrid/index.tsx @@ -46,11 +46,7 @@ export default function HubContentGrid({ content }) {
{filteredContent.map((article, i) => (
- +
))}
diff --git a/src/app/_blocks/HubHead/Highlights/index.tsx b/src/app/_blocks/HubHead/Highlights/index.tsx index d1df017..103eb1f 100644 --- a/src/app/_blocks/HubHead/Highlights/index.tsx +++ b/src/app/_blocks/HubHead/Highlights/index.tsx @@ -36,7 +36,7 @@ export async function Highlights({ content, main }) {
- {categories.map((category, i) => )} + {categories && categories.map((category, i) => )} diff --git a/src/app/_blocks/RelatedContent/index.tsx b/src/app/_blocks/RelatedContent/index.tsx index 3faa2eb..0474132 100644 --- a/src/app/_blocks/RelatedContent/index.tsx +++ b/src/app/_blocks/RelatedContent/index.tsx @@ -5,6 +5,7 @@ import styles from "./styles.module.css"; import ContentCard from "@/app/_components/ContentCard"; export function RelatedContent({ content }) { + return (
@@ -19,10 +20,8 @@ export function RelatedContent({ content }) { }} > {content.related.map((contentPiece, i) => ( -
- -
- {contentPiece.title} +
+
))}
diff --git a/src/app/_components/AuthorSummary/index.tsx b/src/app/_components/AuthorSummary/index.tsx index 227304a..ac34906 100644 --- a/src/app/_components/AuthorSummary/index.tsx +++ b/src/app/_components/AuthorSummary/index.tsx @@ -6,10 +6,10 @@ import styles from "./styles.module.css"; import { Author } from "@/payload-types"; export default function AuthorSummary({ author }) { - const { name, role, bio, linkedIn, gitHub, medium, x, featuredImage } = author; + const { authorName, role, bio, linkedIn, gitHub, medium, x, featuredImage, socials } = author; // TODO: Convert this to an array with names in collection config - const socials = [linkedIn, gitHub, medium, x].filter(Boolean); + return (
@@ -20,7 +20,7 @@ export default function AuthorSummary({ author }) {
}
-
{name}
+
{authorName}

{role}

diff --git a/src/app/_components/Authors/index.tsx b/src/app/_components/Authors/index.tsx deleted file mode 100644 index 2e2da5b..0000000 --- a/src/app/_components/Authors/index.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from "react"; -import styles from "./AuthorPills/styles.module.css"; - - -import FeaturedImage from "@/app/_components/FeaturedImage"; -import { LinkedInIcon, TwitterIcon } from "@/app/_icons/socialIcons"; -import Image from "next/image"; -import { TwoAuthorPill } from "@/app/_components/Authors/AuthorPills"; -import { VariousAuthorsPill } from "@/app/_components/Authors/AuthorPills"; -import { AuthorPill } from "@/app/_components/Authors/AuthorPills"; -export default function Authors({ authors }) { - - - return ( -
- {authors.length === 1 && } - {authors.length === 2 && } - {authors.length > 2 && } -
- - - ); -} diff --git a/src/app/_components/ContentCard/index.tsx b/src/app/_components/ContentCard/index.tsx index b1dae61..a15a171 100644 --- a/src/app/_components/ContentCard/index.tsx +++ b/src/app/_components/ContentCard/index.tsx @@ -16,12 +16,12 @@ import ArchiveButton from "../ArchiveButton"; import CategoryPill from "../CategoryPill"; import FeaturedImage from "../FeaturedImage"; import styles from "./styles.module.css"; -import Authors from "@/app/_components/Authors"; import { AuthorPill } from "@/app/_components/AuthorPill"; interface ContentSummaryProps { contentType: string; - content: Blogpost | Podcast | CaseStudy | TalksAndRoundtable; // TODO: Extend to CaseStudy and TalksAndRoundTables once consistency is assured + content: Blogpost | Podcast | CaseStudy | TalksAndRoundtable; + rounded: boolean; } const archiveMap = { @@ -31,17 +31,19 @@ const archiveMap = { TalksAndRoundtables: "talks-and-roundtables", }; -export default function ContentCard({ contentType, content }: ContentSummaryProps) { +export default function ContentCard({ contentType, content, rounded }: ContentSummaryProps) { const { slug, title, summary, featuredImage, categories, publishedAt, authors } = content; + const borderStyle = { + '--dynamic-border': rounded ? '45px' : '' + } // todo: convert to a collection item property const readTime = estimateReadTime("herpaderpa"); return ( -
- - + // @ts-ignore +
{featuredImage && ( diff --git a/src/app/_components/ContentCard/styles.module.css b/src/app/_components/ContentCard/styles.module.css index 44db90d..59d38a4 100644 --- a/src/app/_components/ContentCard/styles.module.css +++ b/src/app/_components/ContentCard/styles.module.css @@ -2,6 +2,7 @@ padding: 30px 20px; height: min-content; border: 1px solid var(--dark-rock-800); + border-radius: var(--dynamic-border); } .contentMetaContainer { diff --git a/src/app/_components/Header/index.tsx b/src/app/_components/Header/index.tsx index 4ab765b..ebedb02 100644 --- a/src/app/_components/Header/index.tsx +++ b/src/app/_components/Header/index.tsx @@ -5,9 +5,9 @@ import Logo from "@/app/_components/Header/Logo"; import { fetchGlobals } from "@/app/_utilities/contentFetchers"; const defaultStyle = { - '--dynamic-background': "transparent", - '--dynamic-color': "var(--dark-rock-800)", - '--dynamic-width': '100%' + "--dynamic-background": "transparent", + "--dynamic-color": "var(--dark-rock-800)", + "--dynamic-width": "100%", }; export async function Header({ style = defaultStyle }) { @@ -22,7 +22,7 @@ export async function Header({ style = defaultStyle }) { {/* @ts-ignore */}
- +
{/* TODO: Conditionally format Content Hub to reflect active link?*/} diff --git a/src/app/_components/PostSummary/index.tsx b/src/app/_components/PostSummary/index.tsx index 81cdb47..351c951 100644 --- a/src/app/_components/PostSummary/index.tsx +++ b/src/app/_components/PostSummary/index.tsx @@ -4,7 +4,7 @@ import { formatDateTime } from "../../_utilities/formatDateTime"; import ArchiveButton from "../ArchiveButton"; import styles from "./styles.module.css"; -import Authors from '@/app/_components/Authors' +import SocialLinks from "@/app/_components/SocialLinks"; import { AuthorPill } from "@/app/_components/AuthorPill"; export default function PostSummary({ post }) { @@ -26,7 +26,7 @@ export default function PostSummary({ post }) {

WRITTEN BY

- Social Links + { authors.length === 1 && }
); diff --git a/src/app/_components/PostSummary/styles.module.css b/src/app/_components/PostSummary/styles.module.css index 17cc77f..1047635 100644 --- a/src/app/_components/PostSummary/styles.module.css +++ b/src/app/_components/PostSummary/styles.module.css @@ -33,6 +33,11 @@ flex-direction: column; padding-left: 16px; gap: 20px; + color: var(--soft-white-100); +} + +.authorInfo a { + color: var(--soft-white-100); } .authorBio { diff --git a/src/app/_components/SocialLinks/index.tsx b/src/app/_components/SocialLinks/index.tsx index b6d1e47..524d7aa 100644 --- a/src/app/_components/SocialLinks/index.tsx +++ b/src/app/_components/SocialLinks/index.tsx @@ -1,15 +1,35 @@ import Link from 'next/link' -import { LinkedInIcon } from '../../_icons/socialIcons' +import { LinkedInIcon, TwitterIcon } from "../../_icons/socialIcons"; export default function SocialLinks({ socials }) { return (
- {socials.map((link, i) => ( - + {socials.linkedIn.length === 1 && ( + - ))} + )} + + {socials.x.length === 1 && ( + + + + )} + + {socials.medium.length === 1 && ( + + + + )} + + {socials.github.length === 1 && ( + + + + )} + +
) } diff --git a/src/app/_icons/socialIcons.tsx b/src/app/_icons/socialIcons.tsx index 2120ace..4d88e89 100644 --- a/src/app/_icons/socialIcons.tsx +++ b/src/app/_icons/socialIcons.tsx @@ -1,10 +1,10 @@ export function LinkedInIcon() { return ( - + ); @@ -13,10 +13,10 @@ export function LinkedInIcon() { export function TwitterIcon() { return ( - + ); @@ -25,10 +25,10 @@ export function TwitterIcon() { export function FacebookIcon() { return ( - + ); diff --git a/src/app/_utilities/contentFetchers.ts b/src/app/_utilities/contentFetchers.ts index 8ef199a..6edfaa4 100644 --- a/src/app/_utilities/contentFetchers.ts +++ b/src/app/_utilities/contentFetchers.ts @@ -3,6 +3,7 @@ import configPromise from "@payload-config"; import type { Author, Blogpost, CaseStudy, Config, Media, Podcast, TalksAndRoundtable } from "@/payload-types"; import { notFound } from "next/navigation"; import { CollectionSlug, getPayload } from "payload"; +import { draftMode } from "next/headers"; interface FetcherArgs { collection?: CollectionSlug, @@ -33,10 +34,15 @@ export async function fetchContentBySlug({ slug, type, depth }: { slug: string, throw new Error("Must input slug and/or type."); } + const { isEnabled: draft } = await draftMode() + const query = { slug: { equals: slug } }; return await fetcher({ collection: type, + draft: draft, + overrideAccess: draft, + limit: 1, depth: depth, query: query, }).then(res => { diff --git a/src/blocks/ArchiveBlock/Component.tsx b/src/blocks/ArchiveBlock/Component.tsx deleted file mode 100644 index fdfbd1b..0000000 --- a/src/blocks/ArchiveBlock/Component.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import type { Post, ArchiveBlock as ArchiveBlockProps } from '@/payload-types' - -import configPromise from '@payload-config' -import { getPayloadHMR } from '@payloadcms/next/utilities' -import React from 'react' -import RichText from '@/components/RichText' - -import { CollectionArchive } from '@/components/CollectionArchive' - -export const ArchiveBlock: React.FC< - ArchiveBlockProps & { - id?: string - } -> = async (props) => { - const { id, categories, introContent, limit: limitFromProps, populateBy, selectedDocs } = props - - const limit = limitFromProps || 3 - - let posts: Post[] = [] - - if (populateBy === 'collection') { - const payload = await getPayloadHMR({ config: configPromise }) - - const flattenedCategories = categories?.map((category) => { - if (typeof category === 'object') return category.id - else return category - }) - - const fetchedPosts = await payload.find({ - collection: 'posts', - depth: 1, - limit, - ...(flattenedCategories && flattenedCategories.length > 0 - ? { - where: { - categories: { - in: flattenedCategories, - }, - }, - } - : {}), - }) - - posts = fetchedPosts.docs - } else { - if (selectedDocs?.length) { - const filteredSelectedPosts = selectedDocs.map((post) => { - if (typeof post.value === 'object') return post.value - }) as Post[] - - posts = filteredSelectedPosts - } - } - - return ( -
- {introContent && ( -
- -
- )} - -
- ) -} diff --git a/src/blocks/ArchiveBlock/config.ts b/src/blocks/ArchiveBlock/config.ts deleted file mode 100644 index f87a376..0000000 --- a/src/blocks/ArchiveBlock/config.ts +++ /dev/null @@ -1,94 +0,0 @@ -import type { Block } from 'payload' - -import { - FixedToolbarFeature, - HeadingFeature, - InlineToolbarFeature, - lexicalEditor, -} from '@payloadcms/richtext-lexical' - -export const Archive: Block = { - slug: 'archive', - interfaceName: 'ArchiveBlock', - fields: [ - { - name: 'introContent', - type: 'richText', - editor: lexicalEditor({ - features: ({ rootFeatures }) => { - return [ - ...rootFeatures, - HeadingFeature({ enabledHeadingSizes: ['h1', 'h2', 'h3', 'h4'] }), - FixedToolbarFeature(), - InlineToolbarFeature(), - ] - }, - }), - label: 'Intro Content', - }, - { - name: 'populateBy', - type: 'select', - defaultValue: 'collection', - options: [ - { - label: 'Collection', - value: 'collection', - }, - { - label: 'Individual Selection', - value: 'selection', - }, - ], - }, - { - name: 'relationTo', - type: 'select', - admin: { - condition: (_, siblingData) => siblingData.populateBy === 'collection', - }, - defaultValue: 'posts', - label: 'Collections To Show', - options: [ - { - label: 'Posts', - value: 'posts', - }, - ], - }, - { - name: 'categories', - type: 'relationship', - admin: { - condition: (_, siblingData) => siblingData.populateBy === 'collection', - }, - hasMany: true, - label: 'Categories To Show', - relationTo: 'categories', - }, - { - name: 'limit', - type: 'number', - admin: { - condition: (_, siblingData) => siblingData.populateBy === 'collection', - step: 1, - }, - defaultValue: 10, - label: 'Limit', - }, - { - name: 'selectedDocs', - type: 'relationship', - admin: { - condition: (_, siblingData) => siblingData.populateBy === 'selection', - }, - hasMany: true, - label: 'Selection', - relationTo: ['posts'], - }, - ], - labels: { - plural: 'Archives', - singular: 'Archive', - }, -} diff --git a/src/blocks/Banner/Component.tsx b/src/blocks/Banner/Component.tsx deleted file mode 100644 index 143596c..0000000 --- a/src/blocks/Banner/Component.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import type { BannerBlock as BannerBlockProps } from 'src/payload-types' - -import { cn } from 'src/utilities/cn' -import React from 'react' -import RichText from '@/components/RichText' - -type Props = { - className?: string -} & BannerBlockProps - -export const BannerBlock: React.FC = ({ className, content, style }) => { - return ( -
-
- -
-
- ) -} diff --git a/src/blocks/Banner/config.ts b/src/blocks/Banner/config.ts deleted file mode 100644 index 53e46b5..0000000 --- a/src/blocks/Banner/config.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { Block } from 'payload' - -import { - FixedToolbarFeature, - InlineToolbarFeature, - lexicalEditor, -} from '@payloadcms/richtext-lexical' - -export const Banner: Block = { - slug: 'banner', - fields: [ - { - name: 'style', - type: 'select', - defaultValue: 'info', - options: [ - { label: 'Info', value: 'info' }, - { label: 'Warning', value: 'warning' }, - { label: 'Error', value: 'error' }, - { label: 'Success', value: 'success' }, - ], - required: true, - }, - { - name: 'content', - type: 'richText', - editor: lexicalEditor({ - features: ({ rootFeatures }) => { - return [...rootFeatures, FixedToolbarFeature(), InlineToolbarFeature()] - }, - }), - label: false, - required: true, - }, - ], - interfaceName: 'BannerBlock', -} diff --git a/src/blocks/CallToAction/Component.tsx b/src/blocks/CallToAction/Component.tsx deleted file mode 100644 index 776ba84..0000000 --- a/src/blocks/CallToAction/Component.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react' - -import type { Page } from '@/payload-types' - -import RichText from '@/components/RichText' -import { CMSLink } from '@/components/Link' - -type Props = Extract - -export const CallToActionBlock: React.FC< - Props & { - id?: string - } -> = ({ links, richText }) => { - return ( -
-
-
- {richText && } -
-
- {(links || []).map(({ link }, i) => { - return - })} -
-
-
- ) -} diff --git a/src/blocks/CallToAction/config.ts b/src/blocks/CallToAction/config.ts deleted file mode 100644 index f4ffa77..0000000 --- a/src/blocks/CallToAction/config.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { Block } from 'payload' - -import { - FixedToolbarFeature, - HeadingFeature, - InlineToolbarFeature, - lexicalEditor, -} from '@payloadcms/richtext-lexical' - -import { linkGroup } from '../../fields/linkGroup' - -export const CallToAction: Block = { - slug: 'cta', - interfaceName: 'CallToActionBlock', - fields: [ - { - name: 'richText', - type: 'richText', - editor: lexicalEditor({ - features: ({ rootFeatures }) => { - return [ - ...rootFeatures, - HeadingFeature({ enabledHeadingSizes: ['h1', 'h2', 'h3', 'h4'] }), - FixedToolbarFeature(), - InlineToolbarFeature(), - ] - }, - }), - label: false, - }, - linkGroup({ - appearances: ['default', 'outline'], - overrides: { - maxRows: 2, - }, - }), - ], - labels: { - plural: 'Calls to Action', - singular: 'Call to Action', - }, -} diff --git a/src/blocks/Content/Component.tsx b/src/blocks/Content/Component.tsx deleted file mode 100644 index a09232b..0000000 --- a/src/blocks/Content/Component.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { cn } from 'src/utilities/cn' -import React from 'react' -import RichText from '@/components/RichText' - -import type { Page } from '@/payload-types' - -import { CMSLink } from '../../components/Link' - -type Props = Extract - -export const ContentBlock: React.FC< - { - id?: string - } & Props -> = (props) => { - const { columns } = props - - const colsSpanClasses = { - full: '12', - half: '6', - oneThird: '4', - twoThirds: '8', - } - - return ( -
-
- {columns && - columns.length > 0 && - columns.map((col, index) => { - const { enableLink, link, richText, size } = col - - return ( -
- {richText && } - - {enableLink && } -
- ) - })} -
-
- ) -} diff --git a/src/blocks/Content/config.ts b/src/blocks/Content/config.ts deleted file mode 100644 index 01c22ed..0000000 --- a/src/blocks/Content/config.ts +++ /dev/null @@ -1,74 +0,0 @@ -import type { Block, Field } from 'payload' - -import { - FixedToolbarFeature, - HeadingFeature, - InlineToolbarFeature, - lexicalEditor, -} from '@payloadcms/richtext-lexical' - -import { link } from '@/fields/link' - -const columnFields: Field[] = [ - { - name: 'size', - type: 'select', - defaultValue: 'oneThird', - options: [ - { - label: 'One Third', - value: 'oneThird', - }, - { - label: 'Half', - value: 'half', - }, - { - label: 'Two Thirds', - value: 'twoThirds', - }, - { - label: 'Full', - value: 'full', - }, - ], - }, - { - name: 'richText', - type: 'richText', - editor: lexicalEditor({ - features: ({ rootFeatures }) => { - return [ - ...rootFeatures, - HeadingFeature({ enabledHeadingSizes: ['h2', 'h3', 'h4'] }), - FixedToolbarFeature(), - InlineToolbarFeature(), - ] - }, - }), - label: false, - }, - { - name: 'enableLink', - type: 'checkbox', - }, - link({ - overrides: { - admin: { - condition: (_, { enableLink }) => Boolean(enableLink), - }, - }, - }), -] - -export const Content: Block = { - slug: 'content', - interfaceName: 'ContentBlock', - fields: [ - { - name: 'columns', - type: 'array', - fields: columnFields, - }, - ], -} diff --git a/src/blocks/Form/Checkbox/index.tsx b/src/blocks/Form/Checkbox/index.tsx deleted file mode 100644 index 1eff694..0000000 --- a/src/blocks/Form/Checkbox/index.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import type { CheckboxField } from '@payloadcms/plugin-form-builder/types' -import type { FieldErrorsImpl, FieldValues, UseFormRegister } from 'react-hook-form' - -import { useFormContext } from 'react-hook-form' - -import { Checkbox as CheckboxUi } from '@/components/ui/checkbox' -import { Label } from '@/components/ui/label' -import React from 'react' - -import { Error } from '../Error' -import { Width } from '../Width' - -export const Checkbox: React.FC< - CheckboxField & { - errors: Partial< - FieldErrorsImpl<{ - [x: string]: any - }> - > - getValues: any - register: UseFormRegister - setValue: any - } -> = ({ name, defaultValue, errors, label, register, required: requiredFromProps, width }) => { - const props = register(name, { required: requiredFromProps }) - const { setValue } = useFormContext() - - return ( - -
- { - setValue(props.name, checked) - }} - /> - -
- {requiredFromProps && errors[name] && } -
- ) -} diff --git a/src/blocks/Form/Component.tsx b/src/blocks/Form/Component.tsx deleted file mode 100644 index eb26b68..0000000 --- a/src/blocks/Form/Component.tsx +++ /dev/null @@ -1,171 +0,0 @@ -'use client' -import type { Form as FormType } from '@payloadcms/plugin-form-builder/types' - -import { useRouter } from 'next/navigation' -import React, { useCallback, useState } from 'react' -import { useForm, FormProvider } from 'react-hook-form' -import RichText from '@/components/RichText' -import { Button } from '@/components/ui/button' - -import { buildInitialFormState } from './buildInitialFormState' -import { fields } from './fields' - -export type Value = unknown - -export interface Property { - [key: string]: Value -} - -export interface Data { - [key: string]: Property | Property[] -} - -export type FormBlockType = { - blockName?: string - blockType?: 'formBlock' - enableIntro: boolean - form: FormType - introContent?: { - [k: string]: unknown - }[] -} - -export const FormBlock: React.FC< - { - id?: string - } & FormBlockType -> = (props) => { - const { - enableIntro, - form: formFromProps, - form: { id: formID, confirmationMessage, confirmationType, redirect, submitButtonLabel } = {}, - introContent, - } = props - - const formMethods = useForm({ - defaultValues: buildInitialFormState(formFromProps.fields), - }) - const { - control, - formState: { errors }, - handleSubmit, - register, - } = formMethods - - const [isLoading, setIsLoading] = useState(false) - const [hasSubmitted, setHasSubmitted] = useState() - const [error, setError] = useState<{ message: string; status?: string } | undefined>() - const router = useRouter() - - const onSubmit = useCallback( - (data: Data) => { - let loadingTimerID: ReturnType - const submitForm = async () => { - setError(undefined) - - const dataToSend = Object.entries(data).map(([name, value]) => ({ - field: name, - value, - })) - - // delay loading indicator by 1s - loadingTimerID = setTimeout(() => { - setIsLoading(true) - }, 1000) - - try { - const req = await fetch(`${process.env.NEXT_PUBLIC_SERVER_URL}/api/form-submissions`, { - body: JSON.stringify({ - form: formID, - submissionData: dataToSend, - }), - headers: { - 'Content-Type': 'application/json', - }, - method: 'POST', - }) - - const res = await req.json() - - clearTimeout(loadingTimerID) - - if (req.status >= 400) { - setIsLoading(false) - - setError({ - message: res.errors?.[0]?.message || 'Internal Server Error', - status: res.status, - }) - - return - } - - setIsLoading(false) - setHasSubmitted(true) - - if (confirmationType === 'redirect' && redirect) { - const { url } = redirect - - const redirectUrl = url - - if (redirectUrl) router.push(redirectUrl) - } - } catch (err) { - console.warn(err) - setIsLoading(false) - setError({ - message: 'Something went wrong.', - }) - } - } - - void submitForm() - }, - [router, formID, redirect, confirmationType], - ) - - return ( -
- - {enableIntro && introContent && !hasSubmitted && ( - - )} - {!isLoading && hasSubmitted && confirmationType === 'message' && ( - - )} - {isLoading && !hasSubmitted &&

Loading, please wait...

} - {error &&
{`${error.status || '500'}: ${error.message || ''}`}
} - {!hasSubmitted && ( -
-
- {formFromProps && - formFromProps.fields && - formFromProps.fields?.map((field, index) => { - const Field: React.FC = fields?.[field.blockType] - if (Field) { - return ( -
- -
- ) - } - return null - })} -
- - -
- )} -
-
- ) -} diff --git a/src/blocks/Form/Country/index.tsx b/src/blocks/Form/Country/index.tsx deleted file mode 100644 index e6cfa08..0000000 --- a/src/blocks/Form/Country/index.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import type { CountryField } from '@payloadcms/plugin-form-builder/types' -import type { Control, FieldErrorsImpl, FieldValues } from 'react-hook-form' - -import { Label } from '@/components/ui/label' -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select' -import React from 'react' -import { Controller } from 'react-hook-form' - -import { Error } from '../Error' -import { Width } from '../Width' -import { countryOptions } from './options' - -export const Country: React.FC< - CountryField & { - control: Control - errors: Partial< - FieldErrorsImpl<{ - [x: string]: any - }> - > - } -> = ({ name, control, errors, label, required, width }) => { - return ( - - - { - const controlledValue = countryOptions.find((t) => t.value === value) - - return ( - - ) - }} - rules={{ required }} - /> - {required && errors[name] && } - - ) -} diff --git a/src/blocks/Form/Country/options.ts b/src/blocks/Form/Country/options.ts deleted file mode 100644 index f952c1d..0000000 --- a/src/blocks/Form/Country/options.ts +++ /dev/null @@ -1,982 +0,0 @@ -export const countryOptions = [ - { - label: 'Afghanistan', - value: 'AF', - }, - { - label: 'Ă…land Islands', - value: 'AX', - }, - { - label: 'Albania', - value: 'AL', - }, - { - label: 'Algeria', - value: 'DZ', - }, - { - label: 'American Samoa', - value: 'AS', - }, - { - label: 'Andorra', - value: 'AD', - }, - { - label: 'Angola', - value: 'AO', - }, - { - label: 'Anguilla', - value: 'AI', - }, - { - label: 'Antarctica', - value: 'AQ', - }, - { - label: 'Antigua and Barbuda', - value: 'AG', - }, - { - label: 'Argentina', - value: 'AR', - }, - { - label: 'Armenia', - value: 'AM', - }, - { - label: 'Aruba', - value: 'AW', - }, - { - label: 'Australia', - value: 'AU', - }, - { - label: 'Austria', - value: 'AT', - }, - { - label: 'Azerbaijan', - value: 'AZ', - }, - { - label: 'Bahamas', - value: 'BS', - }, - { - label: 'Bahrain', - value: 'BH', - }, - { - label: 'Bangladesh', - value: 'BD', - }, - { - label: 'Barbados', - value: 'BB', - }, - { - label: 'Belarus', - value: 'BY', - }, - { - label: 'Belgium', - value: 'BE', - }, - { - label: 'Belize', - value: 'BZ', - }, - { - label: 'Benin', - value: 'BJ', - }, - { - label: 'Bermuda', - value: 'BM', - }, - { - label: 'Bhutan', - value: 'BT', - }, - { - label: 'Bolivia', - value: 'BO', - }, - { - label: 'Bosnia and Herzegovina', - value: 'BA', - }, - { - label: 'Botswana', - value: 'BW', - }, - { - label: 'Bouvet Island', - value: 'BV', - }, - { - label: 'Brazil', - value: 'BR', - }, - { - label: 'British Indian Ocean Territory', - value: 'IO', - }, - { - label: 'Brunei Darussalam', - value: 'BN', - }, - { - label: 'Bulgaria', - value: 'BG', - }, - { - label: 'Burkina Faso', - value: 'BF', - }, - { - label: 'Burundi', - value: 'BI', - }, - { - label: 'Cambodia', - value: 'KH', - }, - { - label: 'Cameroon', - value: 'CM', - }, - { - label: 'Canada', - value: 'CA', - }, - { - label: 'Cape Verde', - value: 'CV', - }, - { - label: 'Cayman Islands', - value: 'KY', - }, - { - label: 'Central African Republic', - value: 'CF', - }, - { - label: 'Chad', - value: 'TD', - }, - { - label: 'Chile', - value: 'CL', - }, - { - label: 'China', - value: 'CN', - }, - { - label: 'Christmas Island', - value: 'CX', - }, - { - label: 'Cocos (Keeling) Islands', - value: 'CC', - }, - { - label: 'Colombia', - value: 'CO', - }, - { - label: 'Comoros', - value: 'KM', - }, - { - label: 'Congo', - value: 'CG', - }, - { - label: 'Congo, The Democratic Republic of the', - value: 'CD', - }, - { - label: 'Cook Islands', - value: 'CK', - }, - { - label: 'Costa Rica', - value: 'CR', - }, - { - label: "Cote D'Ivoire", - value: 'CI', - }, - { - label: 'Croatia', - value: 'HR', - }, - { - label: 'Cuba', - value: 'CU', - }, - { - label: 'Cyprus', - value: 'CY', - }, - { - label: 'Czech Republic', - value: 'CZ', - }, - { - label: 'Denmark', - value: 'DK', - }, - { - label: 'Djibouti', - value: 'DJ', - }, - { - label: 'Dominica', - value: 'DM', - }, - { - label: 'Dominican Republic', - value: 'DO', - }, - { - label: 'Ecuador', - value: 'EC', - }, - { - label: 'Egypt', - value: 'EG', - }, - { - label: 'El Salvador', - value: 'SV', - }, - { - label: 'Equatorial Guinea', - value: 'GQ', - }, - { - label: 'Eritrea', - value: 'ER', - }, - { - label: 'Estonia', - value: 'EE', - }, - { - label: 'Ethiopia', - value: 'ET', - }, - { - label: 'Falkland Islands (Malvinas)', - value: 'FK', - }, - { - label: 'Faroe Islands', - value: 'FO', - }, - { - label: 'Fiji', - value: 'FJ', - }, - { - label: 'Finland', - value: 'FI', - }, - { - label: 'France', - value: 'FR', - }, - { - label: 'French Guiana', - value: 'GF', - }, - { - label: 'French Polynesia', - value: 'PF', - }, - { - label: 'French Southern Territories', - value: 'TF', - }, - { - label: 'Gabon', - value: 'GA', - }, - { - label: 'Gambia', - value: 'GM', - }, - { - label: 'Georgia', - value: 'GE', - }, - { - label: 'Germany', - value: 'DE', - }, - { - label: 'Ghana', - value: 'GH', - }, - { - label: 'Gibraltar', - value: 'GI', - }, - { - label: 'Greece', - value: 'GR', - }, - { - label: 'Greenland', - value: 'GL', - }, - { - label: 'Grenada', - value: 'GD', - }, - { - label: 'Guadeloupe', - value: 'GP', - }, - { - label: 'Guam', - value: 'GU', - }, - { - label: 'Guatemala', - value: 'GT', - }, - { - label: 'Guernsey', - value: 'GG', - }, - { - label: 'Guinea', - value: 'GN', - }, - { - label: 'Guinea-Bissau', - value: 'GW', - }, - { - label: 'Guyana', - value: 'GY', - }, - { - label: 'Haiti', - value: 'HT', - }, - { - label: 'Heard Island and Mcdonald Islands', - value: 'HM', - }, - { - label: 'Holy See (Vatican City State)', - value: 'VA', - }, - { - label: 'Honduras', - value: 'HN', - }, - { - label: 'Hong Kong', - value: 'HK', - }, - { - label: 'Hungary', - value: 'HU', - }, - { - label: 'Iceland', - value: 'IS', - }, - { - label: 'India', - value: 'IN', - }, - { - label: 'Indonesia', - value: 'ID', - }, - { - label: 'Iran, Islamic Republic Of', - value: 'IR', - }, - { - label: 'Iraq', - value: 'IQ', - }, - { - label: 'Ireland', - value: 'IE', - }, - { - label: 'Isle of Man', - value: 'IM', - }, - { - label: 'Israel', - value: 'IL', - }, - { - label: 'Italy', - value: 'IT', - }, - { - label: 'Jamaica', - value: 'JM', - }, - { - label: 'Japan', - value: 'JP', - }, - { - label: 'Jersey', - value: 'JE', - }, - { - label: 'Jordan', - value: 'JO', - }, - { - label: 'Kazakhstan', - value: 'KZ', - }, - { - label: 'Kenya', - value: 'KE', - }, - { - label: 'Kiribati', - value: 'KI', - }, - { - label: "Democratic People's Republic of Korea", - value: 'KP', - }, - { - label: 'Korea, Republic of', - value: 'KR', - }, - { - label: 'Kosovo', - value: 'XK', - }, - { - label: 'Kuwait', - value: 'KW', - }, - { - label: 'Kyrgyzstan', - value: 'KG', - }, - { - label: "Lao People's Democratic Republic", - value: 'LA', - }, - { - label: 'Latvia', - value: 'LV', - }, - { - label: 'Lebanon', - value: 'LB', - }, - { - label: 'Lesotho', - value: 'LS', - }, - { - label: 'Liberia', - value: 'LR', - }, - { - label: 'Libyan Arab Jamahiriya', - value: 'LY', - }, - { - label: 'Liechtenstein', - value: 'LI', - }, - { - label: 'Lithuania', - value: 'LT', - }, - { - label: 'Luxembourg', - value: 'LU', - }, - { - label: 'Macao', - value: 'MO', - }, - { - label: 'Macedonia, The Former Yugoslav Republic of', - value: 'MK', - }, - { - label: 'Madagascar', - value: 'MG', - }, - { - label: 'Malawi', - value: 'MW', - }, - { - label: 'Malaysia', - value: 'MY', - }, - { - label: 'Maldives', - value: 'MV', - }, - { - label: 'Mali', - value: 'ML', - }, - { - label: 'Malta', - value: 'MT', - }, - { - label: 'Marshall Islands', - value: 'MH', - }, - { - label: 'Martinique', - value: 'MQ', - }, - { - label: 'Mauritania', - value: 'MR', - }, - { - label: 'Mauritius', - value: 'MU', - }, - { - label: 'Mayotte', - value: 'YT', - }, - { - label: 'Mexico', - value: 'MX', - }, - { - label: 'Micronesia, Federated States of', - value: 'FM', - }, - { - label: 'Moldova, Republic of', - value: 'MD', - }, - { - label: 'Monaco', - value: 'MC', - }, - { - label: 'Mongolia', - value: 'MN', - }, - { - label: 'Montenegro', - value: 'ME', - }, - { - label: 'Montserrat', - value: 'MS', - }, - { - label: 'Morocco', - value: 'MA', - }, - { - label: 'Mozambique', - value: 'MZ', - }, - { - label: 'Myanmar', - value: 'MM', - }, - { - label: 'Namibia', - value: 'NA', - }, - { - label: 'Nauru', - value: 'NR', - }, - { - label: 'Nepal', - value: 'NP', - }, - { - label: 'Netherlands', - value: 'NL', - }, - { - label: 'Netherlands Antilles', - value: 'AN', - }, - { - label: 'New Caledonia', - value: 'NC', - }, - { - label: 'New Zealand', - value: 'NZ', - }, - { - label: 'Nicaragua', - value: 'NI', - }, - { - label: 'Niger', - value: 'NE', - }, - { - label: 'Nigeria', - value: 'NG', - }, - { - label: 'Niue', - value: 'NU', - }, - { - label: 'Norfolk Island', - value: 'NF', - }, - { - label: 'Northern Mariana Islands', - value: 'MP', - }, - { - label: 'Norway', - value: 'NO', - }, - { - label: 'Oman', - value: 'OM', - }, - { - label: 'Pakistan', - value: 'PK', - }, - { - label: 'Palau', - value: 'PW', - }, - { - label: 'Palestinian Territory, Occupied', - value: 'PS', - }, - { - label: 'Panama', - value: 'PA', - }, - { - label: 'Papua New Guinea', - value: 'PG', - }, - { - label: 'Paraguay', - value: 'PY', - }, - { - label: 'Peru', - value: 'PE', - }, - { - label: 'Philippines', - value: 'PH', - }, - { - label: 'Pitcairn', - value: 'PN', - }, - { - label: 'Poland', - value: 'PL', - }, - { - label: 'Portugal', - value: 'PT', - }, - { - label: 'Puerto Rico', - value: 'PR', - }, - { - label: 'Qatar', - value: 'QA', - }, - { - label: 'Reunion', - value: 'RE', - }, - { - label: 'Romania', - value: 'RO', - }, - { - label: 'Russian Federation', - value: 'RU', - }, - { - label: 'Rwanda', - value: 'RW', - }, - { - label: 'Saint Helena', - value: 'SH', - }, - { - label: 'Saint Kitts and Nevis', - value: 'KN', - }, - { - label: 'Saint Lucia', - value: 'LC', - }, - { - label: 'Saint Pierre and Miquelon', - value: 'PM', - }, - { - label: 'Saint Vincent and the Grenadines', - value: 'VC', - }, - { - label: 'Samoa', - value: 'WS', - }, - { - label: 'San Marino', - value: 'SM', - }, - { - label: 'Sao Tome and Principe', - value: 'ST', - }, - { - label: 'Saudi Arabia', - value: 'SA', - }, - { - label: 'Senegal', - value: 'SN', - }, - { - label: 'Serbia', - value: 'RS', - }, - { - label: 'Seychelles', - value: 'SC', - }, - { - label: 'Sierra Leone', - value: 'SL', - }, - { - label: 'Singapore', - value: 'SG', - }, - { - label: 'Slovakia', - value: 'SK', - }, - { - label: 'Slovenia', - value: 'SI', - }, - { - label: 'Solomon Islands', - value: 'SB', - }, - { - label: 'Somalia', - value: 'SO', - }, - { - label: 'South Africa', - value: 'ZA', - }, - { - label: 'South Georgia and the South Sandwich Islands', - value: 'GS', - }, - { - label: 'Spain', - value: 'ES', - }, - { - label: 'Sri Lanka', - value: 'LK', - }, - { - label: 'Sudan', - value: 'SD', - }, - { - label: 'Suriname', - value: 'SR', - }, - { - label: 'Svalbard and Jan Mayen', - value: 'SJ', - }, - { - label: 'Swaziland', - value: 'SZ', - }, - { - label: 'Sweden', - value: 'SE', - }, - { - label: 'Switzerland', - value: 'CH', - }, - { - label: 'Syrian Arab Republic', - value: 'SY', - }, - { - label: 'Taiwan', - value: 'TW', - }, - { - label: 'Tajikistan', - value: 'TJ', - }, - { - label: 'Tanzania, United Republic of', - value: 'TZ', - }, - { - label: 'Thailand', - value: 'TH', - }, - { - label: 'Timor-Leste', - value: 'TL', - }, - { - label: 'Togo', - value: 'TG', - }, - { - label: 'Tokelau', - value: 'TK', - }, - { - label: 'Tonga', - value: 'TO', - }, - { - label: 'Trinidad and Tobago', - value: 'TT', - }, - { - label: 'Tunisia', - value: 'TN', - }, - { - label: 'Turkey', - value: 'TR', - }, - { - label: 'Turkmenistan', - value: 'TM', - }, - { - label: 'Turks and Caicos Islands', - value: 'TC', - }, - { - label: 'Tuvalu', - value: 'TV', - }, - { - label: 'Uganda', - value: 'UG', - }, - { - label: 'Ukraine', - value: 'UA', - }, - { - label: 'United Arab Emirates', - value: 'AE', - }, - { - label: 'United Kingdom', - value: 'GB', - }, - { - label: 'United States', - value: 'US', - }, - { - label: 'United States Minor Outlying Islands', - value: 'UM', - }, - { - label: 'Uruguay', - value: 'UY', - }, - { - label: 'Uzbekistan', - value: 'UZ', - }, - { - label: 'Vanuatu', - value: 'VU', - }, - { - label: 'Venezuela', - value: 'VE', - }, - { - label: 'Viet Nam', - value: 'VN', - }, - { - label: 'Virgin Islands, British', - value: 'VG', - }, - { - label: 'Virgin Islands, U.S.', - value: 'VI', - }, - { - label: 'Wallis and Futuna', - value: 'WF', - }, - { - label: 'Western Sahara', - value: 'EH', - }, - { - label: 'Yemen', - value: 'YE', - }, - { - label: 'Zambia', - value: 'ZM', - }, - { - label: 'Zimbabwe', - value: 'ZW', - }, -] diff --git a/src/blocks/Form/Email/index.tsx b/src/blocks/Form/Email/index.tsx deleted file mode 100644 index c015b93..0000000 --- a/src/blocks/Form/Email/index.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import type { EmailField } from '@payloadcms/plugin-form-builder/types' -import type { FieldErrorsImpl, FieldValues, UseFormRegister } from 'react-hook-form' - -import { Input } from '@/components/ui/input' -import { Label } from '@/components/ui/label' -import React from 'react' - -import { Error } from '../Error' -import { Width } from '../Width' - -export const Email: React.FC< - EmailField & { - errors: Partial< - FieldErrorsImpl<{ - [x: string]: any - }> - > - register: UseFormRegister - } -> = ({ name, defaultValue, errors, label, register, required: requiredFromProps, width }) => { - return ( - - - - - {requiredFromProps && errors[name] && } - - ) -} diff --git a/src/blocks/Form/Error/index.tsx b/src/blocks/Form/Error/index.tsx deleted file mode 100644 index 94aa851..0000000 --- a/src/blocks/Form/Error/index.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import * as React from 'react' - -export const Error: React.FC = () => { - return
This field is required
-} diff --git a/src/blocks/Form/Message/index.tsx b/src/blocks/Form/Message/index.tsx deleted file mode 100644 index f1d7851..0000000 --- a/src/blocks/Form/Message/index.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import RichText from '@/components/RichText' -import React from 'react' - -import { Width } from '../Width' - -export const Message: React.FC = ({ message }: { message: Record }) => { - return ( - - {message && } - - ) -} diff --git a/src/blocks/Form/Number/index.tsx b/src/blocks/Form/Number/index.tsx deleted file mode 100644 index 5dedc8a..0000000 --- a/src/blocks/Form/Number/index.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import type { TextField } from '@payloadcms/plugin-form-builder/types' -import type { FieldErrorsImpl, FieldValues, UseFormRegister } from 'react-hook-form' - -import { Input } from '@/components/ui/input' -import { Label } from '@/components/ui/label' -import React from 'react' - -import { Error } from '../Error' -import { Width } from '../Width' -export const Number: React.FC< - TextField & { - errors: Partial< - FieldErrorsImpl<{ - [x: string]: any - }> - > - register: UseFormRegister - } -> = ({ name, defaultValue, errors, label, register, required: requiredFromProps, width }) => { - return ( - - - - {requiredFromProps && errors[name] && } - - ) -} diff --git a/src/blocks/Form/Select/index.tsx b/src/blocks/Form/Select/index.tsx deleted file mode 100644 index c0d1f77..0000000 --- a/src/blocks/Form/Select/index.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import type { SelectField } from '@payloadcms/plugin-form-builder/types' -import type { Control, FieldErrorsImpl, FieldValues } from 'react-hook-form' - -import { Label } from '@/components/ui/label' -import { - Select as SelectComponent, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select' -import React from 'react' -import { Controller } from 'react-hook-form' - -import { Error } from '../Error' -import { Width } from '../Width' - -export const Select: React.FC< - SelectField & { - control: Control - errors: Partial< - FieldErrorsImpl<{ - [x: string]: any - }> - > - } -> = ({ name, control, errors, label, options, required, width }) => { - return ( - - - { - const controlledValue = options.find((t) => t.value === value) - - return ( - onChange(val)} value={controlledValue?.value}> - - - - - {options.map(({ label, value }) => { - return ( - - {label} - - ) - })} - - - ) - }} - rules={{ required }} - /> - {required && errors[name] && } - - ) -} diff --git a/src/blocks/Form/State/index.tsx b/src/blocks/Form/State/index.tsx deleted file mode 100644 index db8aa8a..0000000 --- a/src/blocks/Form/State/index.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import type { StateField } from '@payloadcms/plugin-form-builder/types' -import type { Control, FieldErrorsImpl, FieldValues } from 'react-hook-form' - -import { Label } from '@/components/ui/label' -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select' -import React from 'react' -import { Controller } from 'react-hook-form' - -import { Error } from '../Error' -import { Width } from '../Width' -import { stateOptions } from './options' - -export const State: React.FC< - StateField & { - control: Control - errors: Partial< - FieldErrorsImpl<{ - [x: string]: any - }> - > - } -> = ({ name, control, errors, label, required, width }) => { - return ( - - - { - const controlledValue = stateOptions.find((t) => t.value === value) - - return ( - - ) - }} - rules={{ required }} - /> - {required && errors[name] && } - - ) -} diff --git a/src/blocks/Form/State/options.ts b/src/blocks/Form/State/options.ts deleted file mode 100644 index 8dff991..0000000 --- a/src/blocks/Form/State/options.ts +++ /dev/null @@ -1,52 +0,0 @@ -export const stateOptions = [ - { label: 'Alabama', value: 'AL' }, - { label: 'Alaska', value: 'AK' }, - { label: 'Arizona', value: 'AZ' }, - { label: 'Arkansas', value: 'AR' }, - { label: 'California', value: 'CA' }, - { label: 'Colorado', value: 'CO' }, - { label: 'Connecticut', value: 'CT' }, - { label: 'Delaware', value: 'DE' }, - { label: 'Florida', value: 'FL' }, - { label: 'Georgia', value: 'GA' }, - { label: 'Hawaii', value: 'HI' }, - { label: 'Idaho', value: 'ID' }, - { label: 'Illinois', value: 'IL' }, - { label: 'Indiana', value: 'IN' }, - { label: 'Iowa', value: 'IA' }, - { label: 'Kansas', value: 'KS' }, - { label: 'Kentucky', value: 'KY' }, - { label: 'Louisiana', value: 'LA' }, - { label: 'Maine', value: 'ME' }, - { label: 'Maryland', value: 'MD' }, - { label: 'Massachusetts', value: 'MA' }, - { label: 'Michigan', value: 'MI' }, - { label: 'Minnesota', value: 'MN' }, - { label: 'Mississippi', value: 'MS' }, - { label: 'Missouri', value: 'MO' }, - { label: 'Montana', value: 'MT' }, - { label: 'Nebraska', value: 'NE' }, - { label: 'Nevada', value: 'NV' }, - { label: 'New Hampshire', value: 'NH' }, - { label: 'New Jersey', value: 'NJ' }, - { label: 'New Mexico', value: 'NM' }, - { label: 'New York', value: 'NY' }, - { label: 'North Carolina', value: 'NC' }, - { label: 'North Dakota', value: 'ND' }, - { label: 'Ohio', value: 'OH' }, - { label: 'Oklahoma', value: 'OK' }, - { label: 'Oregon', value: 'OR' }, - { label: 'Pennsylvania', value: 'PA' }, - { label: 'Rhode Island', value: 'RI' }, - { label: 'South Carolina', value: 'SC' }, - { label: 'South Dakota', value: 'SD' }, - { label: 'Tennessee', value: 'TN' }, - { label: 'Texas', value: 'TX' }, - { label: 'Utah', value: 'UT' }, - { label: 'Vermont', value: 'VT' }, - { label: 'Virginia', value: 'VA' }, - { label: 'Washington', value: 'WA' }, - { label: 'West Virginia', value: 'WV' }, - { label: 'Wisconsin', value: 'WI' }, - { label: 'Wyoming', value: 'WY' }, -] diff --git a/src/blocks/Form/Text/index.tsx b/src/blocks/Form/Text/index.tsx deleted file mode 100644 index 33778bd..0000000 --- a/src/blocks/Form/Text/index.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import type { TextField } from '@payloadcms/plugin-form-builder/types' -import type { FieldErrorsImpl, FieldValues, UseFormRegister } from 'react-hook-form' - -import { Input } from '@/components/ui/input' -import { Label } from '@/components/ui/label' -import React from 'react' - -import { Error } from '../Error' -import { Width } from '../Width' - -export const Text: React.FC< - TextField & { - errors: Partial< - FieldErrorsImpl<{ - [x: string]: any - }> - > - register: UseFormRegister - } -> = ({ name, defaultValue, errors, label, register, required: requiredFromProps, width }) => { - return ( - - - - {requiredFromProps && errors[name] && } - - ) -} diff --git a/src/blocks/Form/Textarea/index.tsx b/src/blocks/Form/Textarea/index.tsx deleted file mode 100644 index ef52d88..0000000 --- a/src/blocks/Form/Textarea/index.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import type { TextField } from '@payloadcms/plugin-form-builder/types' -import type { FieldErrorsImpl, FieldValues, UseFormRegister } from 'react-hook-form' - -import { Label } from '@/components/ui/label' -import { Textarea as TextAreaComponent } from '@/components/ui/textarea' -import React from 'react' - -import { Error } from '../Error' -import { Width } from '../Width' - -export const Textarea: React.FC< - TextField & { - errors: Partial< - FieldErrorsImpl<{ - [x: string]: any - }> - > - register: UseFormRegister - rows?: number - } -> = ({ - name, - defaultValue, - errors, - label, - register, - required: requiredFromProps, - rows = 3, - width, -}) => { - return ( - - - - - - {requiredFromProps && errors[name] && } - - ) -} diff --git a/src/blocks/Form/Width/index.tsx b/src/blocks/Form/Width/index.tsx deleted file mode 100644 index bcc51a3..0000000 --- a/src/blocks/Form/Width/index.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import * as React from 'react' - -export const Width: React.FC<{ - children: React.ReactNode - className?: string - width?: number | string -}> = ({ children, className, width }) => { - return ( -
- {children} -
- ) -} diff --git a/src/blocks/Form/buildInitialFormState.tsx b/src/blocks/Form/buildInitialFormState.tsx deleted file mode 100644 index fe5d3c0..0000000 --- a/src/blocks/Form/buildInitialFormState.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import type { FormFieldBlock } from '@payloadcms/plugin-form-builder/types' - -export const buildInitialFormState = (fields: FormFieldBlock[]) => { - return fields?.reduce((initialSchema, field) => { - if (field.blockType === 'checkbox') { - return { - ...initialSchema, - [field.name]: field.defaultValue, - } - } - if (field.blockType === 'country') { - return { - ...initialSchema, - [field.name]: '', - } - } - if (field.blockType === 'email') { - return { - ...initialSchema, - [field.name]: '', - } - } - if (field.blockType === 'text') { - return { - ...initialSchema, - [field.name]: '', - } - } - if (field.blockType === 'select') { - return { - ...initialSchema, - [field.name]: '', - } - } - if (field.blockType === 'state') { - return { - ...initialSchema, - [field.name]: '', - } - } - }, {}) -} diff --git a/src/blocks/Form/config.ts b/src/blocks/Form/config.ts deleted file mode 100644 index 5334289..0000000 --- a/src/blocks/Form/config.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type { Block } from 'payload' - -import { - FixedToolbarFeature, - HeadingFeature, - InlineToolbarFeature, - lexicalEditor, -} from '@payloadcms/richtext-lexical' - -export const FormBlock: Block = { - slug: 'formBlock', - interfaceName: 'FormBlock', - fields: [ - { - name: 'form', - type: 'relationship', - relationTo: 'forms', - required: true, - }, - { - name: 'enableIntro', - type: 'checkbox', - label: 'Enable Intro Content', - }, - { - name: 'introContent', - type: 'richText', - admin: { - condition: (_, { enableIntro }) => Boolean(enableIntro), - }, - editor: lexicalEditor({ - features: ({ rootFeatures }) => { - return [ - ...rootFeatures, - HeadingFeature({ enabledHeadingSizes: ['h1', 'h2', 'h3', 'h4'] }), - FixedToolbarFeature(), - InlineToolbarFeature(), - ] - }, - }), - label: 'Intro Content', - }, - ], - graphQL: { - singularName: 'FormBlock', - }, - labels: { - plural: 'Form Blocks', - singular: 'Form Block', - }, -} diff --git a/src/blocks/Form/fields.tsx b/src/blocks/Form/fields.tsx deleted file mode 100644 index fa660f7..0000000 --- a/src/blocks/Form/fields.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { Checkbox } from './Checkbox' -import { Country } from './Country' -import { Email } from './Email' -import { Message } from './Message' -import { Number } from './Number' -import { Select } from './Select' -import { State } from './State' -import { Text } from './Text' -import { Textarea } from './Textarea' - -export const fields = { - checkbox: Checkbox, - country: Country, - email: Email, - message: Message, - number: Number, - select: Select, - state: State, - text: Text, - textarea: Textarea, -} diff --git a/src/blocks/MediaBlock/Component.tsx b/src/blocks/MediaBlock/Component.tsx deleted file mode 100644 index f459c0c..0000000 --- a/src/blocks/MediaBlock/Component.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import type { StaticImageData } from 'next/image' - -import { cn } from 'src/utilities/cn' -import React from 'react' -import RichText from '@/components/RichText' - -import type { Page } from '@/payload-types' - -import { Media } from '../../components/Media' - -type Props = Extract & { - breakout?: boolean - captionClassName?: string - className?: string - enableGutter?: boolean - id?: string - imgClassName?: string - staticImage?: StaticImageData - disableInnerContainer?: boolean -} - -export const MediaBlock: React.FC = (props) => { - const { - captionClassName, - className, - enableGutter = true, - imgClassName, - media, - position = 'default', - staticImage, - disableInnerContainer, - } = props - - let caption - if (media && typeof media === 'object') caption = media.caption - - return ( -
- {position === 'fullscreen' && ( -
- -
- )} - {position === 'default' && ( - - )} - {caption && ( -
- -
- )} -
- ) -} diff --git a/src/blocks/MediaBlock/config.ts b/src/blocks/MediaBlock/config.ts deleted file mode 100644 index ba84efe..0000000 --- a/src/blocks/MediaBlock/config.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { Block } from 'payload' - -export const MediaBlock: Block = { - slug: 'mediaBlock', - interfaceName: 'MediaBlock', - fields: [ - { - name: 'position', - type: 'select', - defaultValue: 'default', - options: [ - { - label: 'Default', - value: 'default', - }, - { - label: 'Fullscreen', - value: 'fullscreen', - }, - ], - }, - { - name: 'media', - type: 'upload', - relationTo: 'media', - required: true, - }, - ], -} diff --git a/src/blocks/RelatedPosts/Component.tsx b/src/blocks/RelatedPosts/Component.tsx deleted file mode 100644 index 3bebc75..0000000 --- a/src/blocks/RelatedPosts/Component.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import clsx from 'clsx' -import React from 'react' -import RichText from '@/components/RichText' - -import type { Post } from '@/payload-types' - -import { Card } from '../../components/Card' - -export type RelatedPostsProps = { - className?: string - docs?: Post[] - introContent?: any -} - -export const RelatedPosts: React.FC = (props) => { - const { className, docs, introContent } = props - - return ( -
- {introContent && } - -
- {docs?.map((doc, index) => { - if (typeof doc === 'string') return null - - return - })} -
-
- ) -} diff --git a/src/blocks/RenderBlocks.tsx b/src/blocks/RenderBlocks.tsx deleted file mode 100644 index e87fd8a..0000000 --- a/src/blocks/RenderBlocks.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { cn } from 'src/utilities/cn' -import React, { Fragment } from 'react' - -import type { Page } from '@/payload-types' - -import { ArchiveBlock } from '@/blocks/ArchiveBlock/Component' -import { CallToActionBlock } from '@/blocks/CallToAction/Component' -import { ContentBlock } from '@/blocks/Content/Component' -import { FormBlock } from '@/blocks/Form/Component' -import { MediaBlock } from '@/blocks/MediaBlock/Component' - -const blockComponents = { - archive: ArchiveBlock, - content: ContentBlock, - cta: CallToActionBlock, - formBlock: FormBlock, - mediaBlock: MediaBlock, -} - -export const RenderBlocks: React.FC<{ - blocks: Page['layout'][0][] -}> = (props) => { - const { blocks } = props - - const hasBlocks = blocks && Array.isArray(blocks) && blocks.length > 0 - - if (hasBlocks) { - return ( - - {blocks.map((block, index) => { - const { blockType } = block - - if (blockType && blockType in blockComponents) { - const Block = blockComponents[blockType] - - if (Block) { - return ( -
- {/* @ts-expect-error */} - -
- ) - } - } - return null - })} -
- ) - } - - return null -} diff --git a/src/collections/Authors.ts b/src/collections/Authors.ts index abcdc29..24e4fb6 100644 --- a/src/collections/Authors.ts +++ b/src/collections/Authors.ts @@ -1,114 +1,177 @@ -import type { CollectionConfig } from 'payload' +import type { CollectionConfig } from "payload"; -import { anyone } from '@/access/anyone' -import { slugField } from '@/fields/slug' +import { anyone } from "@/access/anyone"; +import { slugField } from "@/fields/slug"; -import { populatePublishedAt } from '@/hooks/populatePublishedAt' +import { populatePublishedAt } from "@/hooks/populatePublishedAt"; import { authenticated } from "@/access/authenticated"; +import { link } from "@/fields/link"; +import { generatePreviewPath } from "@/utilities/generatePreviewPath"; +import { authenticatedOrPublished } from "@/access/authenticatedOrPublished"; +import { MetaDescriptionField, MetaImageField, MetaTitleField, OverviewField, PreviewField } from "@payloadcms/plugin-seo/fields"; export const Authors: CollectionConfig = { - slug: 'authors', + slug: "authors", admin: { - useAsTitle: 'name', - defaultColumns: ['name', 'role', 'slug'], + useAsTitle: "authorName", + defaultColumns: ["authorName", "role", "slug"], + livePreview: { + url: ({ data }) => { + const path = generatePreviewPath({ + slug: typeof data?.slug === "string" ? data.slug : "", + collection: "authors", + }); + return `${process.env.NEXT_PUBLIC_SERVER_URL}${path}`; + }, + }, + preview: (data) => { + const path = generatePreviewPath({ + slug: typeof data?.slug === "string" ? data.slug : "", + collection: "authors", + }); + return `${process.env.NEXT_PUBLIC_SERVER_URL}${path}`; + }, }, hooks: { beforeChange: [populatePublishedAt], }, versions: { drafts: true }, access: { - read: anyone, + read: authenticatedOrPublished, update: anyone, create: authenticated, delete: authenticated, }, fields: [ { - name: 'featuredImage', - label: 'Picture', - type: 'upload', - relationTo: 'media', + name: "featuredImage", + label: "Picture", + type: "upload", + relationTo: "media", admin: { - position: 'sidebar', + position: "sidebar", }, }, { - name: 'name', - type: 'text', + name: "authorName", + label: "Name", + type: "text", required: true, }, { - name: 'role', - type: 'text', + name: "role", + type: "text", required: true, }, { - name: 'bio', - type: 'textarea', + name: "bio", + type: "textarea", maxLength: 250, required: true, }, { - type: 'row', + type: "group", + name: "socials", + label: 'Socials', fields: [ { name: 'linkedIn', - label: 'LinkedIn Username', - type: 'text', - maxLength: 30, - admin: { - width: '25%', - }, + label: 'LinkedIn', + type: "array", + fields: [ + { + name: "url", + type: "text", + }, + ], + maxRows: 1, }, { - name: 'x', - label: 'X Username', - type: 'text', - maxLength: 30, - admin: { - width: '25%', - }, + name: "x", + type: "array", + fields: [ + { + name: "url", + type: "text", + }, + ], + maxRows: 1, }, { - name: 'gitHub', - label: 'GitHub Username', - type: 'text', - maxLength: 30, - admin: { - width: '25%', - }, + name: "github", + type: "array", + fields: [ + { + name: "url", + type: "text", + }, + ], + maxRows: 1, }, { - name: 'medium', - label: 'Medium Username', - type: 'text', - maxLength: 30, - admin: { - width: '25%', - }, + name: "medium", + type: "array", + fields: [ + { + name: "url", + type: "text", + }, + ], + maxRows: 1, }, ], }, { - name: 'publishedAt', - type: 'date', + name: "publishedAt", + type: "date", admin: { - position: 'sidebar', + position: "sidebar", date: { - pickerAppearance: 'dayAndTime', + pickerAppearance: "dayAndTime", }, }, hooks: { beforeChange: [ ({ siblingData, value }) => { - if (siblingData._status === 'published' && !value) { - return new Date() + if (siblingData._status === "published" && !value) { + return new Date(); } - return value + return value; }, ], }, }, - ...slugField(), + ...slugField("authorName"), + { + name: "meta", + type: "group", + label: "SEO", + admin: { + position: "sidebar", + }, + fields: [ + OverviewField({ + titlePath: "meta.title", + descriptionPath: "meta.description", + imagePath: "meta.image", + }), + MetaTitleField({ + hasGenerateFn: true, + }), + MetaImageField({ + relationTo: "media", + }), + + MetaDescriptionField({}), + PreviewField({ + // if the `generateUrl` function is configured + hasGenerateFn: true, + + // field paths to match the target field for data + titlePath: "meta.title", + descriptionPath: "meta.description", + }), + ], + }, ], -} +}; diff --git a/src/collections/Blogposts/index.ts b/src/collections/Blogposts/index.ts index d8325c9..d3f9fa6 100644 --- a/src/collections/Blogposts/index.ts +++ b/src/collections/Blogposts/index.ts @@ -1,106 +1,181 @@ -import { HTMLConverterFeature, lexicalEditor, lexicalHTML } from '@payloadcms/richtext-lexical' -import type { CollectionConfig } from 'payload' +import { BlocksFeature, FixedToolbarFeature, HeadingFeature, HorizontalRuleFeature, HTMLConverterFeature, InlineToolbarFeature, lexicalEditor, lexicalHTML } from "@payloadcms/richtext-lexical"; +import type { CollectionConfig } from "payload"; -import { slugField } from '@/fields/slug' +import { slugField } from "@/fields/slug"; import { authenticated } from "@/access/authenticated"; import { authenticatedOrPublished } from "@/access/authenticatedOrPublished"; import { revalidatePost } from "@/collections/Blogposts/hooks"; +import { + MetaDescriptionField, + MetaImageField, + MetaTitleField, + OverviewField, + PreviewField, +} from "@payloadcms/plugin-seo/fields"; +import { generatePreviewPath } from "@/utilities/generatePreviewPath"; +import { Banner } from "@payloadcms/ui/elements/Banner"; +import { Code } from "@/blocks/Code/config"; + export const Blogposts: CollectionConfig = { - slug: 'blogposts', + slug: "blogposts", admin: { - useAsTitle: 'title', - defaultColumns: ['title', 'slug', 'updatedAt'], + useAsTitle: "title", + defaultColumns: ["title", "slug", "updatedAt"], + livePreview: { + url: ({ data }) => { + const path = generatePreviewPath({ + slug: typeof data?.slug === "string" ? data.slug : "", + collection: "blogposts", + }); + + return `${process.env.NEXT_PUBLIC_SERVER_URL}${path}`; + }, + }, + preview: (data) => { + const path = generatePreviewPath({ + slug: typeof data?.slug === "string" ? data.slug : "", + collection: "blogposts", + }); + return `${process.env.NEXT_PUBLIC_SERVER_URL}${path}`; + }, }, hooks: { - afterChange: [revalidatePost] + afterChange: [revalidatePost], + }, + versions: { + drafts: { + autosave: { + interval: 100, + }, + }, + maxPerDoc: 50, }, - versions: { drafts: true }, access: { read: authenticatedOrPublished, - update: authenticatedOrPublished, + update: authenticated, create: authenticated, delete: authenticated, }, fields: [ { - name: 'title', - type: 'text', + name: "title", + type: "text", required: true, }, { - name: 'summary', - type: 'textarea', + name: "summary", + type: "textarea", required: true, - maxLength: 250, + maxLength: 1000, }, { - name: 'content', - label: 'Content', - type: 'richText', + name: "content", + label: "Content", + type: "richText", required: true, editor: lexicalEditor({ - features: ({ defaultFeatures }) => [...defaultFeatures, HTMLConverterFeature({})], + features: ({ defaultFeatures }) => [ + ...defaultFeatures, + HeadingFeature({ enabledHeadingSizes: ["h1", "h2", "h3", "h4"] }), + BlocksFeature({ blocks: [Code] }), + FixedToolbarFeature(), + InlineToolbarFeature(), + HorizontalRuleFeature(), + HTMLConverterFeature({})], }), }, - lexicalHTML('content', { name: 'content_html' }), + lexicalHTML("content", { name: "content_html" }), { - name: 'featuredImage', - label: 'Featured Image', - type: 'upload', - relationTo: 'media', + name: "featuredImage", + label: "Featured Image", + type: "upload", + relationTo: "media", admin: { - position: 'sidebar', + position: "sidebar", }, }, { - name: 'authors', - type: 'relationship', - relationTo: 'authors', + name: "authors", + type: "relationship", + relationTo: "authors", required: true, hasMany: true, admin: { - position: 'sidebar', + position: "sidebar", }, }, { - name: 'categories', - label: 'Categories', - type: 'relationship', - relationTo: 'categories', + name: "categories", + label: "Categories", + type: "relationship", + relationTo: "categories", hasMany: true, admin: { - position: 'sidebar', + position: "sidebar", }, }, { - name: 'related', - label: 'Related Posts', - type: 'relationship', - relationTo: 'blogposts', + name: "related", + label: "Related Posts", + type: "relationship", + relationTo: "blogposts", hasMany: true, + admin: { + position: "sidebar", + }, }, { - name: 'publishedAt', - type: 'date', + name: "publishedAt", + type: "date", admin: { - position: 'sidebar', + position: "sidebar", date: { - pickerAppearance: 'dayAndTime', + pickerAppearance: "dayAndTime", }, }, hooks: { beforeChange: [ ({ siblingData, value }) => { - if (siblingData._status === 'published' && !value) { - return new Date() + if (siblingData._status === "published" && !value) { + return new Date(); } - return value + return value; }, ], }, }, ...slugField(), + { + name: "meta", + type: "group", + label: "SEO", + admin: { + position: "sidebar", + }, + fields: [ + OverviewField({ + titlePath: "meta.title", + descriptionPath: "meta.description", + imagePath: "meta.image", + }), + MetaTitleField({ + hasGenerateFn: true, + }), + MetaImageField({ + relationTo: "media", + }), + + MetaDescriptionField({}), + PreviewField({ + // if the `generateUrl` function is configured + hasGenerateFn: true, + // field paths to match the target field for data + titlePath: "meta.title", + descriptionPath: "meta.description", + }), + ], + }, ], -} +}; diff --git a/src/collections/Pages/hooks/revalidatePage.ts b/src/collections/Pages/hooks/revalidatePage.ts deleted file mode 100644 index d01296a..0000000 --- a/src/collections/Pages/hooks/revalidatePage.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { CollectionAfterChangeHook } from 'payload' - -import { revalidatePath } from 'next/cache' - -import type { Page } from '../../../payload-types' - -export const revalidatePage: CollectionAfterChangeHook = ({ - doc, - previousDoc, - req: { payload }, -}) => { - if (doc._status === 'published') { - const path = doc.slug === 'home' ? '/' : `/${doc.slug}` - - payload.logger.info(`Revalidating page at path: ${path}`) - - revalidatePath(path) - } - - // If the page was previously published, we need to revalidate the old path - if (previousDoc?._status === 'published' && doc._status !== 'published') { - const oldPath = previousDoc.slug === 'home' ? '/' : `/${previousDoc.slug}` - - payload.logger.info(`Revalidating old page at path: ${oldPath}`) - - revalidatePath(oldPath) - } - - return doc -} diff --git a/src/collections/Pages/index.ts b/src/collections/Pages/index.ts deleted file mode 100644 index 15791fb..0000000 --- a/src/collections/Pages/index.ts +++ /dev/null @@ -1,127 +0,0 @@ -import type { CollectionConfig } from 'payload' - -import { authenticated } from '../../access/authenticated' -import { authenticatedOrPublished } from '../../access/authenticatedOrPublished' -import { Archive } from '../../blocks/ArchiveBlock/config' -import { CallToAction } from '../../blocks/CallToAction/config' -import { Content } from '../../blocks/Content/config' -import { FormBlock } from '../../blocks/Form/config' -import { MediaBlock } from '../../blocks/MediaBlock/config' -import { hero } from '@/heros/config' -import { slugField } from '@/fields/slug' -import { populatePublishedAt } from '../../hooks/populatePublishedAt' -import { generatePreviewPath } from '../../utilities/generatePreviewPath' -import { revalidatePage } from './hooks/revalidatePage' - -import { - MetaDescriptionField, - MetaImageField, - MetaTitleField, - OverviewField, - PreviewField, -} from '@payloadcms/plugin-seo/fields' -export const Pages: CollectionConfig = { - slug: 'pages', - access: { - create: authenticated, - delete: authenticated, - read: authenticatedOrPublished, - update: authenticated, - }, - admin: { - defaultColumns: ['title', 'slug', 'updatedAt'], - livePreview: { - url: ({ data }) => { - const path = generatePreviewPath({ - slug: typeof data?.slug === 'string' ? data.slug : '', - collection: 'pages', - }) - - return `${process.env.NEXT_PUBLIC_SERVER_URL}${path}` - }, - }, - preview: (data) => { - const path = generatePreviewPath({ - slug: typeof data?.slug === 'string' ? data.slug : '', - collection: 'pages', - }) - - return `${process.env.NEXT_PUBLIC_SERVER_URL}${path}` - }, - useAsTitle: 'title', - }, - fields: [ - { - name: 'title', - type: 'text', - required: true, - }, - { - type: 'tabs', - tabs: [ - { - fields: [hero], - label: 'Hero', - }, - { - fields: [ - { - name: 'layout', - type: 'blocks', - blocks: [CallToAction, Content, MediaBlock, Archive, FormBlock], - required: true, - }, - ], - label: 'Content', - }, - { - name: 'meta', - label: 'SEO', - fields: [ - OverviewField({ - titlePath: 'meta.title', - descriptionPath: 'meta.description', - imagePath: 'meta.image', - }), - MetaTitleField({ - hasGenerateFn: true, - }), - MetaImageField({ - relationTo: 'media', - }), - - MetaDescriptionField({}), - PreviewField({ - // if the `generateUrl` function is configured - hasGenerateFn: true, - - // field paths to match the target field for data - titlePath: 'meta.title', - descriptionPath: 'meta.description', - }), - ], - }, - ], - }, - { - name: 'publishedAt', - type: 'date', - admin: { - position: 'sidebar', - }, - }, - ...slugField(), - ], - hooks: { - afterChange: [revalidatePage], - beforeChange: [populatePublishedAt], - }, - versions: { - drafts: { - autosave: { - interval: 100, // We set this interval for optimal live preview - }, - }, - maxPerDoc: 50, - }, -} diff --git a/src/collections/Podcasts/index.ts b/src/collections/Podcasts/index.ts index 344cd8e..993538b 100644 --- a/src/collections/Podcasts/index.ts +++ b/src/collections/Podcasts/index.ts @@ -1,139 +1,197 @@ -import type { CollectionConfig } from 'payload' +import type { CollectionConfig } from "payload"; -import { slugField } from '@/fields/slug' -import { populatePublishedAt } from '@/hooks/populatePublishedAt' +import { slugField } from "@/fields/slug"; +import { populatePublishedAt } from "@/hooks/populatePublishedAt"; import { authenticatedOrPublished } from "@/access/authenticatedOrPublished"; import { authenticated } from "@/access/authenticated"; import { revalidatePodcast } from "@/collections/Podcasts/hooks"; +import { MetaDescriptionField, MetaImageField, MetaTitleField, OverviewField, PreviewField } from "@payloadcms/plugin-seo/fields"; +import { generatePreviewPath } from "@/utilities/generatePreviewPath"; // TODO: Add preview; export const Podcasts: CollectionConfig = { - slug: 'podcasts', + slug: "podcasts", admin: { - useAsTitle: 'title', - defaultColumns: ['title', 'slug', 'updatedAt'], + useAsTitle: "title", + defaultColumns: ["title", "slug", "updatedAt"], + livePreview: { + url: ({ data }) => { + const path = generatePreviewPath({ + slug: typeof data?.slug === "string" ? data.slug : "", + collection: "podcasts", + }); + + return `${process.env.NEXT_PUBLIC_SERVER_URL}${path}`; + }, + }, + preview: (data) => { + const path = generatePreviewPath({ + slug: typeof data?.slug === "string" ? data.slug : "", + collection: "podcasts", + }); + return `${process.env.NEXT_PUBLIC_SERVER_URL}${path}`; + }, }, hooks: { beforeChange: [populatePublishedAt], afterChange: [revalidatePodcast], }, - versions: { drafts: true }, + versions: { + drafts: { + autosave: { + interval: 100, + }, + }, + maxPerDoc: 50, + }, access: { read: authenticatedOrPublished, - update: authenticatedOrPublished, + update: authenticated, create: authenticated, delete: authenticated, }, fields: [ { - name: 'title', - type: 'text', + name: "title", + type: "text", required: true, }, { - name: 'summary', - label: 'Episode Summary', - type: 'textarea', + name: "summary", + label: "Episode Summary", + type: "textarea", required: true, }, { - name: 'notes', - label: 'Episode Notes', - type: 'textarea', + name: "notes", + label: "Episode Notes", + type: "textarea", required: true, }, { - name: 'featuredImage', - label: 'Featured Image', - type: 'upload', - relationTo: 'media', + name: "featuredImage", + label: "Featured Image", + type: "upload", + relationTo: "media", admin: { - position: 'sidebar', + position: "sidebar", }, }, { - name: 'episodeFile', - label: 'Episode File', - type: 'upload', - relationTo: 'media', + name: "episodeFile", + label: "Episode File", + type: "upload", + relationTo: "media", required: true, admin: { - position: 'sidebar', + position: "sidebar", }, }, { - type: 'row', + type: "row", fields: [ { - name: 'spotify', - label: 'Spotify CMSLink', - type: 'text', + name: "spotify", + label: "Spotify CMSLink", + type: "text", admin: { - width: '50%', + width: "50%", }, }, { - name: 'apple', - label: 'Apple Podcasts CMSLink', - type: 'text', + name: "apple", + label: "Apple Podcasts CMSLink", + type: "text", admin: { - width: '50%', + width: "50%", }, }, ], }, { - name: 'authors', - type: 'relationship', - relationTo: 'authors', + name: "authors", + type: "relationship", + relationTo: "authors", hasMany: true, required: true, }, { - name: 'categories', - label: 'Categories', - type: 'relationship', - relationTo: 'categories', + name: "categories", + label: "Categories", + type: "relationship", + relationTo: "categories", hasMany: true, + required: true, admin: { - position: 'sidebar', + position: "sidebar", }, }, { - name: 'related', - label: 'Related Episodes', - type: 'relationship', - relationTo: 'podcasts', + name: "related", + label: "Related Episodes", + type: "relationship", + relationTo: "podcasts", hasMany: true, filterOptions: ({ id }) => { return { id: { not_in: [id], }, - } + }; }, }, { - name: 'publishedAt', - type: 'date', + name: "publishedAt", + type: "date", admin: { - position: 'sidebar', + position: "sidebar", date: { - pickerAppearance: 'dayAndTime', + pickerAppearance: "dayAndTime", }, }, hooks: { beforeChange: [ ({ siblingData, value }) => { - if (siblingData._status === 'published' && !value) { - return new Date() + if (siblingData._status === "published" && !value) { + return new Date(); } - return value + return value; }, ], }, }, ...slugField(), + { + name: "meta", + type: "group", + label: "SEO", + admin: { + position: "sidebar", + }, + fields: [ + OverviewField({ + titlePath: "meta.title", + descriptionPath: "meta.description", + imagePath: "meta.image", + }), + MetaTitleField({ + hasGenerateFn: true, + }), + MetaImageField({ + relationTo: "media", + }), + + MetaDescriptionField({}), + PreviewField({ + // if the `generateUrl` function is configured + hasGenerateFn: true, + + // field paths to match the target field for data + titlePath: "meta.title", + descriptionPath: "meta.description", + }), + ], + }, ], -} +}; diff --git a/src/collections/Posts/hooks/populateAuthors.ts b/src/collections/Posts/hooks/populateAuthors.ts deleted file mode 100644 index 8b812b4..0000000 --- a/src/collections/Posts/hooks/populateAuthors.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { CollectionAfterReadHook } from 'payload' -import { User } from 'src/payload-types' - -// The `user` collection has access control locked so that users are not publicly accessible -// This means that we need to populate the authors manually here to protect user privacy -// GraphQL will not return mutated user data that differs from the underlying schema -// So we use an alternative `populatedAuthors` field to populate the user data, hidden from the admin UI -export const populateAuthors: CollectionAfterReadHook = async ({ doc, req, req: { payload } }) => { - if (doc?.authors) { - const authorDocs: User[] = [] - - for (const author of doc.authors) { - const authorDoc = await payload.findByID({ - id: typeof author === 'object' ? author?.id : author, - collection: 'users', - depth: 0, - req, - }) - - authorDocs.push(authorDoc) - } - - doc.populatedAuthors = authorDocs.map((authorDoc) => ({ - id: authorDoc.id, - name: authorDoc.name, - })) - } - - return doc -} diff --git a/src/collections/Posts/hooks/revalidatePost.ts b/src/collections/Posts/hooks/revalidatePost.ts deleted file mode 100644 index ac55bcf..0000000 --- a/src/collections/Posts/hooks/revalidatePost.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { CollectionAfterChangeHook } from 'payload' - -import { revalidatePath } from 'next/cache' - -import type { Post } from '../../../payload-types' - -export const revalidatePost: CollectionAfterChangeHook = ({ - doc, - previousDoc, - req: { payload }, -}) => { - if (doc._status === 'published') { - const path = `/posts/${doc.slug}` - - payload.logger.info(`Revalidating post at path: ${path}`) - - revalidatePath(path) - } - - // If the post was previously published, we need to revalidate the old path - if (previousDoc._status === 'published' && doc._status !== 'published') { - const oldPath = `/posts/${previousDoc.slug}` - - payload.logger.info(`Revalidating old post at path: ${oldPath}`) - - revalidatePath(oldPath) - } - - return doc -} diff --git a/src/collections/Posts/index.ts b/src/collections/Posts/index.ts deleted file mode 100644 index 1f6e1af..0000000 --- a/src/collections/Posts/index.ts +++ /dev/null @@ -1,218 +0,0 @@ -import type { CollectionConfig } from 'payload' - -import { - BlocksFeature, - FixedToolbarFeature, - HeadingFeature, - HorizontalRuleFeature, - InlineToolbarFeature, - lexicalEditor, -} from '@payloadcms/richtext-lexical' - -import { authenticated } from '../../access/authenticated' -import { authenticatedOrPublished } from '../../access/authenticatedOrPublished' -import { Banner } from '../../blocks/Banner/config' -import { Code } from '../../blocks/Code/config' -import { MediaBlock } from '../../blocks/MediaBlock/config' -import { generatePreviewPath } from '../../utilities/generatePreviewPath' -import { populateAuthors } from './hooks/populateAuthors' -import { revalidatePost } from './hooks/revalidatePost' - -import { - MetaDescriptionField, - MetaImageField, - MetaTitleField, - OverviewField, - PreviewField, -} from '@payloadcms/plugin-seo/fields' -import { slugField } from '@/fields/slug' - -export const Posts: CollectionConfig = { - slug: 'posts', - access: { - create: authenticated, - delete: authenticated, - read: authenticatedOrPublished, - update: authenticated, - }, - admin: { - defaultColumns: ['title', 'slug', 'updatedAt'], - livePreview: { - url: ({ data }) => { - const path = generatePreviewPath({ - slug: typeof data?.slug === 'string' ? data.slug : '', - collection: 'posts', - }) - - return `${process.env.NEXT_PUBLIC_SERVER_URL}${path}` - }, - }, - preview: (data) => { - const path = generatePreviewPath({ - slug: typeof data?.slug === 'string' ? data.slug : '', - collection: 'posts', - }) - - return `${process.env.NEXT_PUBLIC_SERVER_URL}${path}` - }, - useAsTitle: 'title', - }, - fields: [ - { - name: 'title', - type: 'text', - required: true, - }, - { - type: 'tabs', - tabs: [ - { - fields: [ - { - name: 'content', - type: 'richText', - editor: lexicalEditor({ - features: ({ rootFeatures }) => { - return [ - ...rootFeatures, - HeadingFeature({ enabledHeadingSizes: ['h1', 'h2', 'h3', 'h4'] }), - BlocksFeature({ blocks: [Banner, Code, MediaBlock] }), - FixedToolbarFeature(), - InlineToolbarFeature(), - HorizontalRuleFeature(), - ] - }, - }), - label: false, - required: true, - }, - ], - label: 'Content', - }, - { - fields: [ - { - name: 'relatedPosts', - type: 'relationship', - admin: { - position: 'sidebar', - }, - filterOptions: ({ id }) => { - return { - id: { - not_in: [id], - }, - } - }, - hasMany: true, - relationTo: 'posts', - }, - { - name: 'categories', - type: 'relationship', - admin: { - position: 'sidebar', - }, - hasMany: true, - relationTo: 'categories', - }, - ], - label: 'Meta', - }, - { - name: 'meta', - label: 'SEO', - fields: [ - OverviewField({ - titlePath: 'meta.title', - descriptionPath: 'meta.description', - imagePath: 'meta.image', - }), - MetaTitleField({ - hasGenerateFn: true, - }), - MetaImageField({ - relationTo: 'media', - }), - - MetaDescriptionField({}), - PreviewField({ - // if the `generateUrl` function is configured - hasGenerateFn: true, - - // field paths to match the target field for data - titlePath: 'meta.title', - descriptionPath: 'meta.description', - }), - ], - }, - ], - }, - { - name: 'publishedAt', - type: 'date', - admin: { - date: { - pickerAppearance: 'dayAndTime', - }, - position: 'sidebar', - }, - hooks: { - beforeChange: [ - ({ siblingData, value }) => { - if (siblingData._status === 'published' && !value) { - return new Date() - } - return value - }, - ], - }, - }, - { - name: 'authors', - type: 'relationship', - admin: { - position: 'sidebar', - }, - hasMany: true, - relationTo: 'users', - }, - // This field is only used to populate the user data via the `populateAuthors` hook - // This is because the `user` collection has access control locked to protect user privacy - // GraphQL will also not return mutated user data that differs from the underlying schema - { - name: 'populatedAuthors', - type: 'array', - access: { - update: () => false, - }, - admin: { - disabled: true, - readOnly: true, - }, - fields: [ - { - name: 'id', - type: 'text', - }, - { - name: 'name', - type: 'text', - }, - ], - }, - ...slugField(), - ], - hooks: { - afterChange: [revalidatePost], - afterRead: [populateAuthors], - }, - versions: { - drafts: { - autosave: { - interval: 100, // We set this interval for optimal live preview - }, - }, - maxPerDoc: 50, - }, -} diff --git a/src/collections/TalksAndRoundtables.ts b/src/collections/TalksAndRoundtables.ts index e77fc7c..2fffb0f 100644 --- a/src/collections/TalksAndRoundtables.ts +++ b/src/collections/TalksAndRoundtables.ts @@ -27,7 +27,6 @@ export const TalksAndRoundtables: CollectionConfig = { name: 'summary', type: 'textarea', required: true, - maxLength: 250, }, { name: 'categories', @@ -76,7 +75,6 @@ export const TalksAndRoundtables: CollectionConfig = { label: 'Featured Image', type: 'upload', relationTo: 'media', - required: true, admin: { position: 'sidebar', }, diff --git a/src/components/BeforeDashboard/SeedButton/index.tsx b/src/components/BeforeDashboard/SeedButton/index.tsx deleted file mode 100644 index bc81350..0000000 --- a/src/components/BeforeDashboard/SeedButton/index.tsx +++ /dev/null @@ -1,51 +0,0 @@ -'use client' - -import React, { Fragment, useCallback, useState } from 'react' -import { toast } from '@payloadcms/ui' - -const SuccessMessage: React.FC = () => ( -
- Database seeded! You can now{' '} - - visit your website - -
-) - -export const SeedButton: React.FC = () => { - const [loading, setLoading] = useState(false) - const [seeded, setSeeded] = useState(false) - const [error, setError] = useState(null) - - const handleClick = useCallback( - async (e) => { - e.preventDefault() - if (loading || seeded) return - - setLoading(true) - - try { - await fetch('/api/seed') - setSeeded(true) - toast.success(, { duration: 5000 }) - } catch (err) { - setError(err) - } - }, - [loading, seeded], - ) - - let message = '' - if (loading) message = ' (seeding...)' - if (seeded) message = ' (done!)' - if (error) message = ` (error: ${error})` - - return ( - - - Seed your database - - {message} - - ) -} diff --git a/src/components/BeforeDashboard/index.scss b/src/components/BeforeDashboard/index.scss deleted file mode 100644 index 7169b4f..0000000 --- a/src/components/BeforeDashboard/index.scss +++ /dev/null @@ -1,24 +0,0 @@ -@import '~@payloadcms/ui/scss'; - -.dashboard .before-dashboard { - margin-bottom: base(1.5); - - &__banner { - & h4 { - margin: 0; - } - } - - &__instructions { - list-style: decimal; - margin-bottom: base(0.5); - - & li { - width: 100%; - } - } - - & a:hover { - opacity: 0.85; - } -} diff --git a/src/components/BeforeDashboard/index.tsx b/src/components/BeforeDashboard/index.tsx index 3f358aa..512b7cd 100644 --- a/src/components/BeforeDashboard/index.tsx +++ b/src/components/BeforeDashboard/index.tsx @@ -1,74 +1,42 @@ import { Banner } from '@payloadcms/ui/elements/Banner' -import React from 'react' +import styles from './styles.module.css' -import { SeedButton } from './SeedButton' -import './index.scss' +const linkProps = { + target:"_blank", + rel:"noopener noreferrer" +} -const baseClass = 'before-dashboard' +const repoLink = 'https://github.com/subvisual/content-sub/tree/main/public/media' +const loomLink = 'https://www.loom.com/share/32ad2349f0d743f0aaf72b02314396e1?sid=367067d4-1051-430f-9583-e56ad28e6a94' -const BeforeDashboard: React.FC = () => { +export default function BeforeDashboard() { return ( -
- -

Welcome to your dashboard!

+
+ +

Welcome to the dashboard!

- Here's what to do next: -
    +

    This is where you can create / delete / update content that will appear on the Content Hub

    +
    +

    You can find a quick tutorial / demonstration of the plaftorm here.

    +
    +

    Noteworthy:

    +
    • - - {' with a few pages, posts, and projects to jump-start your new site, then '} - - visit your website - - {' to see the results.'} + Media you create is automacatically pushed and stored to Github
    • - If you created this repo using Payload Cloud, head over to GitHub and clone it to your - local machine. It will be under the GitHub Scope that you selected when creating - this project. + Homepage Settings defines what content is displayed on the top of the Content Hub homepage, do not forget + to set them up.
    • - {'Modify your '} - - collections - - {' and add more '} - - fields - - {' as needed. If you are new to Payload, we also recommend you check out the '} - - Getting Started - - {' docs.'} + The Footer and Banner include the navigation links to different Subvisual pages. They are pre-set but can be changed on + the Header and Footer settings.
    • - Commit and push your changes to the repository to trigger a redeployment of your project. + The same thing applies to Socials.
    - {'Pro Tip: This block is a '} - - custom component - - , you can remove it at any time by updating your payload.config.
- ) +) } -export default BeforeDashboard diff --git a/src/components/BeforeDashboard/styles.module.css b/src/components/BeforeDashboard/styles.module.css new file mode 100644 index 0000000..bd1f946 --- /dev/null +++ b/src/components/BeforeDashboard/styles.module.css @@ -0,0 +1,9 @@ +.container ul { + list-style: decimal; + margin-top: 10px; + font-size: 16px; +} + +.container ul li { + margin-bottom: 5px; +} diff --git a/src/components/Card/index.tsx b/src/components/Card/index.tsx deleted file mode 100644 index 6277de2..0000000 --- a/src/components/Card/index.tsx +++ /dev/null @@ -1,82 +0,0 @@ -'use client' -import { cn } from '@/utilities/cn' -import useClickableCard from '@/utilities/useClickableCard' -import Link from 'next/link' -import React, { Fragment } from 'react' - -import type { Post } from '@/payload-types' - -import { Media } from '@/components/Media' - -export const Card: React.FC<{ - alignItems?: 'center' - className?: string - doc?: Post - relationTo?: 'posts' - showCategories?: boolean - title?: string -}> = (props) => { - const { card, link } = useClickableCard({}) - const { className, doc, relationTo, showCategories, title: titleFromProps } = props - - const { slug, categories, meta, title } = doc || {} - const { description, image: metaImage } = meta || {} - - const hasCategories = categories && Array.isArray(categories) && categories.length > 0 - const titleToUse = titleFromProps || title - const sanitizedDescription = description?.replace(/\s/g, ' ') // replace non-breaking space with white space - const href = `/${relationTo}/${slug}` - - return ( -
-
- {!metaImage &&
No image
} - {metaImage && typeof metaImage !== 'string' && } -
-
- {showCategories && hasCategories && ( -
- {showCategories && hasCategories && ( -
- {categories?.map((category, index) => { - if (typeof category === 'object') { - const { title: titleFromCategory } = category - - const categoryTitle = titleFromCategory || 'Untitled category' - - const isLast = index === categories.length - 1 - - return ( - - {categoryTitle} - {!isLast && ,  } - - ) - } - - return null - })} -
- )} -
- )} - {titleToUse && ( -
-

- - {titleToUse} - -

-
- )} - {description &&
{description &&

{sanitizedDescription}

}
} -
-
- ) -} diff --git a/src/components/CollectionArchive/index.tsx b/src/components/CollectionArchive/index.tsx deleted file mode 100644 index b31185f..0000000 --- a/src/components/CollectionArchive/index.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { cn } from 'src/utilities/cn' -import React from 'react' - -import type { Post } from '@/payload-types' - -import { Card } from '@/components/Card' - -export type Props = { - posts: Post[] -} - -export const CollectionArchive: React.FC = (props) => { - const { posts } = props - - return ( -
-
-
- {posts?.map((result, index) => { - if (typeof result === 'object' && result !== null) { - return ( -
- -
- ) - } - - return null - })} -
-
-
- ) -} diff --git a/src/components/Link/index.tsx b/src/components/Link/index.tsx index af7fd04..402fbba 100644 --- a/src/components/Link/index.tsx +++ b/src/components/Link/index.tsx @@ -3,7 +3,7 @@ import { cn } from 'src/utilities/cn' import Link from 'next/link' import React from 'react' -import type { Page, Post } from '@/payload-types' +import type { Author, Blogpost, CaseStudy, Podcast, TalksAndRoundtable } from "@/payload-types"; type CMSLinkType = { appearance?: 'inline' | ButtonProps['variant'] @@ -13,7 +13,7 @@ type CMSLinkType = { newTab?: boolean | null reference?: { relationTo: 'pages' | 'posts' - value: Page | Post | string | number + value: Blogpost | Podcast | Author | CaseStudy | TalksAndRoundtable | string | number } | null size?: ButtonProps['size'] | null type?: 'custom' | 'reference' | null diff --git a/src/components/PayloadRedirects/index.tsx b/src/components/PayloadRedirects/index.tsx index 2e94fdd..f9af034 100644 --- a/src/components/PayloadRedirects/index.tsx +++ b/src/components/PayloadRedirects/index.tsx @@ -1,5 +1,5 @@ import type React from 'react' -import type { Page, Post } from '@/payload-types' +import type { Author, Blogpost, CaseStudy, Podcast, TalksAndRoundtable } from "@/payload-types"; import { getCachedDocument } from '@/utilities/getDocument' import { getCachedRedirects } from '@/utilities/getRedirects' @@ -29,12 +29,12 @@ export const PayloadRedirects: React.FC = async ({ disableNotFound, url } const collection = redirectItem.to?.reference?.relationTo const id = redirectItem.to?.reference?.value - const document = (await getCachedDocument(collection, id)()) as Page | Post - redirectUrl = `${redirectItem.to?.reference?.relationTo !== 'pages' ? `/${redirectItem.to?.reference?.relationTo}` : ''}/${ + const document = (await getCachedDocument(collection, id)()) as Blogpost | Podcast | Author | TalksAndRoundtable | CaseStudy + redirectUrl = `/${redirectItem.to?.reference?.relationTo}/${ document?.slug }` } else { - redirectUrl = `${redirectItem.to?.reference?.relationTo !== 'pages' ? `/${redirectItem.to?.reference?.relationTo}` : ''}/${ + redirectUrl = `/${redirectItem.to?.reference?.relationTo}/${ typeof redirectItem.to?.reference?.value === 'object' ? redirectItem.to?.reference?.value?.slug : '' diff --git a/src/components/RichText/serialize.tsx b/src/components/RichText/serialize.tsx index 69bf3bd..5969f32 100644 --- a/src/components/RichText/serialize.tsx +++ b/src/components/RichText/serialize.tsx @@ -1,11 +1,7 @@ -import { BannerBlock } from '@/blocks/Banner/Component' -import { CallToActionBlock } from '@/blocks/CallToAction/Component' import { CodeBlock, CodeBlockProps } from '@/blocks/Code/Component' -import { MediaBlock } from '@/blocks/MediaBlock/Component' import React, { Fragment, JSX } from 'react' import { CMSLink } from '@/components/Link' import { DefaultNodeTypes, SerializedBlockNode } from '@payloadcms/richtext-lexical' -import type { BannerBlock as BannerBlockProps } from '@/payload-types' import { IS_BOLD, @@ -16,14 +12,10 @@ import { IS_SUPERSCRIPT, IS_UNDERLINE, } from './nodeFormat' -import type { Page } from '@/payload-types' export type NodeTypes = | DefaultNodeTypes | SerializedBlockNode< - | Extract - | Extract - | BannerBlockProps | CodeBlockProps > @@ -34,6 +26,7 @@ type Props = { export function serializeLexical({ nodes }: Props): JSX.Element { return ( + {/* @ts-expect-error */} {nodes?.map((node, index): JSX.Element | null => { if (node == null) { return null @@ -104,29 +97,6 @@ export function serializeLexical({ nodes }: Props): JSX.Element { if (!block || !blockType) { return null } - - switch (blockType) { - case 'cta': - return - case 'mediaBlock': - return ( - - ) - case 'banner': - return - case 'code': - return - default: - return null - } } else { switch (node.type) { case 'linebreak': { diff --git a/src/endpoints/seed/contact-form.ts b/src/endpoints/seed/contact-form.ts deleted file mode 100644 index 5fa84be..0000000 --- a/src/endpoints/seed/contact-form.ts +++ /dev/null @@ -1,111 +0,0 @@ -import type { Form } from '@/payload-types' - -export const contactForm: Partial
= { - confirmationMessage: { - root: { - type: 'root', - children: [ - { - type: 'heading', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'The contact form has been submitted successfully.', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - tag: 'h2', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, - confirmationType: 'message', - createdAt: '2023-01-12T21:47:41.374Z', - emails: [ - { - emailFrom: '"Payload" \u003Cdemo@payloadcms.com\u003E', - emailTo: '{{email}}', - message: { - root: { - type: 'root', - children: [ - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Your contact form submission was successfully received.', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, - subject: "You've received a new message.", - }, - ], - fields: [ - { - name: 'full-name', - blockName: 'full-name', - blockType: 'text', - label: 'Full Name', - required: true, - width: 100, - }, - { - name: 'email', - blockName: 'email', - blockType: 'email', - label: 'Email', - required: true, - width: 100, - }, - { - name: 'phone', - blockName: 'phone', - blockType: 'number', - label: 'Phone', - required: false, - width: 100, - }, - { - name: 'message', - blockName: 'message', - blockType: 'textarea', - label: 'Message', - required: true, - width: 100, - }, - ], - redirect: undefined, - submitButtonLabel: 'Submit', - title: 'Contact Form', - updatedAt: '2023-01-12T21:47:41.374Z', -} diff --git a/src/endpoints/seed/contact-page.ts b/src/endpoints/seed/contact-page.ts deleted file mode 100644 index f17fa37..0000000 --- a/src/endpoints/seed/contact-page.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type { Page } from '@/payload-types' - -export const contact: Partial = { - slug: 'contact', - _status: 'published', - hero: { - type: 'none', - }, - layout: [ - { - blockType: 'formBlock', - enableIntro: true, - // @ts-ignore - form: '{{CONTACT_FORM_ID}}', - introContent: { - root: { - type: 'root', - children: [ - { - type: 'heading', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Example contact form:', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - tag: 'h3', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, - }, - ], - title: 'Contact', -} diff --git a/src/endpoints/seed/home-static.ts b/src/endpoints/seed/home-static.ts deleted file mode 100644 index 91a652d..0000000 --- a/src/endpoints/seed/home-static.ts +++ /dev/null @@ -1,88 +0,0 @@ -import type { Page } from '@/payload-types' - -// Used for pre-seeded content so that the homepage is not empty -// @ts-expect-error -export const homeStatic: Page = { - slug: 'home', - _status: 'published', - hero: { - type: 'lowImpact', - richText: { - root: { - type: 'root', - children: [ - { - type: 'heading', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Payload Website Template', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - tag: 'h1', - version: 1, - }, - { - type: 'paragraph', - children: [ - { - type: 'link', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Visit the admin dashboard', - version: 1, - }, - ], - direction: 'ltr', - fields: { - linkType: 'custom', - newTab: false, - url: '/admin', - }, - format: '', - indent: 0, - version: 2, - }, - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: ' to make your account and seed content for your website.', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, - }, - meta: { - description: 'An open-source website built with Payload and Next.js.', - title: 'Payload Website Template', - }, - title: 'Home', -} diff --git a/src/endpoints/seed/home.ts b/src/endpoints/seed/home.ts deleted file mode 100644 index bdca800..0000000 --- a/src/endpoints/seed/home.ts +++ /dev/null @@ -1,668 +0,0 @@ -import type { RequiredDataFromCollectionSlug } from 'payload' - -export const home: RequiredDataFromCollectionSlug<'pages'> = { - slug: 'home', - _status: 'published', - hero: { - type: 'highImpact', - links: [ - { - link: { - type: 'custom', - appearance: 'default', - label: 'All posts', - url: '/posts', - }, - }, - { - link: { - type: 'custom', - appearance: 'outline', - label: 'Contact', - url: '/contact', - }, - }, - ], - // @ts-ignore - media: '{{IMAGE_1}}', - richText: { - root: { - type: 'root', - children: [ - { - type: 'heading', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Payload Website Template', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - tag: 'h1', - version: 1, - }, - { - type: 'paragraph', - children: [ - { - type: 'link', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Visit the admin dashboard', - version: 1, - }, - ], - direction: 'ltr', - fields: { - linkType: 'custom', - newTab: false, - url: '/admin', - }, - format: '', - indent: 0, - version: 3, - }, - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: " to begin managing this site's content. The code for this template is completely open-source and can be found ", - version: 1, - }, - { - type: 'link', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'here', - version: 1, - }, - ], - direction: 'ltr', - fields: { - linkType: 'custom', - newTab: true, - url: 'https://github.com/payloadcms/payload/tree/beta/templates/website', - }, - format: '', - indent: 0, - version: 3, - }, - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: '. ', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, - }, - layout: [ - { - blockName: 'Content Block', - blockType: 'content', - columns: [ - { - richText: { - root: { - type: 'root', - children: [ - { - type: 'heading', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Core features', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - tag: 'h2', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, - size: 'full', - }, - { - enableLink: false, - richText: { - root: { - type: 'root', - children: [ - { - type: 'heading', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Admin Dashboard', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - tag: 'h3', - version: 1, - }, - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: "Manage this site's pages and posts from the ", - version: 1, - }, - { - type: 'link', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'admin dashboard', - version: 1, - }, - ], - direction: 'ltr', - fields: { - linkType: 'custom', - newTab: false, - url: '/admin', - }, - format: '', - indent: 0, - version: 2, - }, - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: '.', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, - size: 'oneThird', - }, - { - enableLink: false, - richText: { - root: { - type: 'root', - children: [ - { - type: 'heading', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Preview', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - tag: 'h3', - version: 1, - }, - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Using versions, drafts, and preview, editors can review and share their changes before publishing them.', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, - size: 'oneThird', - }, - { - enableLink: false, - richText: { - root: { - type: 'root', - children: [ - { - type: 'heading', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Page Builder', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - tag: 'h3', - version: 1, - }, - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Custom page builder allows you to create unique page, post, and project layouts for any type of content.', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, - size: 'oneThird', - }, - { - enableLink: false, - richText: { - root: { - type: 'root', - children: [ - { - type: 'heading', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'SEO', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - tag: 'h3', - version: 1, - }, - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Editors have complete control over SEO data and site content directly from the ', - version: 1, - }, - { - type: 'link', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'admin dashboard', - version: 1, - }, - ], - direction: 'ltr', - fields: { - linkType: 'custom', - newTab: false, - url: '/admin', - }, - format: '', - indent: 0, - version: 2, - }, - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: '.', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, - size: 'oneThird', - }, - { - enableLink: false, - richText: { - root: { - type: 'root', - children: [ - { - type: 'heading', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Dark Mode', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - tag: 'h3', - version: 1, - }, - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Users will experience this site in their preferred color scheme and each block can be inverted.', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, - size: 'oneThird', - }, - ], - }, - { - blockName: 'Media Block', - blockType: 'mediaBlock', - // @ts-ignore - media: '{{IMAGE_2}}', - position: 'default', - }, - { - blockName: 'Archive Block', - blockType: 'archive', - categories: [], - introContent: { - root: { - type: 'root', - children: [ - { - type: 'heading', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Recent posts', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - tag: 'h3', - version: 1, - }, - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'The posts below are displayed in an "Archive" layout building block which is an extremely powerful way to display documents on a page. It can be auto-populated by collection or by category, or posts can be individually selected. Pagination controls will automatically appear if the number of results exceeds the number of items per page.', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, - populateBy: 'collection', - relationTo: 'posts', - }, - { - blockName: 'CTA', - blockType: 'cta', - links: [ - { - link: { - type: 'custom', - appearance: 'default', - label: 'All posts', - url: '/posts', - }, - }, - ], - richText: { - root: { - type: 'root', - children: [ - { - type: 'heading', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'This is a call to action', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - tag: 'h3', - version: 1, - }, - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'This is a custom layout building block ', - version: 1, - }, - { - type: 'link', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'configured in the admin dashboard', - version: 1, - }, - ], - direction: 'ltr', - fields: { - linkType: 'custom', - newTab: false, - url: '/admin', - }, - format: '', - indent: 0, - version: 2, - }, - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: '.', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, - }, - ], - meta: { - description: 'An open-source website built with Payload and Next.js.', - // @ts-ignore - image: '{{IMAGE_1}}', - title: 'Payload Website Template', - }, - title: 'Home', -} diff --git a/src/endpoints/seed/image-1.ts b/src/endpoints/seed/image-1.ts deleted file mode 100644 index 8c71808..0000000 --- a/src/endpoints/seed/image-1.ts +++ /dev/null @@ -1,67 +0,0 @@ -import type { Media } from '@/payload-types' - -export const image1: Omit = { - alt: 'Curving abstract shapes with an orange and blue gradient', - caption: { - root: { - type: 'root', - children: [ - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Photo by ', - version: 1, - }, - { - type: 'link', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Andrew Kliatskyi', - version: 1, - }, - ], - direction: 'ltr', - fields: { - linkType: 'custom', - newTab: true, - url: 'https://unsplash.com/@kirp', - }, - format: '', - indent: 0, - version: 2, - }, - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: ' on Unsplash.', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, -} diff --git a/src/endpoints/seed/image-2.ts b/src/endpoints/seed/image-2.ts deleted file mode 100644 index 34993b5..0000000 --- a/src/endpoints/seed/image-2.ts +++ /dev/null @@ -1,67 +0,0 @@ -import type { Media } from '@/payload-types' - -export const image2: Omit = { - alt: 'Curving abstract shapes with an orange and blue gradient', - caption: { - root: { - type: 'root', - children: [ - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Photo by ', - version: 1, - }, - { - type: 'link', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Andrew Kliatskyi', - version: 1, - }, - ], - direction: 'ltr', - fields: { - linkType: 'custom', - newTab: true, - url: 'https://unsplash.com/@kirp', - }, - format: '', - indent: 0, - version: 2, - }, - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: ' on Unsplash.', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, -} diff --git a/src/endpoints/seed/image-hero1.webp b/src/endpoints/seed/image-hero1.webp deleted file mode 100644 index e69de29..0000000 diff --git a/src/endpoints/seed/image-post1.webp b/src/endpoints/seed/image-post1.webp deleted file mode 100644 index e69de29..0000000 diff --git a/src/endpoints/seed/image-post2.webp b/src/endpoints/seed/image-post2.webp deleted file mode 100644 index e69de29..0000000 diff --git a/src/endpoints/seed/image-post3.webp b/src/endpoints/seed/image-post3.webp deleted file mode 100644 index e69de29..0000000 diff --git a/src/endpoints/seed/index.ts b/src/endpoints/seed/index.ts deleted file mode 100644 index 4880c27..0000000 --- a/src/endpoints/seed/index.ts +++ /dev/null @@ -1,362 +0,0 @@ -import type { CollectionSlug, GlobalSlug, Payload, PayloadRequest } from 'payload' - -import fs from 'fs' -import path from 'path' -import { fileURLToPath } from 'url' - -import { contactForm as contactFormData } from './contact-form' -import { contact as contactPageData } from './contact-page' -import { home } from './home' -import { image1 } from './image-1' -import { image2 } from './image-2' -import { post1 } from './post-1' -import { post2 } from './post-2' -import { post3 } from './post-3' - -const filename = fileURLToPath(import.meta.url) -const dirname = path.dirname(filename) - -const collections: CollectionSlug[] = [ - 'categories', - 'media', - 'pages', - 'posts', - 'forms', - 'form-submissions', - 'search', -] -const globals: GlobalSlug[] = ['header', 'footer'] - -// Next.js revalidation errors are normal when seeding the database without a server running -// i.e. running `yarn seed` locally instead of using the admin UI within an active app -// The app is not running to revalidate the pages and so the API routes are not available -// These error messages can be ignored: `Error hitting revalidate route for...` -export const seed = async ({ - payload, - req, -}: { - payload: Payload - req: PayloadRequest -}): Promise => { - payload.logger.info('Seeding database...') - - // we need to clear the media directory before seeding - // as well as the collections and globals - // this is because while `yarn seed` drops the database - // the custom `/api/seed` endpoint does not - - payload.logger.info(`— Clearing media...`) - - const mediaDir = path.resolve(dirname, '../../public/media') - if (fs.existsSync(mediaDir)) { - fs.rmdirSync(mediaDir, { recursive: true }) - } - - payload.logger.info(`— Clearing collections and globals...`) - - // clear the database - for (const global of globals) { - await payload.updateGlobal({ - slug: global, - - data: { - // @ts-ignore - navItems: [], - }, - req, - }) - } - - for (const collection of collections) { - await payload.delete({ - collection: collection, - where: { - id: { - exists: true, - }, - }, - req, - }) - } - - const pages = await payload.delete({ - collection: 'pages', - where: {}, - req, - }) - - payload.logger.info(`— Seeding demo author and user...`) - - await payload.delete({ - collection: 'users', - where: { - email: { - equals: 'demo-author@payloadcms.com', - }, - }, - req, - }) - - const demoAuthor = await payload.create({ - collection: 'users', - data: { - name: 'Demo Author', - email: 'demo-author@payloadcms.com', - password: 'password', - }, - req, - }) - - let demoAuthorID: number | string = demoAuthor.id - - payload.logger.info(`— Seeding media...`) - const image1Doc = await payload.create({ - collection: 'media', - data: image1, - filePath: path.resolve(dirname, 'image-post1.webp'), - req, - }) - const image2Doc = await payload.create({ - collection: 'media', - data: image2, - filePath: path.resolve(dirname, 'image-post2.webp'), - req, - }) - const image3Doc = await payload.create({ - collection: 'media', - data: image2, - filePath: path.resolve(dirname, 'image-post3.webp'), - req, - }) - const imageHomeDoc = await payload.create({ - collection: 'media', - data: image2, - filePath: path.resolve(dirname, 'image-hero1.webp'), - req, - }) - - payload.logger.info(`— Seeding categories...`) - const technologyCategory = await payload.create({ - collection: 'categories', - data: { - title: 'Technology', - }, - req, - }) - - const newsCategory = await payload.create({ - collection: 'categories', - data: { - title: 'News', - }, - req, - }) - - const financeCategory = await payload.create({ - collection: 'categories', - data: { - title: 'Finance', - }, - req, - }) - - await payload.create({ - collection: 'categories', - data: { - title: 'Design', - }, - req, - }) - - await payload.create({ - collection: 'categories', - data: { - title: 'Software', - }, - req, - }) - - await payload.create({ - collection: 'categories', - data: { - title: 'Engineering', - }, - req, - }) - - let image1ID: number | string = image1Doc.id - let image2ID: number | string = image2Doc.id - let image3ID: number | string = image3Doc.id - let imageHomeID: number | string = imageHomeDoc.id - - if (payload.db.defaultIDType === 'text') { - image1ID = `"${image1Doc.id}"` - image2ID = `"${image2Doc.id}"` - image3ID = `"${image3Doc.id}"` - imageHomeID = `"${imageHomeDoc.id}"` - demoAuthorID = `"${demoAuthorID}"` - } - - payload.logger.info(`— Seeding posts...`) - - // Do not create posts with `Promise.all` because we want the posts to be created in order - // This way we can sort them by `createdAt` or `publishedAt` and they will be in the expected order - const post1Doc = await payload.create({ - collection: 'posts', - data: JSON.parse( - JSON.stringify({ ...post1, categories: [technologyCategory.id] }) - .replace(/"\{\{IMAGE_1\}\}"/g, String(image1ID)) - .replace(/"\{\{IMAGE_2\}\}"/g, String(image2ID)) - .replace(/"\{\{AUTHOR\}\}"/g, String(demoAuthorID)), - ), - req, - }) - - const post2Doc = await payload.create({ - collection: 'posts', - data: JSON.parse( - JSON.stringify({ ...post2, categories: [newsCategory.id] }) - .replace(/"\{\{IMAGE_1\}\}"/g, String(image2ID)) - .replace(/"\{\{IMAGE_2\}\}"/g, String(image3ID)) - .replace(/"\{\{AUTHOR\}\}"/g, String(demoAuthorID)), - ), - req, - }) - - const post3Doc = await payload.create({ - collection: 'posts', - data: JSON.parse( - JSON.stringify({ ...post3, categories: [financeCategory.id] }) - .replace(/"\{\{IMAGE_1\}\}"/g, String(image3ID)) - .replace(/"\{\{IMAGE_2\}\}"/g, String(image1ID)) - .replace(/"\{\{AUTHOR\}\}"/g, String(demoAuthorID)), - ), - req, - }) - - // update each post with related posts - await payload.update({ - id: post1Doc.id, - collection: 'posts', - data: { - relatedPosts: [post2Doc.id, post3Doc.id], - }, - req, - }) - await payload.update({ - id: post2Doc.id, - collection: 'posts', - data: { - relatedPosts: [post1Doc.id, post3Doc.id], - }, - req, - }) - await payload.update({ - id: post3Doc.id, - collection: 'posts', - data: { - relatedPosts: [post1Doc.id, post2Doc.id], - }, - req, - }) - - payload.logger.info(`— Seeding home page...`) - - await payload.create({ - collection: 'pages', - data: JSON.parse( - JSON.stringify(home) - .replace(/"\{\{IMAGE_1\}\}"/g, String(imageHomeID)) - .replace(/"\{\{IMAGE_2\}\}"/g, String(image2ID)), - ), - req, - }) - - payload.logger.info(`— Seeding contact form...`) - - const contactForm = await payload.create({ - collection: 'forms', - data: JSON.parse(JSON.stringify(contactFormData)), - req, - }) - - let contactFormID: number | string = contactForm.id - - if (payload.db.defaultIDType === 'text') { - contactFormID = `"${contactFormID}"` - } - - payload.logger.info(`— Seeding contact page...`) - - const contactPage = await payload.create({ - collection: 'pages', - data: JSON.parse( - JSON.stringify(contactPageData).replace(/"\{\{CONTACT_FORM_ID\}\}"/g, String(contactFormID)), - ), - req, - }) - - payload.logger.info(`— Seeding header...`) - - await payload.updateGlobal({ - slug: 'header', - data: { - navItems: [ - { - link: { - type: 'custom', - label: 'Posts', - url: '/posts', - }, - }, - { - link: { - type: 'reference', - label: 'Contact', - reference: { - relationTo: 'pages', - value: contactPage.id, - }, - }, - }, - ], - }, - req, - }) - - payload.logger.info(`— Seeding footer...`) - - await payload.updateGlobal({ - slug: 'footer', - data: { - navItems: [ - { - link: { - type: 'custom', - label: 'Admin', - url: '/admin', - }, - }, - { - link: { - type: 'custom', - label: 'Source Code', - newTab: true, - url: 'https://github.com/payloadcms/payload/tree/beta/templates/website', - }, - }, - { - link: { - type: 'custom', - label: 'Payload', - newTab: true, - url: 'https://payloadcms.com/', - }, - }, - ], - }, - req, - }) - - payload.logger.info('Seeded database successfully!') -} diff --git a/src/endpoints/seed/post-1.ts b/src/endpoints/seed/post-1.ts deleted file mode 100644 index f8235e2..0000000 --- a/src/endpoints/seed/post-1.ts +++ /dev/null @@ -1,305 +0,0 @@ -import type { Post } from '@/payload-types' - -export const post1: Partial = { - slug: 'digital-horizons', - _status: 'published', - // @ts-ignore - authors: ['{{AUTHOR}}'], - content: { - root: { - type: 'root', - children: [ - { - type: 'heading', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Dive into the marvels of modern innovation, where the only constant is change. A journey where pixels and data converge to craft the future.', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - tag: 'h2', - version: 1, - }, - { - type: 'block', - fields: { - blockName: 'Disclaimer', - blockType: 'banner', - content: { - root: { - type: 'root', - children: [ - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 1, - mode: 'normal', - style: '', - text: 'Disclaimer:', - version: 1, - }, - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: ' This content is fabricated and for demonstration purposes only. To edit this post, ', - version: 1, - }, - { - type: 'link', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'navigate to the admin dashboard', - version: 1, - }, - ], - direction: 'ltr', - fields: { - linkType: 'custom', - newTab: true, - url: '/admin', - }, - format: '', - indent: 0, - version: 3, - }, - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: '.', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, - style: 'info', - }, - format: '', - version: 2, - }, - { - type: 'heading', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'The Rise of AI and Machine Learning', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - tag: 'h2', - version: 1, - }, - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'We find ourselves in a transformative era where artificial intelligence (AI) stands at the forefront of technological evolution. The ripple effects of its advancements are reshaping industries at an unprecedented pace. No longer are businesses bound by the limitations of tedious, manual processes. Instead, sophisticated machines, fueled by vast amounts of historical data, are now capable of making decisions previously left to human intuition. These intelligent systems are not only optimizing operations but also pioneering innovative approaches, heralding a new age of business transformation worldwide. ', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - { - type: 'heading', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'To demonstrate basic AI functionality, here is a javascript snippet that makes a POST request to a generic AI API in order to generate text based on a prompt. ', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - tag: 'h4', - version: 1, - }, - { - type: 'block', - fields: { - blockName: 'Generate Text', - blockType: 'code', - code: "async function generateText(prompt) {\n const apiKey = 'your-api-key';\n const apiUrl = 'https://api.example.com/generate-text';\n\n const response = await fetch(apiUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`\n },\n body: JSON.stringify({\n model: 'text-generation-model',\n prompt: prompt,\n max_tokens: 50\n })\n });\n\n const data = await response.json();\n console.log(data.choices[0].text.trim());\n}\n\n// Example usage\ngenerateText(\"Once upon a time in a faraway land,\");\n", - language: 'javascript', - }, - format: '', - version: 2, - }, - { - type: 'heading', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'IoT: Connecting the World Around Us', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - tag: 'h2', - version: 1, - }, - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: "In today's rapidly evolving technological landscape, the Internet of Things (IoT) stands out as a revolutionary force. From transforming our residences with smart home systems to redefining transportation through connected cars, IoT's influence is palpable in nearly every facet of our daily lives.", - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: "This technology hinges on the seamless integration of devices and systems, allowing them to communicate and collaborate effortlessly. With each connected device, we move a step closer to a world where convenience and efficiency are embedded in the very fabric of our existence. As a result, we're transitioning into an era where our surroundings intuitively respond to our needs, heralding a smarter and more interconnected global community.", - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - { - type: 'block', - fields: { - blockName: '', - blockType: 'mediaBlock', - media: '{{IMAGE_2}}', - position: 'default', - }, - format: '', - version: 2, - }, - { - type: 'block', - fields: { - blockName: 'Dynamic Components', - blockType: 'banner', - content: { - root: { - type: 'root', - children: [ - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: "This content above is completely dynamic using custom layout building blocks configured in the CMS. This can be anything you'd like from rich text and images, to highly designed, complex components.", - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, - style: 'info', - }, - format: '', - version: 2, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, - - meta: { - description: - 'Dive into the marvels of modern innovation, where the only constant is change. A journey where pixels and data converge to craft the future.', - // @ts-ignore - image: '{{IMAGE_1}}', - title: 'Digital Horizons: A Glimpse into Tomorrow', - }, - relatedPosts: [], // this is populated by the seed script - title: 'Digital Horizons: A Glimpse into Tomorrow', -} diff --git a/src/endpoints/seed/post-2.ts b/src/endpoints/seed/post-2.ts deleted file mode 100644 index 5d2ba32..0000000 --- a/src/endpoints/seed/post-2.ts +++ /dev/null @@ -1,227 +0,0 @@ -import type { Post } from '@/payload-types' - -export const post2: Partial = { - slug: 'global-gaze', - _status: 'published', - // @ts-ignore - authors: ['{{AUTHOR}}'], - content: { - root: { - type: 'root', - children: [ - { - type: 'heading', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Explore the untold and overlooked. A magnified view into the corners of the world, where every story deserves its spotlight.', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - tag: 'h2', - version: 1, - }, - { - type: 'block', - fields: { - blockName: 'Disclaimer', - blockType: 'banner', - content: { - root: { - type: 'root', - children: [ - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 1, - mode: 'normal', - style: '', - text: 'Disclaimer:', - version: 1, - }, - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: ' This content is fabricated and for demonstration purposes only. To edit this post, ', - version: 1, - }, - { - type: 'link', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'navigate to the admin dashboard.', - version: 1, - }, - ], - direction: 'ltr', - fields: { - linkType: 'custom', - newTab: true, - url: '/admin', - }, - format: '', - indent: 0, - version: 3, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 1, - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, - style: 'info', - }, - format: '', - version: 2, - }, - { - type: 'heading', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'The Power of Resilience: Stories of Recovery and Hope', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - tag: 'h2', - version: 1, - }, - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: "Throughout history, regions across the globe have faced the devastating impact of natural disasters, the turbulence of political unrest, and the challenging ripples of economic downturns. In these moments of profound crisis, an often-underestimated force emerges: the indomitable resilience of the human spirit. These aren't just tales of mere survival, but stories of communities forging bonds, uniting with a collective purpose, and demonstrating an innate ability to overcome.", - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - { - type: 'block', - fields: { - blockName: '', - blockType: 'mediaBlock', - media: '{{IMAGE_2}}', - position: 'default', - }, - format: '', - version: 2, - }, - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'From neighbors forming makeshift rescue teams during floods to entire cities rallying to rebuild after economic collapse, the essence of humanity is most evident in these acts of solidarity. As we delve into these narratives, we witness the transformative power of community spirit, where adversity becomes a catalyst for growth, unity, and a brighter, rebuilt future.', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - { - type: 'block', - fields: { - blockName: 'Dynamic components', - blockType: 'banner', - content: { - root: { - type: 'root', - children: [ - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: "This content above is completely dynamic using custom layout building blocks configured in the CMS. This can be anything you'd like from rich text and images, to highly designed, complex components.", - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, - style: 'info', - }, - format: '', - version: 2, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, - meta: { - description: - 'Explore the untold and overlooked. A magnified view into the corners of the world, where every story deserves its spotlight.', - // @ts-ignore - image: '{{IMAGE_1}}', - title: 'Global Gaze: Beyond the Headlines', - }, - relatedPosts: [], // this is populated by the seed script - title: 'Global Gaze: Beyond the Headlines', -} diff --git a/src/endpoints/seed/post-3.ts b/src/endpoints/seed/post-3.ts deleted file mode 100644 index d2700cf..0000000 --- a/src/endpoints/seed/post-3.ts +++ /dev/null @@ -1,263 +0,0 @@ -import type { Post } from '@/payload-types' - -export const post3: Partial = { - slug: 'dollar-and-sense-the-financial-forecast', - _status: 'published', - // @ts-ignore - authors: ['{{AUTHOR}}'], - content: { - root: { - type: 'root', - children: [ - { - type: 'block', - fields: { - blockName: 'Disclaimer', - blockType: 'banner', - content: { - root: { - type: 'root', - children: [ - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 1, - mode: 'normal', - style: '', - text: 'Disclaimer: ', - version: 1, - }, - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'This content is fabricated and for demonstration purposes only. To edit this post, ', - version: 1, - }, - { - type: 'link', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'navigate to the admin dashboard.', - version: 1, - }, - ], - direction: 'ltr', - fields: { - linkType: 'custom', - newTab: true, - url: '/admin', - }, - format: '', - indent: 0, - version: 3, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 1, - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, - style: 'info', - }, - format: '', - version: 2, - }, - { - type: 'heading', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: "Money isn't just currency; ", - version: 1, - }, - { - type: 'text', - detail: 0, - format: 2, - mode: 'normal', - style: '', - text: "it's a language. ", - version: 1, - }, - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Dive deep into its nuances, where strategy meets intuition in the vast sea of finance.', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - tag: 'h2', - version: 1, - }, - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: "Money, in its essence, transcends the mere concept of coins and paper notes; it becomes a profound language that speaks of value, trust, and societal structures. Like any language, it possesses intricate nuances and subtleties that require a discerning understanding. It's in these depths where the calculated world of financial strategy collides with the raw, instinctive nature of human intuition. Just as a seasoned linguist might dissect the syntax and semantics of a sentence, a financial expert navigates the vast and tumultuous ocean of finance, guided not only by logic and data but also by gut feelings and foresight. Every transaction, investment, and financial decision becomes a dialogue in this expansive lexicon of commerce and value.", - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - { - type: 'block', - fields: { - blockName: '', - blockType: 'mediaBlock', - media: '{{IMAGE_2}}', - position: 'default', - }, - format: '', - version: 2, - }, - { - type: 'heading', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Stock Market Dynamics: Bulls, Bears, and the Uncertain Middle', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - tag: 'h2', - version: 1, - }, - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'The stock market is a realm of vast opportunity but also poses risks. Discover the forces that drive market trends and the strategies employed by top traders to navigate this complex ecosystem. From market analysis to understanding investor psychology, get a comprehensive insight into the world of stocks.', - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: "The stock market, often visualized as a bustling arena of numbers and ticker tapes, is as much about human behavior as it is about economics. It's a place where optimism, represented by the bullish rally, meets the caution of bearish downturns, with each vying to dictate the market's direction. But between these two extremes lies an uncertain middle ground, a zone populated by traders and investors who constantly weigh hope against fear. Successful navigation requires more than just financial acumen; it demands an understanding of collective sentiments and the ability to predict not just market movements, but also the reactions of other market participants. In this intricate dance of numbers and nerves, the most astute players are those who master both the hard data and the soft nuances of human behavior.", - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - { - type: 'block', - fields: { - blockName: 'Dynamic components', - blockType: 'banner', - content: { - root: { - type: 'root', - children: [ - { - type: 'paragraph', - children: [ - { - type: 'text', - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: "This content above is completely dynamic using custom layout building blocks configured in the CMS. This can be anything you'd like from rich text and images, to highly designed, complex components.", - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - textFormat: 0, - version: 1, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, - style: 'info', - }, - format: '', - version: 2, - }, - ], - direction: 'ltr', - format: '', - indent: 0, - version: 1, - }, - }, - meta: { - description: `Money isn't just currency; it's a language. Dive deep into its nuances, where strategy meets intuition in the vast sea of finance.`, - // @ts-ignore - image: '{{IMAGE_1}}', - title: 'Dollar and Sense: The Financial Forecast', - }, - relatedPosts: [], // this is populated by the seed script - title: 'Dollar and Sense: The Financial Forecast', -} diff --git a/src/endpoints/seedHandler.ts b/src/endpoints/seedHandler.ts deleted file mode 100644 index bd5d813..0000000 --- a/src/endpoints/seedHandler.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { type PayloadHandler, commitTransaction, initTransaction } from 'payload' - -import { seed as seedScript } from '@/endpoints/seed' - -export const seedHandler: PayloadHandler = async (req): Promise => { - const { payload, user } = req - - if (!user) { - return Response.json({ error: 'Unauthorized' }, { status: 401 }) - } - - try { - // Create a transaction so that all seeding happens in one transaction - await initTransaction(req) - - await seedScript({ payload, req }) - - // Finalise transactiojn - await commitTransaction(req) - - return Response.json({ success: true }) - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Unknown error' - payload.logger.error(message) - return Response.json({ error: message }, { status: 500 }) - } -} diff --git a/src/fields/link.ts b/src/fields/link.ts index 9a6d2c2..83b85cf 100644 --- a/src/fields/link.ts +++ b/src/fields/link.ts @@ -68,17 +68,6 @@ export const link: LinkType = ({ appearances, disableLabel = false, overrides = } const linkTypes: Field[] = [ - { - name: 'reference', - type: 'relationship', - admin: { - condition: (_, siblingData) => siblingData?.type === 'reference', - }, - label: 'Document to link to', - maxDepth: 1, - relationTo: ['pages'], - required: true, - }, { name: 'url', type: 'text', diff --git a/src/heros/HighImpact/index.tsx b/src/heros/HighImpact/index.tsx deleted file mode 100644 index 466998e..0000000 --- a/src/heros/HighImpact/index.tsx +++ /dev/null @@ -1,46 +0,0 @@ -'use client' -import { useHeaderTheme } from '@/providers/HeaderTheme' -import React, { useEffect } from 'react' - -import type { Page } from '@/payload-types' - -import { CMSLink } from '@/components/Link' -import { Media } from '@/components/Media' -import RichText from '@/components/RichText' - -export const HighImpactHero: React.FC = ({ links, media, richText }) => { - const { setHeaderTheme } = useHeaderTheme() - - useEffect(() => { - setHeaderTheme('dark') - }) - - return ( -
-
-
- {richText && } - {Array.isArray(links) && links.length > 0 && ( -
    - {links.map(({ link }, i) => { - return ( -
  • - -
  • - ) - })} -
- )} -
-
-
- {media && typeof media === 'object' && ( - - -
- - )} -
-
- ) -} diff --git a/src/heros/LowImpact/index.tsx b/src/heros/LowImpact/index.tsx deleted file mode 100644 index 2b10bcc..0000000 --- a/src/heros/LowImpact/index.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react' - -import type { Page } from '@/payload-types' - -import RichText from '@/components/RichText' - -type LowImpactHeroType = - | { - children?: React.ReactNode - richText?: never - } - | (Omit & { - children?: never - richText?: Page['hero']['richText'] - }) - -export const LowImpactHero: React.FC = ({ children, richText }) => { - return ( -
-
- {children || (richText && )} -
-
- ) -} diff --git a/src/heros/MediumImpact/index.tsx b/src/heros/MediumImpact/index.tsx deleted file mode 100644 index 52c54da..0000000 --- a/src/heros/MediumImpact/index.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React from 'react' - -import type { Page } from '@/payload-types' - -import { CMSLink } from '@/components/Link' -import { Media } from '@/components/Media' -import RichText from '@/components/RichText' - -export const MediumImpactHero: React.FC = ({ links, media, richText }) => { - return ( -
-
- {richText && } - - {Array.isArray(links) && links.length > 0 && ( -
    - {links.map(({ link }, i) => { - return ( -
  • - -
  • - ) - })} -
- )} -
-
- {media && typeof media === 'object' && ( -
- - {media?.caption && ( -
- -
- )} -
- )} -
-
- ) -} diff --git a/src/heros/PostHero/index.tsx b/src/heros/PostHero/index.tsx deleted file mode 100644 index 279afb2..0000000 --- a/src/heros/PostHero/index.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { formatDateTime } from 'src/utilities/formatDateTime' -import React from 'react' - -import type { Post } from '@/payload-types' - -import { Media } from '@/components/Media' - -export const PostHero: React.FC<{ - post: Post -}> = ({ post }) => { - const { categories, meta: { image: metaImage } = {}, populatedAuthors, publishedAt, title } = post - - return ( -
-
-
-
- {categories?.map((category, index) => { - if (typeof category === 'object' && category !== null) { - const { title: categoryTitle } = category - - const titleToUse = categoryTitle || 'Untitled category' - - const isLast = index === categories.length - 1 - - return ( - - {titleToUse} - {!isLast && ,  } - - ) - } - return null - })} -
- -
-

{title}

-
- -
-
- {populatedAuthors && ( -
-

Author

- {populatedAuthors.map((author, index) => { - const { name } = author - - const isLast = index === populatedAuthors.length - 1 - const secondToLast = index === populatedAuthors.length - 2 - - return ( - - {name} - {secondToLast && populatedAuthors.length > 2 && ( - , - )} - {secondToLast && populatedAuthors.length === 2 && ( - - )} - {!isLast && populatedAuthors.length > 1 && ( - and - )} - - ) - })} -
- )} -
- {publishedAt && ( -
-

Date Published

- - -
- )} -
-
-
-
- {metaImage && typeof metaImage !== 'string' && ( - - )} -
-
-
- ) -} diff --git a/src/heros/RenderHero.tsx b/src/heros/RenderHero.tsx deleted file mode 100644 index 00ca702..0000000 --- a/src/heros/RenderHero.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react' - -import type { Page } from '@/payload-types' - -import { HighImpactHero } from '@/heros/HighImpact' -import { LowImpactHero } from '@/heros/LowImpact' -import { MediumImpactHero } from '@/heros/MediumImpact' - -const heroes = { - highImpact: HighImpactHero, - lowImpact: LowImpactHero, - mediumImpact: MediumImpactHero, -} - -export const RenderHero: React.FC = (props) => { - const { type } = props || {} - - if (!type || type === 'none') return null - - const HeroToRender = heroes[type] - - if (!HeroToRender) return null - - return -} diff --git a/src/heros/config.ts b/src/heros/config.ts deleted file mode 100644 index 7bc2f24..0000000 --- a/src/heros/config.ts +++ /dev/null @@ -1,72 +0,0 @@ -import type { Field } from 'payload' - -import { - FixedToolbarFeature, - HeadingFeature, - InlineToolbarFeature, - lexicalEditor, -} from '@payloadcms/richtext-lexical' - -import { linkGroup } from '@/fields/linkGroup' - -export const hero: Field = { - name: 'hero', - type: 'group', - fields: [ - { - name: 'type', - type: 'select', - defaultValue: 'lowImpact', - label: 'Type', - options: [ - { - label: 'None', - value: 'none', - }, - { - label: 'High Impact', - value: 'highImpact', - }, - { - label: 'Medium Impact', - value: 'mediumImpact', - }, - { - label: 'Low Impact', - value: 'lowImpact', - }, - ], - required: true, - }, - { - name: 'richText', - type: 'richText', - editor: lexicalEditor({ - features: ({ rootFeatures }) => { - return [ - ...rootFeatures, - HeadingFeature({ enabledHeadingSizes: ['h1', 'h2', 'h3', 'h4'] }), - FixedToolbarFeature(), - InlineToolbarFeature(), - ] - }, - }), - label: false, - }, - linkGroup({ - overrides: { - maxRows: 2, - }, - }), - { - name: 'media', - type: 'upload', - admin: { - condition: (_, { type } = {}) => ['highImpact', 'mediumImpact'].includes(type), - }, - relationTo: 'media', - required: true, - }, - ], - label: false, -} diff --git a/src/payload-types.ts b/src/payload-types.ts index 71f49a3..1a2f257 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -11,8 +11,6 @@ export interface Config { users: UserAuthOperations; }; collections: { - pages: Page; - posts: Post; media: Media; categories: Category; users: User; @@ -22,9 +20,6 @@ export interface Config { 'talks-and-roundtables': TalksAndRoundtable; 'case-studies': CaseStudy; redirects: Redirect; - forms: Form; - 'form-submissions': FormSubmission; - search: Search; 'payload-locked-documents': PayloadLockedDocument; 'payload-preferences': PayloadPreference; 'payload-migrations': PayloadMigration; @@ -61,61 +56,6 @@ export interface UserAuthOperations { password: string; }; } -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "pages". - */ -export interface Page { - id: string; - title: string; - hero: { - type: 'none' | 'highImpact' | 'mediumImpact' | 'lowImpact'; - richText?: { - root: { - type: string; - children: { - type: string; - version: number; - [k: string]: unknown; - }[]; - direction: ('ltr' | 'rtl') | null; - format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; - indent: number; - version: number; - }; - [k: string]: unknown; - } | null; - links?: - | { - link: { - type?: ('reference' | 'custom') | null; - newTab?: boolean | null; - reference?: { - relationTo: 'pages'; - value: string | Page; - } | null; - url?: string | null; - label: string; - appearance?: ('default' | 'outline') | null; - }; - id?: string | null; - }[] - | null; - media?: (string | null) | Media; - }; - layout: (CallToActionBlock | ContentBlock | MediaBlock | ArchiveBlock | FormBlock)[]; - meta?: { - title?: string | null; - image?: (string | null) | Media; - description?: string | null; - }; - publishedAt?: string | null; - slug?: string | null; - slugLock?: boolean | null; - updatedAt: string; - createdAt: string; - _status?: ('draft' | 'published') | null; -} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "media". @@ -150,133 +90,6 @@ export interface Media { focalX?: number | null; focalY?: number | null; } -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "CallToActionBlock". - */ -export interface CallToActionBlock { - richText?: { - root: { - type: string; - children: { - type: string; - version: number; - [k: string]: unknown; - }[]; - direction: ('ltr' | 'rtl') | null; - format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; - indent: number; - version: number; - }; - [k: string]: unknown; - } | null; - links?: - | { - link: { - type?: ('reference' | 'custom') | null; - newTab?: boolean | null; - reference?: { - relationTo: 'pages'; - value: string | Page; - } | null; - url?: string | null; - label: string; - appearance?: ('default' | 'outline') | null; - }; - id?: string | null; - }[] - | null; - id?: string | null; - blockName?: string | null; - blockType: 'cta'; -} -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "ContentBlock". - */ -export interface ContentBlock { - columns?: - | { - size?: ('oneThird' | 'half' | 'twoThirds' | 'full') | null; - richText?: { - root: { - type: string; - children: { - type: string; - version: number; - [k: string]: unknown; - }[]; - direction: ('ltr' | 'rtl') | null; - format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; - indent: number; - version: number; - }; - [k: string]: unknown; - } | null; - enableLink?: boolean | null; - link?: { - type?: ('reference' | 'custom') | null; - newTab?: boolean | null; - reference?: { - relationTo: 'pages'; - value: string | Page; - } | null; - url?: string | null; - label: string; - appearance?: ('default' | 'outline') | null; - }; - id?: string | null; - }[] - | null; - id?: string | null; - blockName?: string | null; - blockType: 'content'; -} -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "MediaBlock". - */ -export interface MediaBlock { - position?: ('default' | 'fullscreen') | null; - media: string | Media; - id?: string | null; - blockName?: string | null; - blockType: 'mediaBlock'; -} -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "ArchiveBlock". - */ -export interface ArchiveBlock { - introContent?: { - root: { - type: string; - children: { - type: string; - version: number; - [k: string]: unknown; - }[]; - direction: ('ltr' | 'rtl') | null; - format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; - indent: number; - version: number; - }; - [k: string]: unknown; - } | null; - populateBy?: ('collection' | 'selection') | null; - relationTo?: 'posts' | null; - categories?: (string | Category)[] | null; - limit?: number | null; - selectedDocs?: - | { - relationTo: 'posts'; - value: string | Post; - }[] - | null; - id?: string | null; - blockName?: string | null; - blockType: 'archive'; -} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "categories". @@ -296,49 +109,6 @@ export interface Category { updatedAt: string; createdAt: string; } -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "posts". - */ -export interface Post { - id: string; - title: string; - content: { - root: { - type: string; - children: { - type: string; - version: number; - [k: string]: unknown; - }[]; - direction: ('ltr' | 'rtl') | null; - format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; - indent: number; - version: number; - }; - [k: string]: unknown; - }; - relatedPosts?: (string | Post)[] | null; - categories?: (string | Category)[] | null; - meta?: { - title?: string | null; - image?: (string | null) | Media; - description?: string | null; - }; - publishedAt?: string | null; - authors?: (string | User)[] | null; - populatedAuthors?: - | { - id?: string | null; - name?: string | null; - }[] - | null; - slug?: string | null; - slugLock?: boolean | null; - updatedAt: string; - createdAt: string; - _status?: ('draft' | 'published') | null; -} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "users". @@ -357,196 +127,6 @@ export interface User { lockUntil?: string | null; password?: string | null; } -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "FormBlock". - */ -export interface FormBlock { - form: string | Form; - enableIntro?: boolean | null; - introContent?: { - root: { - type: string; - children: { - type: string; - version: number; - [k: string]: unknown; - }[]; - direction: ('ltr' | 'rtl') | null; - format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; - indent: number; - version: number; - }; - [k: string]: unknown; - } | null; - id?: string | null; - blockName?: string | null; - blockType: 'formBlock'; -} -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "forms". - */ -export interface Form { - id: string; - title: string; - fields?: - | ( - | { - name: string; - label?: string | null; - width?: number | null; - required?: boolean | null; - defaultValue?: boolean | null; - id?: string | null; - blockName?: string | null; - blockType: 'checkbox'; - } - | { - name: string; - label?: string | null; - width?: number | null; - required?: boolean | null; - id?: string | null; - blockName?: string | null; - blockType: 'country'; - } - | { - name: string; - label?: string | null; - width?: number | null; - required?: boolean | null; - id?: string | null; - blockName?: string | null; - blockType: 'email'; - } - | { - message?: { - root: { - type: string; - children: { - type: string; - version: number; - [k: string]: unknown; - }[]; - direction: ('ltr' | 'rtl') | null; - format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; - indent: number; - version: number; - }; - [k: string]: unknown; - } | null; - id?: string | null; - blockName?: string | null; - blockType: 'message'; - } - | { - name: string; - label?: string | null; - width?: number | null; - defaultValue?: number | null; - required?: boolean | null; - id?: string | null; - blockName?: string | null; - blockType: 'number'; - } - | { - name: string; - label?: string | null; - width?: number | null; - defaultValue?: string | null; - options?: - | { - label: string; - value: string; - id?: string | null; - }[] - | null; - required?: boolean | null; - id?: string | null; - blockName?: string | null; - blockType: 'select'; - } - | { - name: string; - label?: string | null; - width?: number | null; - required?: boolean | null; - id?: string | null; - blockName?: string | null; - blockType: 'state'; - } - | { - name: string; - label?: string | null; - width?: number | null; - defaultValue?: string | null; - required?: boolean | null; - id?: string | null; - blockName?: string | null; - blockType: 'text'; - } - | { - name: string; - label?: string | null; - width?: number | null; - defaultValue?: string | null; - required?: boolean | null; - id?: string | null; - blockName?: string | null; - blockType: 'textarea'; - } - )[] - | null; - submitButtonLabel?: string | null; - confirmationType?: ('message' | 'redirect') | null; - confirmationMessage?: { - root: { - type: string; - children: { - type: string; - version: number; - [k: string]: unknown; - }[]; - direction: ('ltr' | 'rtl') | null; - format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; - indent: number; - version: number; - }; - [k: string]: unknown; - } | null; - redirect?: { - url: string; - }; - emails?: - | { - emailTo?: string | null; - cc?: string | null; - bcc?: string | null; - replyTo?: string | null; - emailFrom?: string | null; - subject: string; - message?: { - root: { - type: string; - children: { - type: string; - version: number; - [k: string]: unknown; - }[]; - direction: ('ltr' | 'rtl') | null; - format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; - indent: number; - version: number; - }; - [k: string]: unknown; - } | null; - id?: string | null; - }[] - | null; - updatedAt: string; - createdAt: string; -} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "authors". @@ -554,16 +134,43 @@ export interface Form { export interface Author { id: string; featuredImage?: (string | null) | Media; - name: string; + authorName: string; role: string; bio: string; - linkedIn?: string | null; - x?: string | null; - gitHub?: string | null; - medium?: string | null; + socials?: { + linkedIn?: + | { + url?: string | null; + id?: string | null; + }[] + | null; + x?: + | { + url?: string | null; + id?: string | null; + }[] + | null; + github?: + | { + url?: string | null; + id?: string | null; + }[] + | null; + medium?: + | { + url?: string | null; + id?: string | null; + }[] + | null; + }; publishedAt?: string | null; slug?: string | null; slugLock?: boolean | null; + meta?: { + title?: string | null; + image?: (string | null) | Media; + description?: string | null; + }; updatedAt: string; createdAt: string; _status?: ('draft' | 'published') | null; @@ -599,6 +206,11 @@ export interface Blogpost { publishedAt?: string | null; slug?: string | null; slugLock?: boolean | null; + meta?: { + title?: string | null; + image?: (string | null) | Media; + description?: string | null; + }; updatedAt: string; createdAt: string; _status?: ('draft' | 'published') | null; @@ -617,11 +229,16 @@ export interface Podcast { spotify?: string | null; apple?: string | null; authors: (string | Author)[]; - categories?: (string | Category)[] | null; + categories: (string | Category)[]; related?: (string | Podcast)[] | null; publishedAt?: string | null; slug?: string | null; slugLock?: boolean | null; + meta?: { + title?: string | null; + image?: (string | null) | Media; + description?: string | null; + }; updatedAt: string; createdAt: string; _status?: ('draft' | 'published') | null; @@ -638,7 +255,7 @@ export interface TalksAndRoundtable { url?: string | null; authors?: (string | Author)[] | null; publishedAt?: string | null; - featuredImage: string | Media; + featuredImage?: (string | null) | Media; slug?: string | null; slugLock?: boolean | null; updatedAt: string; @@ -674,63 +291,30 @@ export interface Redirect { type?: ('reference' | 'custom') | null; reference?: | ({ - relationTo: 'pages'; - value: string | Page; + relationTo: 'authors'; + value: string | Author; + } | null) + | ({ + relationTo: 'blogposts'; + value: string | Blogpost; + } | null) + | ({ + relationTo: 'podcasts'; + value: string | Podcast; } | null) | ({ - relationTo: 'posts'; - value: string | Post; + relationTo: 'talks-and-roundtables'; + value: string | TalksAndRoundtable; + } | null) + | ({ + relationTo: 'case-studies'; + value: string | CaseStudy; } | null); url?: string | null; }; updatedAt: string; createdAt: string; } -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "form-submissions". - */ -export interface FormSubmission { - id: string; - form: string | Form; - submissionData?: - | { - field: string; - value: string; - id?: string | null; - }[] - | null; - updatedAt: string; - createdAt: string; -} -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "search". - */ -export interface Search { - id: string; - title?: string | null; - priority?: number | null; - doc: { - relationTo: 'posts'; - value: string | Post; - }; - slug?: string | null; - meta?: { - title?: string | null; - description?: string | null; - image?: (string | null) | Media; - }; - categories?: - | { - relationTo?: string | null; - id?: string | null; - title?: string | null; - }[] - | null; - updatedAt: string; - createdAt: string; -} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-locked-documents". @@ -738,14 +322,6 @@ export interface Search { export interface PayloadLockedDocument { id: string; document?: - | ({ - relationTo: 'pages'; - value: string | Page; - } | null) - | ({ - relationTo: 'posts'; - value: string | Post; - } | null) | ({ relationTo: 'media'; value: string | Media; @@ -781,18 +357,6 @@ export interface PayloadLockedDocument { | ({ relationTo: 'redirects'; value: string | Redirect; - } | null) - | ({ - relationTo: 'forms'; - value: string | Form; - } | null) - | ({ - relationTo: 'form-submissions'; - value: string | FormSubmission; - } | null) - | ({ - relationTo: 'search'; - value: string | Search; } | null); globalSlug?: string | null; user: { @@ -847,10 +411,6 @@ export interface Header { link: { type?: ('reference' | 'custom') | null; newTab?: boolean | null; - reference?: { - relationTo: 'pages'; - value: string | Page; - } | null; url?: string | null; label: string; }; @@ -871,10 +431,6 @@ export interface Footer { link: { type?: ('reference' | 'custom') | null; newTab?: boolean | null; - reference?: { - relationTo: 'pages'; - value: string | Page; - } | null; url?: string | null; label: string; }; @@ -895,10 +451,6 @@ export interface Social { link: { type?: ('reference' | 'custom') | null; newTab?: boolean | null; - reference?: { - relationTo: 'pages'; - value: string | Page; - } | null; url?: string | null; label: string; }; @@ -919,31 +471,6 @@ export interface HomepageSetting { updatedAt?: string | null; createdAt?: string | null; } -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "BannerBlock". - */ -export interface BannerBlock { - style: 'info' | 'warning' | 'error' | 'success'; - content: { - root: { - type: string; - children: { - type: string; - version: number; - [k: string]: unknown; - }[]; - direction: ('ltr' | 'rtl') | null; - format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; - indent: number; - version: number; - }; - [k: string]: unknown; - }; - id?: string | null; - blockName?: string | null; - blockType: 'banner'; -} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "CodeBlock". diff --git a/src/payload.config.ts b/src/payload.config.ts index 6234b56..b6ba90d 100644 --- a/src/payload.config.ts +++ b/src/payload.config.ts @@ -1,7 +1,5 @@ // storage-adapter-import-placeholder import { mongooseAdapter } from '@payloadcms/db-mongodb' - -import { payloadCloudPlugin } from '@payloadcms/plugin-cloud' import { formBuilderPlugin } from '@payloadcms/plugin-form-builder' import { nestedDocsPlugin } from '@payloadcms/plugin-nested-docs' import { redirectsPlugin } from '@payloadcms/plugin-redirects' @@ -23,18 +21,12 @@ import { fileURLToPath } from 'url' import Categories from './collections/Categories' import { Media } from './collections/Media' -import { Pages } from './collections/Pages' -import { Posts } from './collections/Posts' import Users from './collections/Users' -import { seedHandler } from './endpoints/seedHandler' import { Footer } from '@/Globals/Footer/config' import { Header } from '@/Globals/Header/config' import { revalidateRedirects } from './hooks/revalidateRedirects' import { GenerateTitle, GenerateURL } from '@payloadcms/plugin-seo/types' -import { Page, Post } from 'src/payload-types' - -import { searchFields } from '@/search/fieldOverrides' -import { beforeSyncWithSearch } from '@/search/beforeSync' +import { Author, Blogpost, CaseStudy, Podcast, TalksAndRoundtable } from "src/payload-types"; import { Blogposts } from "@/collections/Blogposts"; import { Authors } from "@/collections/Authors"; import { CaseStudies } from "@/collections/CaseStudies"; @@ -48,11 +40,12 @@ import { Socials } from "@/collections/Globals/Socials/config"; const filename = fileURLToPath(import.meta.url) const dirname = path.dirname(filename) -const generateTitle: GenerateTitle = ({ doc }) => { - return doc?.title ? `${doc.title} | Payload Website Template` : 'Payload Website Template' +const generateTitle: GenerateTitle = ({ doc }) => { + // @ts-ignore + return doc?.title ? `Subvisual | ${doc.title}` : 'Payload Website Template' } -const generateURL: GenerateURL = ({ doc }) => { +const generateURL: GenerateURL = ({ doc }) => { return doc?.slug ? `${process.env.NEXT_PUBLIC_SERVER_URL!}/${doc.slug}` : process.env.NEXT_PUBLIC_SERVER_URL! @@ -64,9 +57,8 @@ export default buildConfig({ // The `BeforeLogin` component renders a message that you see while logging into your admin panel. // Feel free to delete this at any time. Simply remove the line below and the import `BeforeLogin` statement on line 15. beforeLogin: ['@/components/BeforeLogin'], - // The `BeforeDashboard` component renders the 'welcome' block that you see after logging into your admin panel. - // Feel free to delete this at any time. Simply remove the line below and the import `BeforeDashboard` statement on line 15. beforeDashboard: ['@/components/BeforeDashboard'], + }, importMap: { baseDir: path.resolve(dirname), @@ -103,7 +95,7 @@ export default buildConfig({ BoldFeature(), ItalicFeature(), LinkFeature({ - enabledCollections: ['pages', 'posts'], + enabledCollections: ['blogposts', 'podcasts', 'case-studies', 'talks-and-roundtables'], fields: ({ defaultFields }) => { const defaultFieldsWithoutUrl = defaultFields.filter((field) => { if ('name' in field && field.name === 'url') return false @@ -130,22 +122,13 @@ export default buildConfig({ db: mongooseAdapter({ url: process.env.DATABASE_URI || '', }), - collections: [Pages, Posts, Media, Categories, Users, Authors, Blogposts, Podcasts, TalksAndRoundtables, CaseStudies], + collections: [Media, Categories, Users, Authors, Blogposts, Podcasts, TalksAndRoundtables, CaseStudies], cors: [process.env.PAYLOAD_PUBLIC_SERVER_URL || ''].filter(Boolean), csrf: [process.env.PAYLOAD_PUBLIC_SERVER_URL || ''].filter(Boolean), - endpoints: [ - // The seed endpoint is used to populate the database with some example data - // You should delete this endpoint before deploying your site to production - { - handler: seedHandler, - method: 'get', - path: '/seed', - }, - ], globals: [Header, Footer, Socials, HomePageSettings], plugins: [ redirectsPlugin({ - collections: ['pages', 'posts'], + collections: ['authors', 'blogposts', 'podcasts', 'talks-and-roundtables', 'case-studies'], overrides: { // @ts-expect-error fields: ({ defaultFields }) => { @@ -173,41 +156,16 @@ export default buildConfig({ generateTitle, generateURL, }), - formBuilderPlugin({ - fields: { - payment: false, - }, - formOverrides: { - fields: ({ defaultFields }) => { - return defaultFields.map((field) => { - if ('name' in field && field.name === 'confirmationMessage') { - return { - ...field, - editor: lexicalEditor({ - features: ({ rootFeatures }) => { - return [ - ...rootFeatures, - FixedToolbarFeature(), - HeadingFeature({ enabledHeadingSizes: ['h1', 'h2', 'h3', 'h4'] }), - ] - }, - }), - } - } - return field - }) - }, - }, - }), - searchPlugin({ - collections: ['posts'], - beforeSync: beforeSyncWithSearch, - searchOverrides: { - fields: ({ defaultFields }) => { - return [...defaultFields, ...searchFields] - }, - }, - }), + // Disabling for the time being + // searchPlugin({ + // collections: ['blogposts', 'podcasts', 'talks-and-roundtables', 'authors', 'case-studies'], + // beforeSync: beforeSyncWithSearch, + // searchOverrides: { + // fields: ({ defaultFields }) => { + // return [...defaultFields, ...searchFields] + // }, + // }, + // }), cloudStoragePlugin({ enabled: true, collections: { diff --git a/src/utilities/generateMeta.ts b/src/utilities/generateMeta.ts index 81aa201..dd644b3 100644 --- a/src/utilities/generateMeta.ts +++ b/src/utilities/generateMeta.ts @@ -1,25 +1,38 @@ import type { Metadata } from 'next' -import type { Page, Post } from '../payload-types' +import type { Author, Blogpost, CaseStudy, Podcast, TalksAndRoundtable } from "../payload-types"; import { mergeOpenGraph } from './mergeOpenGraph' -export const generateMeta = async (args: { doc: Page | Post }): Promise => { +export const generateMeta = async (args: { doc: Blogpost | Podcast | Author | CaseStudy | TalksAndRoundtable}): Promise => { const { doc } = args || {} const ogImage = + // @ts-expect-error typeof doc?.meta?.image === 'object' && + // @ts-expect-error doc.meta.image !== null && + // @ts-expect-error 'url' in doc.meta.image && - `${process.env.NEXT_PUBLIC_SERVER_URL}${doc.meta.image.url}` + // @ts-expect-error + // Use this method instead if you are using a cloud storage solution + `${doc.meta.image.url}` + // Uncomment the line below and comment the line above if using local storage + // instead of cloud storage + // `${process.env.NEXT_PUBLIC_SERVER_URL}${doc.meta.image.url}` + + // @ts-expect-error const title = doc?.meta?.title - ? doc?.meta?.title + ' | Payload Website Template' - : 'Payload Website Template' + // @ts-expect-error + ? `Inside Subvisual | ${doc?.meta?.title}` + : 'Inside Subvisual' return { + // @ts-expect-error description: doc?.meta?.description, openGraph: mergeOpenGraph({ + // @ts-expect-error description: doc?.meta?.description || '', images: ogImage ? [ diff --git a/src/utilities/generatePreviewPath.ts b/src/utilities/generatePreviewPath.ts index cae80d1..3cc84d8 100644 --- a/src/utilities/generatePreviewPath.ts +++ b/src/utilities/generatePreviewPath.ts @@ -1,8 +1,11 @@ import { CollectionSlug } from 'payload' const collectionPrefixMap: Partial> = { - posts: '/posts', - pages: '', + blogposts: '/blogposts', + podcasts: '/podcasts', + authors: '/authors', + 'case-studies': '/case-studies/', + 'talks-and-roundtables': '/talks-and-roundtables', } type Props = {