feat: auto slug
This commit is contained in:
parent
d6e70824d0
commit
7438f39786
6 changed files with 92 additions and 2 deletions
42
src/admin/components/SlugField/SlugField.tsx
Normal file
42
src/admin/components/SlugField/SlugField.tsx
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
'use client'
|
||||
|
||||
import type { TextFieldClientComponent } from 'payload'
|
||||
import { TextField, useField, useFormFields } from '@payloadcms/ui'
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
const toSlug = (value: string): string =>
|
||||
value
|
||||
.toLowerCase()
|
||||
.replace(/ä/g, 'ae')
|
||||
.replace(/ö/g, 'oe')
|
||||
.replace(/ü/g, 'ue')
|
||||
.replace(/ß/g, 'ss')
|
||||
.replace(/[^\w\s-]/g, '')
|
||||
.replace(/[\s_]+/g, '-')
|
||||
.replace(/-+/g, '-')
|
||||
.replace(/^-+|-+$/g, '')
|
||||
|
||||
export const SlugField: TextFieldClientComponent = (props) => {
|
||||
const { value, setValue } = useField<string>({ path: props.path })
|
||||
const prevAutoSlug = useRef<string>('')
|
||||
const sourceField =
|
||||
((props as Record<string, unknown>).sourceField as string) || 'name'
|
||||
|
||||
const sourceValue = useFormFields(([fields]) => {
|
||||
const field = fields[sourceField]
|
||||
return (field?.value as string) ?? ''
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const newSlug = toSlug(sourceValue)
|
||||
// Only auto-fill if slug is empty or matches the previous auto-slug
|
||||
if (!value || value === prevAutoSlug.current) {
|
||||
setValue(newSlug)
|
||||
}
|
||||
prevAutoSlug.current = newSlug
|
||||
}, [sourceValue]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
return <TextField {...props} />
|
||||
}
|
||||
|
||||
export default SlugField
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import { SlugField as SlugField_d8007c5f8420db846c29d4e7711b3d75 } from '@/admin/components/SlugField/SlugField'
|
||||
import { RscEntryLexicalCell as RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||
import { RscEntryLexicalField as RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||
import { LexicalDiffComponent as LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||
|
|
@ -17,6 +18,7 @@ import { GcsClientUploadHandler as GcsClientUploadHandler_06e62ca02c7c441053a9b6
|
|||
import { CollectionCards as CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 } from '@payloadcms/next/rsc'
|
||||
|
||||
export const importMap = {
|
||||
"@/admin/components/SlugField/SlugField#SlugField": SlugField_d8007c5f8420db846c29d4e7711b3d75,
|
||||
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell": RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalField": RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||
"@payloadcms/richtext-lexical/rsc#LexicalDiffComponent": LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||
|
|
|
|||
|
|
@ -42,7 +42,20 @@ export const Groups: CollectionConfig = {
|
|||
de: 'URL slug',
|
||||
},
|
||||
required: true,
|
||||
unique: true
|
||||
unique: true,
|
||||
admin: {
|
||||
description:
|
||||
'Slug der Gruppe (z.B. "ministrant" → Gruppenseite ist zu finden unter /gruppe/ministrant)',
|
||||
components: {
|
||||
Field: '@/admin/components/SlugField/SlugField#SlugField',
|
||||
},
|
||||
},
|
||||
hooks: {
|
||||
beforeValidate: [
|
||||
({ value }) =>
|
||||
typeof value === 'string' ? value.replace(/^\/+/, '') : value,
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'shortDescription',
|
||||
|
|
|
|||
|
|
@ -56,6 +56,20 @@ export const Pages: CollectionConfig = {
|
|||
},
|
||||
admin: {
|
||||
description: 'URL-Pfad der Seite (z.B. "meine-seite" → /meine-seite)',
|
||||
components: {
|
||||
Field: {
|
||||
path: '@/admin/components/SlugField/SlugField#SlugField',
|
||||
clientProps: {
|
||||
sourceField: 'title',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
hooks: {
|
||||
beforeValidate: [
|
||||
({ value }) =>
|
||||
typeof value === 'string' ? value.replace(/^\/+/, '') : value,
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -31,7 +31,20 @@ export const Parish: CollectionConfig = {
|
|||
de: 'URL slug',
|
||||
},
|
||||
type: 'text',
|
||||
required: true
|
||||
required: true,
|
||||
admin: {
|
||||
description:
|
||||
'Slug der Gemeinde (z.B. "st-clara" → Gemeindeseite ist zu finden unter /gemeinde/st-clara)',
|
||||
components: {
|
||||
Field: '@/admin/components/SlugField/SlugField#SlugField',
|
||||
},
|
||||
},
|
||||
hooks: {
|
||||
beforeValidate: [
|
||||
({ value }) =>
|
||||
typeof value === 'string' ? value.replace(/^\/+/, '') : value,
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'churches',
|
||||
|
|
|
|||
|
|
@ -169,6 +169,9 @@ export interface UserAuthOperations {
|
|||
export interface Parish {
|
||||
id: string;
|
||||
name: string;
|
||||
/**
|
||||
* Slug der Gemeinde (z.B. "st-clara" → Gemeindeseite ist zu finden unter /gemeinde/st-clara)
|
||||
*/
|
||||
slug: string;
|
||||
churches: (string | Church)[];
|
||||
contactPersons?:
|
||||
|
|
@ -546,6 +549,9 @@ export interface Group {
|
|||
id: string;
|
||||
photo?: (string | null) | Media;
|
||||
name: string;
|
||||
/**
|
||||
* Slug der Gruppe (z.B. "ministrant" → Gruppenseite ist zu finden unter /gruppe/ministrant)
|
||||
*/
|
||||
slug: string;
|
||||
shortDescription: string;
|
||||
text?: {
|
||||
|
|
|
|||
Loading…
Reference in a new issue