From 98376850f57087b9bdda0f6f477f456b6164ab7e Mon Sep 17 00:00:00 2001 From: alasdiel Date: Thu, 13 Aug 2026 13:52:10 +0800 Subject: [PATCH 1/6] feat(cms): add durianpy-website OrganizationStatus global --- src/constants/durianpy.ts | 2 + .../durianpy-website/OrganizationStatus.ts | 61 +++++++++++++++++++ src/payload.config.ts | 10 ++- 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/globals/durianpy-website/OrganizationStatus.ts diff --git a/src/constants/durianpy.ts b/src/constants/durianpy.ts index 94371c0..5192bd0 100644 --- a/src/constants/durianpy.ts +++ b/src/constants/durianpy.ts @@ -4,6 +4,7 @@ export const DURIANPY_WEBSITE_GLOBALS = { DURIANPY_WEBSITE_CTA_SECTION: 'durianpy-website-cta-section', DURIANPY_WEBSITE_CAROUSEL: 'durianpy-website-carousel', DURIANPY_WEBSITE_CODE_OF_CONDUCT: 'durianpy-website-code-of-conduct', + DURIANPY_WEBSITE_ORGANIZATION_STATUS: 'durianpy-website-organization-status', } as const export type DurianpyWebsiteGlobalSlug = @@ -15,6 +16,7 @@ export const DURIANPY_WEBSITE_GLOBALS_LABELS: Record (access: AccessArgs) => + checkCollectionAccess(access, GLOBALS.DURIANPY_WEBSITE_ORGANIZATION_STATUS, accessType) + +export const OrganizationStatus: GlobalConfig = { + slug: GLOBALS.DURIANPY_WEBSITE_ORGANIZATION_STATUS, + label: GLOBAL_LABELS[GLOBALS.DURIANPY_WEBSITE_ORGANIZATION_STATUS].singular, + + admin: { + group: getCollectionGroupLabel('durianpy-website'), + }, + + access: { + read: (access: AccessArgs) => { + if (access.req?.draft) { + return checkOrganizationStatusAccess('read')(access) + } + return anyone(access) + }, + readVersions: checkOrganizationStatusAccess('read'), + update: checkOrganizationStatusAccess('update'), + }, + + versions: { + drafts: { + autosave: { + showSaveDraftButton: true, + }, + schedulePublish: true, + }, + maxPerDoc: 50, + }, + + fields: [ + { + name: 'isPSFPartner', + type: 'checkbox', + defaultValue: true, + admin: { + description: 'Toggle visibility of the PSF banner', + }, + }, + { + name: 'psfPartnerLogo', + type: 'upload', + relationTo: 'media', + validate: (value: unknown, { siblingData }: any) => { + if (siblingData?.isPSFPartner && !value) { + return 'PSF Partner Logo is required when PSF Partner is enabled.' + } + return true + }, + }, + ], +} diff --git a/src/payload.config.ts b/src/payload.config.ts index e5ae8e2..31aea00 100644 --- a/src/payload.config.ts +++ b/src/payload.config.ts @@ -24,6 +24,7 @@ import { Carousel } from './globals/durianpy-website/Carousel' import { SIGs } from './collections/durianpy-website/SIGs' import { CodeOfConduct } from './globals/durianpy-website/CodeOfConduct' import { durianpyWebsiteTypesEndpoint } from './endpoints/durianpy-website-types' +import { OrganizationStatus } from './globals/durianpy-website/OrganizationStatus' const filename = fileURLToPath(import.meta.url) const dirname = path.dirname(filename) @@ -105,7 +106,14 @@ export default buildConfig({ }), collections: [Media, Categories, Users, Sample, ServiceAccounts, Events, Sponsors, SIGs], endpoints: [durianpyWebsiteTypesEndpoint], - globals: [HomepageConfig, CTASection, StatisticsConfig, Carousel, CodeOfConduct], + globals: [ + HomepageConfig, + CTASection, + StatisticsConfig, + Carousel, + CodeOfConduct, + OrganizationStatus, + ], cors: [getServerSideOrigin()].filter(Boolean).map((url) => { try { const { origin } = new URL(url!) From 7b3dcaff08fe6d0819d1e30186c4f56075fb5d3b Mon Sep 17 00:00:00 2001 From: alasdiel Date: Thu, 13 Aug 2026 14:21:33 +0800 Subject: [PATCH 2/6] fix(cms): resolve PayloadRequest draft property type error --- src/globals/durianpy-website/OrganizationStatus.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/globals/durianpy-website/OrganizationStatus.ts b/src/globals/durianpy-website/OrganizationStatus.ts index 7aa0f75..0f58f74 100644 --- a/src/globals/durianpy-website/OrganizationStatus.ts +++ b/src/globals/durianpy-website/OrganizationStatus.ts @@ -18,7 +18,7 @@ export const OrganizationStatus: GlobalConfig = { access: { read: (access: AccessArgs) => { - if (access.req?.draft) { + if ((access.req as any)?.draft) { return checkOrganizationStatusAccess('read')(access) } return anyone(access) From aaeb8f48fadf3762c04cea1e9f6b29206e9a7369 Mon Sep 17 00:00:00 2001 From: alasdiel Date: Thu, 13 Aug 2026 14:25:39 +0800 Subject: [PATCH 3/6] fix(cms): replace maxPerDoc with max in OrganizationStatus versions config --- src/globals/durianpy-website/OrganizationStatus.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/globals/durianpy-website/OrganizationStatus.ts b/src/globals/durianpy-website/OrganizationStatus.ts index 0f58f74..b04be8f 100644 --- a/src/globals/durianpy-website/OrganizationStatus.ts +++ b/src/globals/durianpy-website/OrganizationStatus.ts @@ -34,7 +34,7 @@ export const OrganizationStatus: GlobalConfig = { }, schedulePublish: true, }, - maxPerDoc: 50, + max: 50, }, fields: [ From 64ce02cb3f1eaeee2e6b1af6b53bb6ca90bcdee3 Mon Sep 17 00:00:00 2001 From: alasdiel Date: Sun, 23 Aug 2026 15:40:04 +0800 Subject: [PATCH 4/6] fix(cms): address PR review comments for OrganizationStatus --- src/endpoints/seed/index.ts | 2 ++ .../durianpy-website/OrganizationStatus.ts | 28 +++++-------------- .../globals/OrganizationStatus.ts | 27 ++++++++++++++++++ 3 files changed, 36 insertions(+), 21 deletions(-) create mode 100644 src/seed/durianpy-website/globals/OrganizationStatus.ts diff --git a/src/endpoints/seed/index.ts b/src/endpoints/seed/index.ts index 2179fd8..edfdcaa 100644 --- a/src/endpoints/seed/index.ts +++ b/src/endpoints/seed/index.ts @@ -14,6 +14,7 @@ import { seedCarousel } from '../../seed/durianpy-website/globals/Carousel' import { seedCodeOfConduct } from '../../seed/durianpy-website/globals/CodeOfConduct' import { seedHomepageConfig } from '../../seed/durianpy-website/globals/HomepageConfig' import { seedStatisticsConfig } from '../../seed/durianpy-website/globals/StatisticsConfig' +import { seedOrganizationStatus } from '../../seed/durianpy-website/globals/OrganizationStatus' import { COLLECTIONS } from '@/constants/collections' @@ -75,6 +76,7 @@ export const seed = async ({ await seedCodeOfConduct({ payload, req }) await seedHomepageConfig({ payload, req }) await seedStatisticsConfig({ payload, req }) + await seedOrganizationStatus({ payload, req }) payload.logger.info('Seeded database successfully!') } diff --git a/src/globals/durianpy-website/OrganizationStatus.ts b/src/globals/durianpy-website/OrganizationStatus.ts index b04be8f..a728910 100644 --- a/src/globals/durianpy-website/OrganizationStatus.ts +++ b/src/globals/durianpy-website/OrganizationStatus.ts @@ -1,31 +1,17 @@ -import { anyone } from '@/access/anyone' -import { checkCollectionAccess } from '@/access/checkCollectionAccess' -import { AccessType } from '@/constants/accessTypes' -import { getCollectionGroupLabel } from '@/constants/collections' +import { createGlobalAccess } from '@/access/checkResourceAccess' import { GLOBALS, GLOBAL_LABELS } from '@/constants/globals' -import type { AccessArgs, GlobalConfig } from 'payload' - -const checkOrganizationStatusAccess = (accessType?: AccessType) => (access: AccessArgs) => - checkCollectionAccess(access, GLOBALS.DURIANPY_WEBSITE_ORGANIZATION_STATUS, accessType) +import { getSidebarGroupLabel, SIDEBAR_GROUPS } from '@/constants/sidebarGroup' +import type { GlobalConfig } from 'payload' export const OrganizationStatus: GlobalConfig = { slug: GLOBALS.DURIANPY_WEBSITE_ORGANIZATION_STATUS, - label: GLOBAL_LABELS[GLOBALS.DURIANPY_WEBSITE_ORGANIZATION_STATUS].singular, + label: GLOBAL_LABELS[GLOBALS.DURIANPY_WEBSITE_ORGANIZATION_STATUS], admin: { - group: getCollectionGroupLabel('durianpy-website'), + group: getSidebarGroupLabel(SIDEBAR_GROUPS.DURIANPY_WEBSITE), }, - access: { - read: (access: AccessArgs) => { - if ((access.req as any)?.draft) { - return checkOrganizationStatusAccess('read')(access) - } - return anyone(access) - }, - readVersions: checkOrganizationStatusAccess('read'), - update: checkOrganizationStatusAccess('update'), - }, + access: createGlobalAccess(GLOBALS.DURIANPY_WEBSITE_ORGANIZATION_STATUS, true), versions: { drafts: { @@ -50,7 +36,7 @@ export const OrganizationStatus: GlobalConfig = { name: 'psfPartnerLogo', type: 'upload', relationTo: 'media', - validate: (value: unknown, { siblingData }: any) => { + validate: (value: unknown, { siblingData }: { siblingData?: Record }) => { if (siblingData?.isPSFPartner && !value) { return 'PSF Partner Logo is required when PSF Partner is enabled.' } diff --git a/src/seed/durianpy-website/globals/OrganizationStatus.ts b/src/seed/durianpy-website/globals/OrganizationStatus.ts new file mode 100644 index 0000000..2d93925 --- /dev/null +++ b/src/seed/durianpy-website/globals/OrganizationStatus.ts @@ -0,0 +1,27 @@ +import { Payload, PayloadRequest } from 'payload' +import { GLOBALS } from '@/constants/globals' + +export async function seedOrganizationStatus({ + payload, + req, +}: { + payload: Payload + req?: PayloadRequest +}) { + const { docs: mediaDocs } = await payload.find({ + collection: 'media', + limit: 1, + req, + }) + const mediaId = mediaDocs[0]?.id + + await payload.updateGlobal({ + slug: GLOBALS.DURIANPY_WEBSITE_ORGANIZATION_STATUS, + data: { + isPSFPartner: true, + psfPartnerLogo: mediaId, + _status: 'published', + }, + req, + }) +} From bc4097ab8548e23950f250b6e9cf9da06d32b37d Mon Sep 17 00:00:00 2001 From: alasdiel Date: Sun, 23 Aug 2026 07:54:21 +0000 Subject: [PATCH 5/6] chore(cms): update payload types for OrganizationStatus --- src/payload-types.ts | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/payload-types.ts b/src/payload-types.ts index 35e4315..55973ee 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -118,6 +118,7 @@ export interface Config { 'durianpy-website-statistics-config': DurianpyWebsiteStatisticsConfig; 'durianpy-website-carousel': DurianpyWebsiteCarousel; 'durianpy-website-code-of-conduct': DurianpyWebsiteCodeOfConduct; + 'durianpy-website-organization-status': DurianpyWebsiteOrganizationStatus; }; globalsSelect: { 'durianpy-website-homepage-config': DurianpyWebsiteHomepageConfigSelect | DurianpyWebsiteHomepageConfigSelect; @@ -125,6 +126,7 @@ export interface Config { 'durianpy-website-statistics-config': DurianpyWebsiteStatisticsConfigSelect | DurianpyWebsiteStatisticsConfigSelect; 'durianpy-website-carousel': DurianpyWebsiteCarouselSelect | DurianpyWebsiteCarouselSelect; 'durianpy-website-code-of-conduct': DurianpyWebsiteCodeOfConductSelect | DurianpyWebsiteCodeOfConductSelect; + 'durianpy-website-organization-status': DurianpyWebsiteOrganizationStatusSelect | DurianpyWebsiteOrganizationStatusSelect; }; locale: null; widgets: { @@ -340,6 +342,7 @@ export interface User { | 'durianpy-website-cta-section' | 'durianpy-website-carousel' | 'durianpy-website-code-of-conduct' + | 'durianpy-website-organization-status' | 'categories' | 'media' | 'users' @@ -400,6 +403,7 @@ export interface ServiceAccount { | 'durianpy-website-cta-section' | 'durianpy-website-carousel' | 'durianpy-website-code-of-conduct' + | 'durianpy-website-organization-status' | 'categories' | 'media' | 'users' @@ -1423,6 +1427,21 @@ export interface DurianpyWebsiteCodeOfConduct { updatedAt?: string | null; createdAt?: string | null; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "durianpy-website-organization-status". + */ +export interface DurianpyWebsiteOrganizationStatus { + id: string; + /** + * Toggle visibility of the PSF banner + */ + isPSFPartner?: boolean | null; + psfPartnerLogo?: (string | null) | Media; + _status?: ('draft' | 'published') | null; + updatedAt?: string | null; + createdAt?: string | null; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "durianpy-website-homepage-config_select". @@ -1504,6 +1523,18 @@ export interface DurianpyWebsiteCodeOfConductSelect { createdAt?: T; globalType?: T; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "durianpy-website-organization-status_select". + */ +export interface DurianpyWebsiteOrganizationStatusSelect { + isPSFPartner?: T; + psfPartnerLogo?: T; + _status?: T; + updatedAt?: T; + createdAt?: T; + globalType?: T; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "collections_widget". @@ -1535,7 +1566,7 @@ export interface TaskSchedulePublish { relationTo: 'durianpy-website-sigs'; value: string | DurianpyWebsiteSig; } | null); - global?: string | null; + global?: 'durianpy-website-organization-status' | null; user?: (string | null) | User; }; output?: unknown; From f5e953e66a17a2529af10754a7d95cece32ffab3 Mon Sep 17 00:00:00 2001 From: Anakin Skywalker Pactores Date: Mon, 24 Aug 2026 16:27:33 +0800 Subject: [PATCH 6/6] chore: adjust seeder --- src/seed/durianpy-website/globals/OrganizationStatus.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/seed/durianpy-website/globals/OrganizationStatus.ts b/src/seed/durianpy-website/globals/OrganizationStatus.ts index 2d93925..a163cfb 100644 --- a/src/seed/durianpy-website/globals/OrganizationStatus.ts +++ b/src/seed/durianpy-website/globals/OrganizationStatus.ts @@ -1,5 +1,6 @@ import { Payload, PayloadRequest } from 'payload' import { GLOBALS } from '@/constants/globals' +import { COLLECTIONS } from '@/constants/collections' export async function seedOrganizationStatus({ payload, @@ -9,7 +10,7 @@ export async function seedOrganizationStatus({ req?: PayloadRequest }) { const { docs: mediaDocs } = await payload.find({ - collection: 'media', + collection: COLLECTIONS.MEDIA, limit: 1, req, })