Skip to content

Instantly share code, notes, and snippets.

@pro-laico
Last active June 23, 2025 14:41
Show Gist options
  • Save pro-laico/2887985e10e1b91721d53320d712774d to your computer and use it in GitHub Desktop.
Save pro-laico/2887985e10e1b91721d53320d712774d to your computer and use it in GitHub Desktop.
import type { CollectionConfig } from 'payload'
import { authenticated } from '../../access/authenticated'
import { authenticatedOrPublished } from '../../access/authenticatedOrPublished'
import { slugField } from '@/fields/slug'
import { populatePublishedAt } from '../../hooks/populatePublishedAt'
import { generatePreviewPath } from '../../utilities/generatePreviewPath'
import { revalidateDelete, revalidatePage } from './hooks/revalidatePage'
import { populateHref } from '@/hooks/populateHref'
import { createBreadcrumbsField, createParentField } from '@payloadcms/plugin-nested-docs'
import {
MetaDescriptionField,
MetaImageField,
MetaTitleField,
OverviewField,
PreviewField,
} from '@payloadcms/plugin-seo/fields'
import Blocks from '@/blocks'
import { ClassNameField } from '@/proLaico/styles/fields/ClassName'
export const Pages: CollectionConfig<'pages'> = {
slug: 'pages',
access: {
create: authenticated,
delete: authenticated,
read: authenticatedOrPublished,
update: authenticated,
},
// This config controls what's populated by default when a page is referenced
// https://payloadcms.com/docs/queries/select#defaultpopulate-collection-config-property
// Type safe if the collection slug generic is passed to `CollectionConfig` - `CollectionConfig<'pages'>
defaultPopulate: {
title: true,
slug: true,
href: true,
},
admin: {
defaultColumns: ['title', 'slug', 'href', 'updatedAt'],
livePreview: {
url: ({ data, req }) => {
const path = generatePreviewPath({
slug: typeof data?.slug === 'string' ? data.slug : '',
path: typeof data?.href === 'string' ? data.href : '',
collection: 'pages',
req,
})
return path
},
},
preview: (data, { req }) =>
generatePreviewPath({
slug: typeof data?.slug === 'string' ? data.slug : '',
path: typeof data?.href === 'string' ? data.href : '',
collection: 'pages',
req,
}),
useAsTitle: 'title',
},
fields: [
{
name: 'title',
type: 'text',
required: true,
},
{
type: 'tabs',
tabs: [
{
label: 'Children',
fields: [
{
name: 'children-gen-1',
type: 'blocks',
blocks: [...Blocks],
required: true,
admin: {
initCollapsed: true,
},
},
],
},
{
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',
}),
],
},
{
label: 'Styles',
fields: [
ClassNameField({
namePrefix: 'main',
}),
],
},
{
label: 'Settings',
fields: [
{
name: 'noIndex',
type: 'checkbox',
defaultValue: false,
},
],
},
],
},
{
name: 'publishedAt',
type: 'date',
admin: {
position: 'sidebar',
},
},
...slugField(),
{
name: 'href',
type: 'text',
admin: {
position: 'sidebar',
},
},
createParentField('pages', {
name: 'parent',
label: 'Parent',
admin: {
position: 'sidebar',
},
}),
createBreadcrumbsField('pages', {
name: 'breadcrumbs',
label: 'Breadcrumbs',
admin: {
initCollapsed: true,
position: 'sidebar',
},
}),
],
hooks: {
afterChange: [revalidatePage],
beforeChange: [populatePublishedAt, populateHref],
afterDelete: [revalidateDelete],
},
versions: {
drafts: {
autosave: {
interval: 100, // We set this interval for optimal live preview
},
schedulePublish: true,
},
maxPerDoc: 50,
},
}
const queryPageByHref = cache(async ({ href }: { href: string }) => {
const { isEnabled: draft } = await draftMode()
const payload = await getPayload({ config: configPromise })
const result = await payload.find({
collection: 'pages',
limit: 1,
draft,
pagination: false,
overrideAccess: draft,
where: {
href: {
equals: href,
},
},
})
return result.docs?.[0] || null
})
muxVideoPlugin({
enabled: true,
adminThumbnail: 'image',
initSettings: {
tokenId: process.env.MUX_TOKEN_ID || '',
tokenSecret: process.env.MUX_TOKEN_SECRET || '',
webhookSecret: process.env.MUX_WEBHOOK_SIGNING_SECRET || '',
},
uploadSettings: {
cors_origin: process.env.NEXT_PUBLIC_SERVER_URL || 'http://localhost:3000',
},
}),
'use server'
import React from 'react'
import type { VideoBlock as VideoBlockProps } from '@/payload-types'
import postHogPropertyApplicator from '@/proLaico/tracking/utilities/propertyApplicator'
import MuxVideo from '../../components/muxVideo'
import { getPayload } from 'payload'
import configPromise from '@/payload.config'
const muxVideoQuery = async ({ id }: { id: string }) => {
const payload = await getPayload({ config: configPromise })
const result = await payload.findByID({
collection: 'mux-video',
id: id,
})
return result || null
}
export const VideoBlock: React.FC<VideoBlockProps> = async (props) => {
let video = null
if (typeof props.video.video === 'string') {
video = await muxVideoQuery({ id: props.video.video })
} else if (typeof props.video.video === 'object') {
video = props.video.video
}
if (!video) {
console.warn('No video found.')
return <div className="sr-only">No video found.</div>
}
return (
<div
{...(props?.postHogProperty && postHogPropertyApplicator(props?.postHogProperty))}
{...(props.ClassName && { className: props.ClassName })}
>
<MuxVideo video={props} muxVideo={video} />
</div>
)
}
'use client'
import { VideoBlock as VideoBlockProps, MuxVideo as MuxVideoProps } from '@/payload-types'
import dynamic from 'next/dynamic'
const MuxPlayer = dynamic(() => import('@mux/mux-player-react'), { ssr: false })
function MuxVideo({ video, muxVideo }: { video: VideoBlockProps; muxVideo: MuxVideoProps }) {
return (
<MuxPlayer
// Using playback id
playbackId={muxVideo.playbackOptions![0].playbackId!}
// Or use the playback URL
src={muxVideo.playbackOptions![0].playbackUrl!}
// Poster
poster={muxVideo.playbackOptions![0].posterUrl!}
/>
)
}
export default MuxVideo
import { ClassNameField } from '@/proLaico/styles/fields/ClassName'
import { deepMerge, GroupField } from 'payload'
type VideoField = (options?: { overrides?: Partial<GroupField> }) => GroupField
export const VideoField: VideoField = ({ overrides = {} } = {}) => {
const groupField: GroupField = {
name: 'video',
label: 'Video',
type: 'group',
fields: [
{
name: 'video',
label: 'Preview Video',
type: 'relationship',
relationTo: 'mux-video',
required: true,
},
ClassNameField(),
],
}
return deepMerge(groupField, overrides)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment