Skip to content

Instantly share code, notes, and snippets.

@ainsleyclark
Created March 12, 2025 18:05
Show Gist options
  • Select an option

  • Save ainsleyclark/fffac030ef3b81cfbf8f1fc09228cd07 to your computer and use it in GitHub Desktop.

Select an option

Save ainsleyclark/fffac030ef3b81cfbf8f1fc09228cd07 to your computer and use it in GitHub Desktop.
import { lexicalHTML } from '@payloadcms/richtext-lexical';
import { CollectionConfig, PayloadRequest } from 'payload';
import { mergeCentres } from '@/collections/Centres/endpoints/mergeCentres';
import { renameCentreField } from '@/collections/Centres/endpoints/rename';
import { actions } from '@/collections/Centres/fields/actions';
import { agencies } from '@/collections/Centres/fields/agencies';
import { capabilitiesGrouped } from '@/collections/Centres/fields/capabilities';
import { contact } from '@/collections/Centres/fields/contact';
import { diveDestinations } from '@/collections/Centres/fields/diveDestinations';
import { nearbyCentres } from '@/collections/Centres/fields/nearbyCentres';
import { centreTypeField } from '@/collections/Centres/fields/type';
import { updateCapabilities } from '@/collections/Centres/hooks/capabilities';
import { updateGoogleData } from '@/collections/Centres/hooks/google';
import { updateCentrePages } from '@/collections/Centres/hooks/pages';
import { updateScore } from '@/collections/Centres/hooks/score';
import { updateType } from '@/collections/Centres/hooks/type';
import updateReviewAggregation from '@/collections/Reviews/hooks/aggregation';
import { Address } from '@/fields/Address';
import { Location } from '@/fields/Location';
import { ReviewAggregation } from '@/fields/ReviewAggregation';
import { SeaTemperatures } from '@/fields/seatemp';
import { URLField } from '@/fields/URL';
import { Weather } from '@/fields/weather';
import { Centre } from '@/types/payload';
import { frontendURL, urlPaths } from '@/util/url';
export const Centres: CollectionConfig = {
slug: 'centres',
dbName: 'centres',
timestamps: true,
labels: {
singular: 'Centre',
plural: 'Centres',
},
versions: {
drafts: true,
maxPerDoc: 10,
},
access: {
read: () => true,
},
defaultPopulate: {
centrePage: false,
},
admin: {
group: 'Dive Centres',
defaultColumns: ['id', 'name', 'url', 'score', 'agencies', 'updatedAt', 'createdAt'],
useAsTitle: 'name',
pagination: {
defaultLimit: 50,
limits: [10, 20, 50, 100],
},
meta: {
title: 'Dive Centres',
},
preview: (doc): string | null => {
return `${frontendURL()}${doc.url}`;
}
},
hooks: {
beforeChange: [
updateCapabilities,
updateType,
updateCentrePages,
],
afterChange: [
updateReviewAggregation,
updateGoogleData,
updateScore,
],
},
endpoints: [
{
path: '/merge',
method: 'post',
handler: mergeCentres,
},
{
path: '/rename/country',
method: 'post',
handler: async (req: PayloadRequest) => {
return renameCentreField(req, 'countrySlug', 'country');
},
},
{
path: '/rename/region',
method: 'post',
handler: async (req: PayloadRequest) => {
return renameCentreField(req, 'regionSlug', 'region');
},
}
],
fields: [
{
type: 'tabs',
tabs: [
{
label: 'Content',
description: '',
fields: [
{
name: 'name',
type: 'text',
required: true,
label: 'Name',
admin: {
description: 'The official name of the dive centre obtained from the provider',
},
},
{
name: 'summary',
label: 'Summary',
type: 'textarea',
localized: true,
admin: {
description: 'The brief outline of the dive centre that will appear on cards throughout the site',
}
},
{
name: 'description',
label: 'Description',
type: 'richText',
localized: true,
admin: {
description: 'Test',
}
},
lexicalHTML('description', {
name: 'descriptionHtml',
storeInDB: true,
}),
{
type: 'row',
fields: [
{
name: 'isFiveStar',
label: 'Is Five Star',
type: 'checkbox',
defaultValue: false,
required: true,
admin: {
width: '33%',
description: 'Determines if the dive centre 5 stars, this is what the provider classifies the centre as.',
}
},
{
name: 'isClub',
label: 'Is Club',
type: 'checkbox',
defaultValue: false,
required: true,
admin: {
width: '33%',
description: 'Determines if the dive centre if a club which usually means that the centre is not qualified for teaching courses.',
}
},
{
name: 'isInstructor',
label: 'Is Instructor',
type: 'checkbox',
defaultValue: false,
required: true,
admin: {
width: '33%',
description: 'Determines if the centre is a physical person instead of a company.',
}
},
],
},
{
type: 'row',
fields: [
{
name: 'languages',
label: 'Languages',
type: 'text',
hasMany: true,
admin: {
width: '50%',
description: 'The languages associated with the centre',
},
},
centreTypeField({
adminOptions: {
width: '50%',
readOnly: true,
}
})
]
},
{
type: 'row',
fields: [
{
name: 'score',
label: 'Score',
type: 'number',
required: true,
defaultValue: 0,
min: 0,
max: 100,
admin: {
readOnly: true,
description: 'The official Seek Scuba score of the dive centre which is automatically generated',
width: '50%',
},
},
{
name: 'scoreOverride',
label: 'Score Override',
type: 'number',
required: true,
defaultValue: 0,
min: 0,
max: 100,
access: {
read: ({req}) => {
return req.payloadAPI === 'local';
},
},
admin: {
description: 'The Seek Scuba score override, you can use this to manually update the centre\'s score',
width: '50%',
}
},
]
},
capabilitiesGrouped(),
]
},
{
label: 'Agencies',
fields: agencies,
},
{
label: 'Media',
fields: [
{
name: 'media',
label: '',
type: 'group',
admin: {
hideGutter: true,
},
fields: [
{
name: 'featuredImage',
label: 'Featured Image',
type: 'upload',
relationTo: 'media',
filterOptions: {
mimeType: {contains: 'image'},
},
},
{
name: 'logo',
label: 'Logo',
type: 'upload',
relationTo: 'media',
filterOptions: {
mimeType: {contains: 'image'},
},
},
{
name: 'images',
label: 'Images',
type: 'upload',
relationTo: 'media',
hasMany: true,
filterOptions: {
mimeType: {contains: 'image'},
},
},
{
name: 'videos',
label: 'Videos',
type: 'upload',
relationTo: 'media',
hasMany: true,
filterOptions: {
mimeType: {contains: 'video'},
},
},
]
}
],
},
{
label: 'Location',
fields: [
Location({entity: 'dive centre'}),
Address({entity: 'dive centre'}),
]
},
{
label: 'Contact',
fields: contact,
},
{
label: 'Reviews',
fields: [
{
type: 'relationship',
name: 'reviews',
label: 'Reviews',
relationTo: 'reviews',
hasMany: true,
admin: {
allowCreate: false,
},
},
ReviewAggregation(),
],
},
{
label: 'Meteorology',
fields: [
SeaTemperatures(),
Weather(),
],
},
{
label: 'Meta',
description: 'Any other meta information associated with the Dive Centre.',
fields: [
{
name: 'centreMeta',
label: 'Meta',
type: 'json',
defaultValue: '{}',
admin: {
description: 'Meta data associated with the dive centre',
readOnly: true,
}
},
],
},
],
},
{
name: 'centreButtons',
type: 'ui',
admin: {
position: 'sidebar',
components: {
Field: {
path: '/collections/Centres/components/Buttons#CentreButtons',
}
},
},
},
{
name: 'slug',
type: 'text',
index: true,
admin: {
position: 'sidebar',
},
},
{
name: 'countrySlug',
type: 'text',
required: true,
index: true,
admin: {
position: 'sidebar',
},
},
{
name: 'regionSlug',
type: 'text',
index: true,
admin: {
position: 'sidebar',
},
},
URLField((data: Partial<Centre>) => {
let url = `/${urlPaths.DIVE_CENTRES}/${data.countrySlug}/`;
if (data.regionSlug && data.regionSlug !== '') {
url += `${data.regionSlug}/`;
}
url += `${data.slug}/`;
return url;
}),
{
name: 'checkedAt',
type: 'date',
required: true,
defaultValue: () => {
return new Date();
},
admin: {
hidden: true,
position: 'sidebar',
},
},
{
name: 'website',
label: 'Website',
type: 'text',
required: false,
admin: {
position: 'sidebar',
},
},
{
name: 'googlePlaceID',
label: 'Google Place ID',
type: 'text',
admin: {
position: 'sidebar',
}
},
actions,
],
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment