templates: fix nested docs url generation for categories (#10403)
Fixes https://github.com/payloadcms/payload/issues/10374
This commit is contained in:
@@ -2,6 +2,7 @@ import type { CollectionConfig } from 'payload'
|
||||
|
||||
import { anyone } from '../access/anyone'
|
||||
import { authenticated } from '../access/authenticated'
|
||||
import { slugField } from '@/fields/slug'
|
||||
|
||||
export const Categories: CollectionConfig = {
|
||||
slug: 'categories',
|
||||
@@ -20,5 +21,6 @@ export const Categories: CollectionConfig = {
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
...slugField(),
|
||||
],
|
||||
}
|
||||
|
||||
@@ -141,6 +141,12 @@ export const seed = async ({
|
||||
collection: 'categories',
|
||||
data: {
|
||||
title: 'Technology',
|
||||
breadcrumbs: [
|
||||
{
|
||||
label: 'Technology',
|
||||
url: '/technology',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -148,6 +154,12 @@ export const seed = async ({
|
||||
collection: 'categories',
|
||||
data: {
|
||||
title: 'News',
|
||||
breadcrumbs: [
|
||||
{
|
||||
label: 'News',
|
||||
url: '/news',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -155,12 +167,24 @@ export const seed = async ({
|
||||
collection: 'categories',
|
||||
data: {
|
||||
title: 'Finance',
|
||||
breadcrumbs: [
|
||||
{
|
||||
label: 'Finance',
|
||||
url: '/finance',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
payload.create({
|
||||
collection: 'categories',
|
||||
data: {
|
||||
title: 'Design',
|
||||
breadcrumbs: [
|
||||
{
|
||||
label: 'Design',
|
||||
url: '/design',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -168,6 +192,12 @@ export const seed = async ({
|
||||
collection: 'categories',
|
||||
data: {
|
||||
title: 'Software',
|
||||
breadcrumbs: [
|
||||
{
|
||||
label: 'Software',
|
||||
url: '/software',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -175,6 +205,12 @@ export const seed = async ({
|
||||
collection: 'categories',
|
||||
data: {
|
||||
title: 'Engineering',
|
||||
breadcrumbs: [
|
||||
{
|
||||
label: 'Engineering',
|
||||
url: '/engineering',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
])
|
||||
|
||||
@@ -117,6 +117,9 @@ export interface Page {
|
||||
} | null);
|
||||
url?: string | null;
|
||||
label: string;
|
||||
/**
|
||||
* Choose how the link should be rendered.
|
||||
*/
|
||||
appearance?: ('default' | 'outline') | null;
|
||||
};
|
||||
id?: string | null;
|
||||
@@ -127,6 +130,9 @@ export interface Page {
|
||||
layout: (CallToActionBlock | ContentBlock | MediaBlock | ArchiveBlock | FormBlock)[];
|
||||
meta?: {
|
||||
title?: string | null;
|
||||
/**
|
||||
* Maximum upload file size: 12MB. Recommended file size for images is <500KB.
|
||||
*/
|
||||
image?: (string | null) | Media;
|
||||
description?: string | null;
|
||||
};
|
||||
@@ -164,6 +170,9 @@ export interface Post {
|
||||
categories?: (string | Category)[] | null;
|
||||
meta?: {
|
||||
title?: string | null;
|
||||
/**
|
||||
* Maximum upload file size: 12MB. Recommended file size for images is <500KB.
|
||||
*/
|
||||
image?: (string | null) | Media;
|
||||
description?: string | null;
|
||||
};
|
||||
@@ -280,6 +289,8 @@ export interface Media {
|
||||
export interface Category {
|
||||
id: string;
|
||||
title: string;
|
||||
slug?: string | null;
|
||||
slugLock?: boolean | null;
|
||||
parent?: (string | null) | Category;
|
||||
breadcrumbs?:
|
||||
| {
|
||||
@@ -346,6 +357,9 @@ export interface CallToActionBlock {
|
||||
} | null);
|
||||
url?: string | null;
|
||||
label: string;
|
||||
/**
|
||||
* Choose how the link should be rendered.
|
||||
*/
|
||||
appearance?: ('default' | 'outline') | null;
|
||||
};
|
||||
id?: string | null;
|
||||
@@ -393,6 +407,9 @@ export interface ContentBlock {
|
||||
} | null);
|
||||
url?: string | null;
|
||||
label: string;
|
||||
/**
|
||||
* Choose how the link should be rendered.
|
||||
*/
|
||||
appearance?: ('default' | 'outline') | null;
|
||||
};
|
||||
id?: string | null;
|
||||
@@ -588,6 +605,9 @@ export interface Form {
|
||||
)[]
|
||||
| null;
|
||||
submitButtonLabel?: string | null;
|
||||
/**
|
||||
* Choose whether to display an on-page message or redirect to a different page after they submit the form.
|
||||
*/
|
||||
confirmationType?: ('message' | 'redirect') | null;
|
||||
confirmationMessage?: {
|
||||
root: {
|
||||
@@ -607,6 +627,9 @@ export interface Form {
|
||||
redirect?: {
|
||||
url: string;
|
||||
};
|
||||
/**
|
||||
* Send custom emails when the form submits. Use comma separated lists to send the same email to multiple recipients. To reference a value from this form, wrap that field's name with double curly brackets, i.e. {{firstName}}. You can use a wildcard {{*}} to output all data and {{*:table}} to format it as an HTML table in the email.
|
||||
*/
|
||||
emails?:
|
||||
| {
|
||||
emailTo?: string | null;
|
||||
@@ -615,6 +638,9 @@ export interface Form {
|
||||
replyTo?: string | null;
|
||||
emailFrom?: string | null;
|
||||
subject: string;
|
||||
/**
|
||||
* Enter the message that should be sent in this email.
|
||||
*/
|
||||
message?: {
|
||||
root: {
|
||||
type: string;
|
||||
@@ -642,6 +668,9 @@ export interface Form {
|
||||
*/
|
||||
export interface Redirect {
|
||||
id: string;
|
||||
/**
|
||||
* You will need to rebuild the website when changing this field.
|
||||
*/
|
||||
from: string;
|
||||
to?: {
|
||||
type?: ('reference' | 'custom') | null;
|
||||
@@ -677,6 +706,8 @@ export interface FormSubmission {
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This is a collection of automatically created search results. These results are used by the global site search and will be updated automatically as documents in the CMS are created or updated.
|
||||
*
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "search".
|
||||
*/
|
||||
@@ -1054,6 +1085,8 @@ export interface MediaSelect<T extends boolean = true> {
|
||||
*/
|
||||
export interface CategoriesSelect<T extends boolean = true> {
|
||||
title?: T;
|
||||
slug?: T;
|
||||
slugLock?: T;
|
||||
parent?: T;
|
||||
breadcrumbs?:
|
||||
| T
|
||||
|
||||
@@ -49,6 +49,7 @@ export const plugins: Plugin[] = [
|
||||
}),
|
||||
nestedDocsPlugin({
|
||||
collections: ['categories'],
|
||||
generateURL: (docs) => docs.reduce((url, doc) => `${url}/${doc.slug}`, ''),
|
||||
}),
|
||||
seoPlugin({
|
||||
generateTitle,
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { CollectionConfig } from 'payload'
|
||||
|
||||
import { anyone } from '../access/anyone'
|
||||
import { authenticated } from '../access/authenticated'
|
||||
import { slugField } from '@/fields/slug'
|
||||
|
||||
export const Categories: CollectionConfig = {
|
||||
slug: 'categories',
|
||||
@@ -20,5 +21,6 @@ export const Categories: CollectionConfig = {
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
...slugField(),
|
||||
],
|
||||
}
|
||||
|
||||
@@ -141,6 +141,12 @@ export const seed = async ({
|
||||
collection: 'categories',
|
||||
data: {
|
||||
title: 'Technology',
|
||||
breadcrumbs: [
|
||||
{
|
||||
label: 'Technology',
|
||||
url: '/technology',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -148,6 +154,12 @@ export const seed = async ({
|
||||
collection: 'categories',
|
||||
data: {
|
||||
title: 'News',
|
||||
breadcrumbs: [
|
||||
{
|
||||
label: 'News',
|
||||
url: '/news',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -155,12 +167,24 @@ export const seed = async ({
|
||||
collection: 'categories',
|
||||
data: {
|
||||
title: 'Finance',
|
||||
breadcrumbs: [
|
||||
{
|
||||
label: 'Finance',
|
||||
url: '/finance',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
payload.create({
|
||||
collection: 'categories',
|
||||
data: {
|
||||
title: 'Design',
|
||||
breadcrumbs: [
|
||||
{
|
||||
label: 'Design',
|
||||
url: '/design',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -168,6 +192,12 @@ export const seed = async ({
|
||||
collection: 'categories',
|
||||
data: {
|
||||
title: 'Software',
|
||||
breadcrumbs: [
|
||||
{
|
||||
label: 'Software',
|
||||
url: '/software',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -175,6 +205,12 @@ export const seed = async ({
|
||||
collection: 'categories',
|
||||
data: {
|
||||
title: 'Engineering',
|
||||
breadcrumbs: [
|
||||
{
|
||||
label: 'Engineering',
|
||||
url: '/engineering',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
])
|
||||
|
||||
@@ -117,6 +117,9 @@ export interface Page {
|
||||
} | null);
|
||||
url?: string | null;
|
||||
label: string;
|
||||
/**
|
||||
* Choose how the link should be rendered.
|
||||
*/
|
||||
appearance?: ('default' | 'outline') | null;
|
||||
};
|
||||
id?: string | null;
|
||||
@@ -127,6 +130,9 @@ export interface Page {
|
||||
layout: (CallToActionBlock | ContentBlock | MediaBlock | ArchiveBlock | FormBlock)[];
|
||||
meta?: {
|
||||
title?: string | null;
|
||||
/**
|
||||
* Maximum upload file size: 12MB. Recommended file size for images is <500KB.
|
||||
*/
|
||||
image?: (number | null) | Media;
|
||||
description?: string | null;
|
||||
};
|
||||
@@ -164,6 +170,9 @@ export interface Post {
|
||||
categories?: (number | Category)[] | null;
|
||||
meta?: {
|
||||
title?: string | null;
|
||||
/**
|
||||
* Maximum upload file size: 12MB. Recommended file size for images is <500KB.
|
||||
*/
|
||||
image?: (number | null) | Media;
|
||||
description?: string | null;
|
||||
};
|
||||
@@ -280,6 +289,8 @@ export interface Media {
|
||||
export interface Category {
|
||||
id: number;
|
||||
title: string;
|
||||
slug?: string | null;
|
||||
slugLock?: boolean | null;
|
||||
parent?: (number | null) | Category;
|
||||
breadcrumbs?:
|
||||
| {
|
||||
@@ -346,6 +357,9 @@ export interface CallToActionBlock {
|
||||
} | null);
|
||||
url?: string | null;
|
||||
label: string;
|
||||
/**
|
||||
* Choose how the link should be rendered.
|
||||
*/
|
||||
appearance?: ('default' | 'outline') | null;
|
||||
};
|
||||
id?: string | null;
|
||||
@@ -393,6 +407,9 @@ export interface ContentBlock {
|
||||
} | null);
|
||||
url?: string | null;
|
||||
label: string;
|
||||
/**
|
||||
* Choose how the link should be rendered.
|
||||
*/
|
||||
appearance?: ('default' | 'outline') | null;
|
||||
};
|
||||
id?: string | null;
|
||||
@@ -588,6 +605,9 @@ export interface Form {
|
||||
)[]
|
||||
| null;
|
||||
submitButtonLabel?: string | null;
|
||||
/**
|
||||
* Choose whether to display an on-page message or redirect to a different page after they submit the form.
|
||||
*/
|
||||
confirmationType?: ('message' | 'redirect') | null;
|
||||
confirmationMessage?: {
|
||||
root: {
|
||||
@@ -607,6 +627,9 @@ export interface Form {
|
||||
redirect?: {
|
||||
url: string;
|
||||
};
|
||||
/**
|
||||
* Send custom emails when the form submits. Use comma separated lists to send the same email to multiple recipients. To reference a value from this form, wrap that field's name with double curly brackets, i.e. {{firstName}}. You can use a wildcard {{*}} to output all data and {{*:table}} to format it as an HTML table in the email.
|
||||
*/
|
||||
emails?:
|
||||
| {
|
||||
emailTo?: string | null;
|
||||
@@ -615,6 +638,9 @@ export interface Form {
|
||||
replyTo?: string | null;
|
||||
emailFrom?: string | null;
|
||||
subject: string;
|
||||
/**
|
||||
* Enter the message that should be sent in this email.
|
||||
*/
|
||||
message?: {
|
||||
root: {
|
||||
type: string;
|
||||
@@ -642,6 +668,9 @@ export interface Form {
|
||||
*/
|
||||
export interface Redirect {
|
||||
id: number;
|
||||
/**
|
||||
* You will need to rebuild the website when changing this field.
|
||||
*/
|
||||
from: string;
|
||||
to?: {
|
||||
type?: ('reference' | 'custom') | null;
|
||||
@@ -677,6 +706,8 @@ export interface FormSubmission {
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This is a collection of automatically created search results. These results are used by the global site search and will be updated automatically as documents in the CMS are created or updated.
|
||||
*
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "search".
|
||||
*/
|
||||
@@ -820,80 +851,11 @@ export interface PagesSelect<T extends boolean = true> {
|
||||
layout?:
|
||||
| T
|
||||
| {
|
||||
cta?:
|
||||
| T
|
||||
| {
|
||||
richText?: T;
|
||||
links?:
|
||||
| T
|
||||
| {
|
||||
link?:
|
||||
| T
|
||||
| {
|
||||
type?: T;
|
||||
newTab?: T;
|
||||
reference?: T;
|
||||
url?: T;
|
||||
label?: T;
|
||||
appearance?: T;
|
||||
};
|
||||
id?: T;
|
||||
};
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
content?:
|
||||
| T
|
||||
| {
|
||||
columns?:
|
||||
| T
|
||||
| {
|
||||
size?: T;
|
||||
richText?: T;
|
||||
enableLink?: T;
|
||||
link?:
|
||||
| T
|
||||
| {
|
||||
type?: T;
|
||||
newTab?: T;
|
||||
reference?: T;
|
||||
url?: T;
|
||||
label?: T;
|
||||
appearance?: T;
|
||||
};
|
||||
id?: T;
|
||||
};
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
mediaBlock?:
|
||||
| T
|
||||
| {
|
||||
media?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
archive?:
|
||||
| T
|
||||
| {
|
||||
introContent?: T;
|
||||
populateBy?: T;
|
||||
relationTo?: T;
|
||||
categories?: T;
|
||||
limit?: T;
|
||||
selectedDocs?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
formBlock?:
|
||||
| T
|
||||
| {
|
||||
form?: T;
|
||||
enableIntro?: T;
|
||||
introContent?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
cta?: T | CallToActionBlockSelect<T>;
|
||||
content?: T | ContentBlockSelect<T>;
|
||||
mediaBlock?: T | MediaBlockSelect<T>;
|
||||
archive?: T | ArchiveBlockSelect<T>;
|
||||
formBlock?: T | FormBlockSelect<T>;
|
||||
};
|
||||
meta?:
|
||||
| T
|
||||
@@ -909,6 +871,90 @@ export interface PagesSelect<T extends boolean = true> {
|
||||
createdAt?: T;
|
||||
_status?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "CallToActionBlock_select".
|
||||
*/
|
||||
export interface CallToActionBlockSelect<T extends boolean = true> {
|
||||
richText?: T;
|
||||
links?:
|
||||
| T
|
||||
| {
|
||||
link?:
|
||||
| T
|
||||
| {
|
||||
type?: T;
|
||||
newTab?: T;
|
||||
reference?: T;
|
||||
url?: T;
|
||||
label?: T;
|
||||
appearance?: T;
|
||||
};
|
||||
id?: T;
|
||||
};
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "ContentBlock_select".
|
||||
*/
|
||||
export interface ContentBlockSelect<T extends boolean = true> {
|
||||
columns?:
|
||||
| T
|
||||
| {
|
||||
size?: T;
|
||||
richText?: T;
|
||||
enableLink?: T;
|
||||
link?:
|
||||
| T
|
||||
| {
|
||||
type?: T;
|
||||
newTab?: T;
|
||||
reference?: T;
|
||||
url?: T;
|
||||
label?: T;
|
||||
appearance?: T;
|
||||
};
|
||||
id?: T;
|
||||
};
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "MediaBlock_select".
|
||||
*/
|
||||
export interface MediaBlockSelect<T extends boolean = true> {
|
||||
media?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "ArchiveBlock_select".
|
||||
*/
|
||||
export interface ArchiveBlockSelect<T extends boolean = true> {
|
||||
introContent?: T;
|
||||
populateBy?: T;
|
||||
relationTo?: T;
|
||||
categories?: T;
|
||||
limit?: T;
|
||||
selectedDocs?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "FormBlock_select".
|
||||
*/
|
||||
export interface FormBlockSelect<T extends boolean = true> {
|
||||
form?: T;
|
||||
enableIntro?: T;
|
||||
introContent?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts_select".
|
||||
@@ -1039,6 +1085,8 @@ export interface MediaSelect<T extends boolean = true> {
|
||||
*/
|
||||
export interface CategoriesSelect<T extends boolean = true> {
|
||||
title?: T;
|
||||
slug?: T;
|
||||
slugLock?: T;
|
||||
parent?: T;
|
||||
breadcrumbs?:
|
||||
| T
|
||||
|
||||
@@ -49,6 +49,7 @@ export const plugins: Plugin[] = [
|
||||
}),
|
||||
nestedDocsPlugin({
|
||||
collections: ['categories'],
|
||||
generateURL: (docs) => docs.reduce((url, doc) => `${url}/${doc.slug}`, ''),
|
||||
}),
|
||||
seoPlugin({
|
||||
generateTitle,
|
||||
|
||||
Reference in New Issue
Block a user