media upload
This commit is contained in:
parent
1ab9c10548
commit
239b67719a
BIN
media/pigeon-400x300.jpg
Normal file
BIN
media/pigeon-400x300.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
BIN
media/pigeon-768x1024.jpg
Normal file
BIN
media/pigeon-768x1024.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 117 KiB |
BIN
media/pigeon.jpg
Normal file
BIN
media/pigeon.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 150 KiB |
@ -28,6 +28,7 @@
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"prettier": "^3.5.3",
|
||||
"tailwindcss": "^4",
|
||||
"typescript": "^5"
|
||||
},
|
||||
|
||||
267
payload-types.ts
267
payload-types.ts
@ -67,7 +67,11 @@ export interface Config {
|
||||
};
|
||||
blocks: {};
|
||||
collections: {
|
||||
projects: Project;
|
||||
news: News;
|
||||
media: Media;
|
||||
documents: Document;
|
||||
data: Datum;
|
||||
users: User;
|
||||
'payload-locked-documents': PayloadLockedDocument;
|
||||
'payload-preferences': PayloadPreference;
|
||||
@ -75,7 +79,11 @@ export interface Config {
|
||||
};
|
||||
collectionsJoins: {};
|
||||
collectionsSelect: {
|
||||
projects: ProjectsSelect<false> | ProjectsSelect<true>;
|
||||
news: NewsSelect<false> | NewsSelect<true>;
|
||||
media: MediaSelect<false> | MediaSelect<true>;
|
||||
documents: DocumentsSelect<false> | DocumentsSelect<true>;
|
||||
data: DataSelect<false> | DataSelect<true>;
|
||||
users: UsersSelect<false> | UsersSelect<true>;
|
||||
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
||||
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
||||
@ -117,6 +125,50 @@ export interface UserAuthOperations {
|
||||
password: string;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "projects".
|
||||
*/
|
||||
export interface Project {
|
||||
id: number;
|
||||
title: string;
|
||||
/**
|
||||
* The slug is used to identify the news item in the URL.
|
||||
*/
|
||||
slug: 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;
|
||||
};
|
||||
/**
|
||||
* Show this project on the homepage.
|
||||
*/
|
||||
featured?: boolean | null;
|
||||
links?:
|
||||
| {
|
||||
link: string;
|
||||
description?: string | null;
|
||||
/**
|
||||
* Optional: organise link under this heading
|
||||
*/
|
||||
group?: string | null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "news".
|
||||
@ -147,6 +199,93 @@ export interface News {
|
||||
createdAt: string;
|
||||
_status?: ('draft' | 'published') | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "media".
|
||||
*/
|
||||
export interface Media {
|
||||
id: number;
|
||||
alt?: string | null;
|
||||
description?: string | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
url?: string | null;
|
||||
thumbnailURL?: string | null;
|
||||
filename?: string | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
focalX?: number | null;
|
||||
focalY?: number | null;
|
||||
sizes?: {
|
||||
thumbnail?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
card?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
tablet?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "documents".
|
||||
*/
|
||||
export interface Document {
|
||||
id: number;
|
||||
title: string;
|
||||
description?: string | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
url?: string | null;
|
||||
thumbnailURL?: string | null;
|
||||
filename?: string | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
focalX?: number | null;
|
||||
focalY?: number | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "data".
|
||||
*/
|
||||
export interface Datum {
|
||||
id: number;
|
||||
title: string;
|
||||
description?: string | null;
|
||||
source: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
url?: string | null;
|
||||
thumbnailURL?: string | null;
|
||||
filename?: string | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
focalX?: number | null;
|
||||
focalY?: number | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "users".
|
||||
@ -171,10 +310,26 @@ export interface User {
|
||||
export interface PayloadLockedDocument {
|
||||
id: number;
|
||||
document?:
|
||||
| ({
|
||||
relationTo: 'projects';
|
||||
value: number | Project;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'news';
|
||||
value: number | News;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'media';
|
||||
value: number | Media;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'documents';
|
||||
value: number | Document;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'data';
|
||||
value: number | Datum;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'users';
|
||||
value: number | User;
|
||||
@ -221,6 +376,26 @@ export interface PayloadMigration {
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "projects_select".
|
||||
*/
|
||||
export interface ProjectsSelect<T extends boolean = true> {
|
||||
title?: T;
|
||||
slug?: T;
|
||||
content?: T;
|
||||
featured?: T;
|
||||
links?:
|
||||
| T
|
||||
| {
|
||||
link?: T;
|
||||
description?: T;
|
||||
group?: T;
|
||||
id?: T;
|
||||
};
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "news_select".
|
||||
@ -233,6 +408,98 @@ export interface NewsSelect<T extends boolean = true> {
|
||||
createdAt?: T;
|
||||
_status?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "media_select".
|
||||
*/
|
||||
export interface MediaSelect<T extends boolean = true> {
|
||||
alt?: T;
|
||||
description?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
url?: T;
|
||||
thumbnailURL?: T;
|
||||
filename?: T;
|
||||
mimeType?: T;
|
||||
filesize?: T;
|
||||
width?: T;
|
||||
height?: T;
|
||||
focalX?: T;
|
||||
focalY?: T;
|
||||
sizes?:
|
||||
| T
|
||||
| {
|
||||
thumbnail?:
|
||||
| T
|
||||
| {
|
||||
url?: T;
|
||||
width?: T;
|
||||
height?: T;
|
||||
mimeType?: T;
|
||||
filesize?: T;
|
||||
filename?: T;
|
||||
};
|
||||
card?:
|
||||
| T
|
||||
| {
|
||||
url?: T;
|
||||
width?: T;
|
||||
height?: T;
|
||||
mimeType?: T;
|
||||
filesize?: T;
|
||||
filename?: T;
|
||||
};
|
||||
tablet?:
|
||||
| T
|
||||
| {
|
||||
url?: T;
|
||||
width?: T;
|
||||
height?: T;
|
||||
mimeType?: T;
|
||||
filesize?: T;
|
||||
filename?: T;
|
||||
};
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "documents_select".
|
||||
*/
|
||||
export interface DocumentsSelect<T extends boolean = true> {
|
||||
title?: T;
|
||||
description?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
url?: T;
|
||||
thumbnailURL?: T;
|
||||
filename?: T;
|
||||
mimeType?: T;
|
||||
filesize?: T;
|
||||
width?: T;
|
||||
height?: T;
|
||||
focalX?: T;
|
||||
focalY?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "data_select".
|
||||
*/
|
||||
export interface DataSelect<T extends boolean = true> {
|
||||
title?: T;
|
||||
description?: T;
|
||||
source?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
url?: T;
|
||||
thumbnailURL?: T;
|
||||
filename?: T;
|
||||
mimeType?: T;
|
||||
filesize?: T;
|
||||
width?: T;
|
||||
height?: T;
|
||||
focalX?: T;
|
||||
focalY?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "users_select".
|
||||
|
||||
@ -1,20 +1,29 @@
|
||||
import sharp from 'sharp'
|
||||
import { lexicalEditor } from '@payloadcms/richtext-lexical'
|
||||
import { FixedToolbarFeature, lexicalEditor } from '@payloadcms/richtext-lexical'
|
||||
import { postgresAdapter } from '@payloadcms/db-postgres'
|
||||
import { buildConfig } from 'payload'
|
||||
import { News } from './src/collections/News'
|
||||
|
||||
import { Home } from '@/globals/Home'
|
||||
|
||||
import { News } from './src/collections/News'
|
||||
import { Projects } from '@/collections/Projects'
|
||||
import { Data, Documents, Media } from '@/collections/Files'
|
||||
|
||||
export default buildConfig({
|
||||
// If you'd like to use Rich Text, pass your editor here
|
||||
editor: lexicalEditor(),
|
||||
editor: lexicalEditor({
|
||||
features: ({ defaultFeatures }) => [
|
||||
...defaultFeatures,
|
||||
FixedToolbarFeature(),
|
||||
]
|
||||
}),
|
||||
|
||||
serverURL: process.env.SERVER_URL || 'http://localhost:3000',
|
||||
|
||||
globals: [Home],
|
||||
|
||||
// Define and configure your collections in this array
|
||||
collections: [News],
|
||||
collections: [Projects, News, Media, Documents, Data],
|
||||
|
||||
// Your Payload secret - should be a complex and secure string, unguessable
|
||||
secret: process.env.PAYLOAD_SECRET || '',
|
||||
|
||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@ -54,6 +54,9 @@ importers:
|
||||
'@types/react-dom':
|
||||
specifier: ^19
|
||||
version: 19.0.4(@types/react@19.0.12)
|
||||
prettier:
|
||||
specifier: ^3.5.3
|
||||
version: 3.5.3
|
||||
tailwindcss:
|
||||
specifier: ^4
|
||||
version: 4.0.17
|
||||
|
||||
53
src/app/(frontend)/projects/[slug]/page.tsx
Normal file
53
src/app/(frontend)/projects/[slug]/page.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import configPromise from "@payload-config";
|
||||
import { getPayload } from "payload";
|
||||
import { notFound } from "next/navigation";
|
||||
import { RichText } from "@payloadcms/richtext-lexical/react";
|
||||
|
||||
export default async function Page({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ slug: string }>;
|
||||
}) {
|
||||
const payload = await getPayload({ config: configPromise });
|
||||
const { slug } = await params;
|
||||
|
||||
const result = await payload.find({
|
||||
collection: "projects",
|
||||
where: {
|
||||
slug: {
|
||||
equals: slug,
|
||||
},
|
||||
},
|
||||
depth: 1,
|
||||
});
|
||||
|
||||
const item = result.docs[0];
|
||||
|
||||
if (!item) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>{item.title}</h1>
|
||||
<RichText data={item.content} className="prose bg-gray-50 p-2" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const payload = await getPayload({ config: configPromise });
|
||||
|
||||
const projectItems = await payload.find({
|
||||
collection: "projects",
|
||||
depth: 1,
|
||||
limit: 5,
|
||||
select: {
|
||||
slug: true,
|
||||
},
|
||||
});
|
||||
|
||||
return projectItems.docs.map((item) => ({
|
||||
slug: item.slug,
|
||||
}));
|
||||
}
|
||||
39
src/app/(frontend)/projects/page.tsx
Normal file
39
src/app/(frontend)/projects/page.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
import configPromise from "@payload-config";
|
||||
import Link from "next/link";
|
||||
import { getPayload } from "payload";
|
||||
|
||||
export default async function Page() {
|
||||
const payload = await getPayload({ config: configPromise });
|
||||
|
||||
const projectItems = await payload.find({
|
||||
collection: "projects",
|
||||
depth: 1,
|
||||
limit: 5,
|
||||
select: {
|
||||
title: true,
|
||||
slug: true,
|
||||
},
|
||||
sort: "-created_at",
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Projects</h1>
|
||||
|
||||
<ul>
|
||||
{projectItems.docs.map((projectItem) => (
|
||||
<li key={projectItem.id} className="p-4">
|
||||
<h3 className="text-lg">
|
||||
<Link
|
||||
href={`
|
||||
/projects/${projectItem.slug}`}
|
||||
>
|
||||
{projectItem.title}
|
||||
</Link>
|
||||
</h3>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
import { RscEntryLexicalCell as RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||
import { RscEntryLexicalField as RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||
import { FixedToolbarFeatureClient as FixedToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { InlineToolbarFeatureClient as InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { HorizontalRuleFeatureClient as HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { UploadFeatureClient as UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
@ -24,6 +25,7 @@ import { ItalicFeatureClient as ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0
|
||||
export const importMap = {
|
||||
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell": RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalField": RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||
"@payloadcms/richtext-lexical/client#FixedToolbarFeatureClient": FixedToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#InlineToolbarFeatureClient": InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#HorizontalRuleFeatureClient": HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#UploadFeatureClient": UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
|
||||
1
src/collections/Documents.ts
Normal file
1
src/collections/Documents.ts
Normal file
@ -0,0 +1 @@
|
||||
import type { CollectionConfig } from "payload";
|
||||
90
src/collections/Files.ts
Normal file
90
src/collections/Files.ts
Normal file
@ -0,0 +1,90 @@
|
||||
import type { CollectionConfig } from "payload";
|
||||
|
||||
export const Media: CollectionConfig = {
|
||||
slug: 'media',
|
||||
upload: {
|
||||
staticDir: 'media',
|
||||
imageSizes: [
|
||||
{
|
||||
name: 'thumbnail',
|
||||
width: 400,
|
||||
height: 300,
|
||||
position: 'centre',
|
||||
},
|
||||
{
|
||||
name: 'card',
|
||||
width: 768,
|
||||
height: 1024,
|
||||
position: 'centre',
|
||||
},
|
||||
{
|
||||
name: 'tablet',
|
||||
width: 1024,
|
||||
height: undefined,
|
||||
position: 'centre',
|
||||
}
|
||||
],
|
||||
adminThumbnail: 'thumbnail',
|
||||
mimeTypes: ['image/*'],
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'alt',
|
||||
label: 'Alt Text',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: 'Description',
|
||||
type: 'textarea',
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const Documents: CollectionConfig = {
|
||||
slug: 'documents',
|
||||
upload: {
|
||||
staticDir: 'documents',
|
||||
mimeTypes: ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation'],
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: 'Description',
|
||||
type: 'textarea',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
export const Data: CollectionConfig = {
|
||||
slug: 'data',
|
||||
upload: {
|
||||
staticDir: 'data',
|
||||
mimeTypes: ['application/json', 'text/csv', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'text/plain'],
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: 'Description',
|
||||
type: 'textarea',
|
||||
},
|
||||
{
|
||||
name: 'source',
|
||||
label: 'Source',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
@ -1,11 +1,6 @@
|
||||
import { formatSlug } from "@/lib/slugs";
|
||||
import { CollectionConfig } from "payload";
|
||||
|
||||
export const formatSlug = (val: string): string =>
|
||||
val
|
||||
.replace(/ /g, '-')
|
||||
.replace(/[^\w-]+/g, '')
|
||||
.toLowerCase()
|
||||
|
||||
export const News: CollectionConfig = {
|
||||
slug: 'news',
|
||||
fields: [
|
||||
@ -28,18 +23,7 @@ export const News: CollectionConfig = {
|
||||
},
|
||||
hooks: {
|
||||
beforeValidate: [
|
||||
({data, operation, value}) => {
|
||||
if (typeof value === 'string') {
|
||||
return formatSlug(value);
|
||||
}
|
||||
if (operation === 'create' || !data?.slug) {
|
||||
const fallbackData = data?.title;
|
||||
if (fallbackData && typeof fallbackData === 'string') {
|
||||
return formatSlug(fallbackData);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
formatSlug('title'),
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
84
src/collections/Projects.ts
Normal file
84
src/collections/Projects.ts
Normal file
@ -0,0 +1,84 @@
|
||||
import { formatSlug } from "@/lib/slugs";
|
||||
import { CollectionConfig } from "payload";
|
||||
|
||||
export const Projects: CollectionConfig = {
|
||||
slug: 'projects',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'slug',
|
||||
label: 'Slug',
|
||||
type: 'text',
|
||||
required: true,
|
||||
unique: true,
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
description: 'The slug is used to identify the news item in the URL.',
|
||||
// readOnly: true,
|
||||
},
|
||||
hooks: {
|
||||
beforeValidate: [
|
||||
formatSlug('title'),
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'content',
|
||||
label: 'Content',
|
||||
type: 'richText',
|
||||
required: true,
|
||||
},
|
||||
// list of files
|
||||
// gallery
|
||||
// keywords
|
||||
// is featured?
|
||||
{
|
||||
name: 'featured',
|
||||
label: 'Featured',
|
||||
type: 'checkbox',
|
||||
defaultValue: false,
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
description: 'Show this project on the homepage.',
|
||||
}
|
||||
},
|
||||
// links
|
||||
{
|
||||
name: 'links',
|
||||
label: 'Links',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
name: 'link',
|
||||
label: 'Link',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: 'Description',
|
||||
type: 'text',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'group',
|
||||
label: 'Group',
|
||||
type: 'text',
|
||||
required: false,
|
||||
admin: {
|
||||
description: 'Optional: organise link under this heading',
|
||||
},
|
||||
}
|
||||
],
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
21
src/lib/slugs.ts
Normal file
21
src/lib/slugs.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { FieldHook } from "payload";
|
||||
|
||||
export const format = (val: string): string =>
|
||||
val
|
||||
.replace(/ /g, '-')
|
||||
.replace(/[^\w-]+/g, '')
|
||||
.toLowerCase()
|
||||
|
||||
export const formatSlug = (name: string): FieldHook =>
|
||||
({data, operation, value}) => {
|
||||
if (typeof value === 'string') {
|
||||
return format(value);
|
||||
}
|
||||
if (operation === 'create' || !data?.[name]) {
|
||||
const fallbackData = data?.[name];
|
||||
if (fallbackData && typeof fallbackData === 'string') {
|
||||
return format(fallbackData);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user