Compare commits
4 commits
1bd95548fa
...
c605420dcb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c605420dcb | ||
|
|
93625f753b | ||
|
|
5f78e19f07 | ||
|
|
6416d74125 |
28 changed files with 65297 additions and 6 deletions
16
package-lock.json
generated
16
package-lock.json
generated
|
|
@ -13,6 +13,7 @@
|
||||||
"@payloadcms/db-postgres": "^3.74.0",
|
"@payloadcms/db-postgres": "^3.74.0",
|
||||||
"@payloadcms/live-preview-react": "^3.74.0",
|
"@payloadcms/live-preview-react": "^3.74.0",
|
||||||
"@payloadcms/next": "^3.74.0",
|
"@payloadcms/next": "^3.74.0",
|
||||||
|
"@payloadcms/plugin-search": "^3.74.0",
|
||||||
"@payloadcms/richtext-lexical": "^3.74.0",
|
"@payloadcms/richtext-lexical": "^3.74.0",
|
||||||
"@payloadcms/storage-gcs": "^3.74.0",
|
"@payloadcms/storage-gcs": "^3.74.0",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
|
|
@ -2011,6 +2012,21 @@
|
||||||
"react-dom": "^19.0.1 || ^19.1.2 || ^19.2.1"
|
"react-dom": "^19.0.1 || ^19.1.2 || ^19.2.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@payloadcms/plugin-search": {
|
||||||
|
"version": "3.74.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@payloadcms/plugin-search/-/plugin-search-3.74.0.tgz",
|
||||||
|
"integrity": "sha512-LFExPKtv3bQPsRQ6LZ4IRUYFPs2bsvcT6ctjqwU1hp3o6+Ily9ltZcA19+Bu6gBw4DvWKHlczVgotmAewGAiPw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@payloadcms/next": "3.74.0",
|
||||||
|
"@payloadcms/ui": "3.74.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"payload": "3.74.0",
|
||||||
|
"react": "^19.0.1 || ^19.1.2 || ^19.2.1",
|
||||||
|
"react-dom": "^19.0.1 || ^19.1.2 || ^19.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@payloadcms/richtext-lexical": {
|
"node_modules/@payloadcms/richtext-lexical": {
|
||||||
"version": "3.74.0",
|
"version": "3.74.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
"@payloadcms/db-postgres": "^3.74.0",
|
"@payloadcms/db-postgres": "^3.74.0",
|
||||||
"@payloadcms/live-preview-react": "^3.74.0",
|
"@payloadcms/live-preview-react": "^3.74.0",
|
||||||
"@payloadcms/next": "^3.74.0",
|
"@payloadcms/next": "^3.74.0",
|
||||||
|
"@payloadcms/plugin-search": "^3.74.0",
|
||||||
"@payloadcms/richtext-lexical": "^3.74.0",
|
"@payloadcms/richtext-lexical": "^3.74.0",
|
||||||
"@payloadcms/storage-gcs": "^3.74.0",
|
"@payloadcms/storage-gcs": "^3.74.0",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
|
|
|
||||||
15
src/app/(home)/suche/page.tsx
Normal file
15
src/app/(home)/suche/page.tsx
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { fetchSearchResults } from '@/fetch/search'
|
||||||
|
import { SearchPage } from '@/pageComponents/Search/SearchPage'
|
||||||
|
|
||||||
|
export const dynamic = 'force-dynamic'
|
||||||
|
|
||||||
|
export default async function Page({
|
||||||
|
searchParams,
|
||||||
|
}: {
|
||||||
|
searchParams: Promise<{ q?: string }>
|
||||||
|
}) {
|
||||||
|
const { q = '' } = await searchParams
|
||||||
|
const results = await fetchSearchResults(q)
|
||||||
|
|
||||||
|
return <SearchPage query={q} results={results} />
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,8 @@ import { HeadingFeatureClient as HeadingFeatureClient_e70f5e05f09f93e00b997edb1e
|
||||||
import { UnderlineFeatureClient as UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
import { UnderlineFeatureClient as UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
import { BoldFeatureClient as BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
import { BoldFeatureClient as BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
import { ItalicFeatureClient as ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
import { ItalicFeatureClient as ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
|
import { LinkToDoc as LinkToDoc_aead06e4cbf6b2620c5c51c9ab283634 } from '@payloadcms/plugin-search/client'
|
||||||
|
import { ReindexButton as ReindexButton_aead06e4cbf6b2620c5c51c9ab283634 } from '@payloadcms/plugin-search/client'
|
||||||
import { default as default_9bcae99938dc292be0063ce32055e14c } from '../../../components/Logo/Logo'
|
import { default as default_9bcae99938dc292be0063ce32055e14c } from '../../../components/Logo/Logo'
|
||||||
import { GcsClientUploadHandler as GcsClientUploadHandler_06e62ca02c7c441053a9b643e5545934 } from '@payloadcms/storage-gcs/client'
|
import { GcsClientUploadHandler as GcsClientUploadHandler_06e62ca02c7c441053a9b643e5545934 } from '@payloadcms/storage-gcs/client'
|
||||||
import { CollectionCards as CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 } from '@payloadcms/next/rsc'
|
import { CollectionCards as CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 } from '@payloadcms/next/rsc'
|
||||||
|
|
@ -31,6 +33,8 @@ export const importMap = {
|
||||||
"@payloadcms/richtext-lexical/client#UnderlineFeatureClient": UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
"@payloadcms/richtext-lexical/client#UnderlineFeatureClient": UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
"@payloadcms/richtext-lexical/client#BoldFeatureClient": BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
"@payloadcms/richtext-lexical/client#BoldFeatureClient": BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
"@payloadcms/richtext-lexical/client#ItalicFeatureClient": ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
"@payloadcms/richtext-lexical/client#ItalicFeatureClient": ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
|
"@payloadcms/plugin-search/client#LinkToDoc": LinkToDoc_aead06e4cbf6b2620c5c51c9ab283634,
|
||||||
|
"@payloadcms/plugin-search/client#ReindexButton": ReindexButton_aead06e4cbf6b2620c5c51c9ab283634,
|
||||||
"/components/Logo/Logo#default": default_9bcae99938dc292be0063ce32055e14c,
|
"/components/Logo/Logo#default": default_9bcae99938dc292be0063ce32055e14c,
|
||||||
"@payloadcms/storage-gcs/client#GcsClientUploadHandler": GcsClientUploadHandler_06e62ca02c7c441053a9b643e5545934,
|
"@payloadcms/storage-gcs/client#GcsClientUploadHandler": GcsClientUploadHandler_06e62ca02c7c441053a9b643e5545934,
|
||||||
"@payloadcms/next/rsc#CollectionCards": CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1
|
"@payloadcms/next/rsc#CollectionCards": CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import { EventsBlock } from '@/collections/blocks/Events'
|
||||||
import { PublicationAndNewsletterBlock } from '@/collections/blocks/PublicationAndNewsletter'
|
import { PublicationAndNewsletterBlock } from '@/collections/blocks/PublicationAndNewsletter'
|
||||||
import { ContactPersonBlock } from '@/collections/blocks/ContactPersonBlock'
|
import { ContactPersonBlock } from '@/collections/blocks/ContactPersonBlock'
|
||||||
import { ImageCardsBlock } from '@/collections/blocks/ImageCards'
|
import { ImageCardsBlock } from '@/collections/blocks/ImageCards'
|
||||||
|
import { ClassifiedsBlock } from '@/collections/blocks/Classifieds'
|
||||||
import { isPublishedPublic } from '@/collections/access/public'
|
import { isPublishedPublic } from '@/collections/access/public'
|
||||||
|
|
||||||
export const Pages: CollectionConfig = {
|
export const Pages: CollectionConfig = {
|
||||||
|
|
@ -102,6 +103,7 @@ export const Pages: CollectionConfig = {
|
||||||
EventsBlock,
|
EventsBlock,
|
||||||
ContactPersonBlock,
|
ContactPersonBlock,
|
||||||
ImageCardsBlock,
|
ImageCardsBlock,
|
||||||
|
ClassifiedsBlock,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ export const Parish: CollectionConfig = {
|
||||||
{
|
{
|
||||||
name: 'contactPersons',
|
name: 'contactPersons',
|
||||||
label: {
|
label: {
|
||||||
de: "Ansprechpartner"
|
de: "Kontakt"
|
||||||
},
|
},
|
||||||
type: 'array',
|
type: 'array',
|
||||||
fields: [
|
fields: [
|
||||||
|
|
|
||||||
14
src/collections/blocks/Classifieds.ts
Normal file
14
src/collections/blocks/Classifieds.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { Block } from 'payload'
|
||||||
|
|
||||||
|
export const ClassifiedsBlock: Block = {
|
||||||
|
slug: 'classifieds',
|
||||||
|
labels: {
|
||||||
|
singular: {
|
||||||
|
de: 'Kleinanzeigen',
|
||||||
|
},
|
||||||
|
plural: {
|
||||||
|
de: 'Kleinanzeigen',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fields: [],
|
||||||
|
}
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
|
|
||||||
.shade {
|
.shade {
|
||||||
background-color: $shade2;
|
background-color: $shade2;
|
||||||
color: $base-color;
|
color: $base-color !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shade:hover {
|
.shade:hover {
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@
|
||||||
line-height: 95%;
|
line-height: 95%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 1100px) {
|
@media screen and (max-width: 1400px) {
|
||||||
.menu {
|
.menu {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 30px;
|
gap: 30px;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import { MegaMenu } from '@/components/MegaMenu/MegaMenu'
|
||||||
import { CollapsibleArrow } from '@/components/CollapsibleArrow/CollapsibleArrow'
|
import { CollapsibleArrow } from '@/components/CollapsibleArrow/CollapsibleArrow'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { siteConfig } from '@/config/site'
|
import { siteConfig } from '@/config/site'
|
||||||
|
import { MenuSearch } from './MenuSearch'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a simple item component.
|
* Represents a simple item component.
|
||||||
|
|
@ -180,6 +181,7 @@ export const Menu = ({menu}: MenuProps) => {
|
||||||
items={menu.rightItems}
|
items={menu.rightItems}
|
||||||
onItemClick={() => setDisplayMenuMobile(false)}
|
onItemClick={() => setDisplayMenuMobile(false)}
|
||||||
/>
|
/>
|
||||||
|
<MenuSearch onSubmitted={() => setDisplayMenuMobile(false)} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
||||||
41
src/components/Menu/MenuSearch.tsx
Normal file
41
src/components/Menu/MenuSearch.tsx
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
'use client'
|
||||||
|
|
||||||
|
import { FormEvent, useState } from 'react'
|
||||||
|
import { useRouter } from 'next/navigation'
|
||||||
|
import Image from 'next/image'
|
||||||
|
import SearchIcon from './search.svg'
|
||||||
|
import styles from './styles.module.scss'
|
||||||
|
|
||||||
|
type MenuSearchProps = {
|
||||||
|
onSubmitted?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MenuSearch = ({ onSubmitted }: MenuSearchProps) => {
|
||||||
|
const router = useRouter()
|
||||||
|
const [value, setValue] = useState('')
|
||||||
|
|
||||||
|
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault()
|
||||||
|
const q = value.trim()
|
||||||
|
if (!q) return
|
||||||
|
router.push(`/suche?q=${encodeURIComponent(q)}`)
|
||||||
|
onSubmitted?.()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form className={styles.search} onSubmit={handleSubmit} role="search">
|
||||||
|
<input
|
||||||
|
type="search"
|
||||||
|
name="q"
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => setValue(e.target.value)}
|
||||||
|
placeholder="Suchen..."
|
||||||
|
aria-label="Suche"
|
||||||
|
className={styles.searchInput}
|
||||||
|
/>
|
||||||
|
<button type="submit" className={styles.searchButton} aria-label="Suchen">
|
||||||
|
<Image src={SearchIcon} width={20} height={20} alt="" />
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
||||||
5
src/components/Menu/search.svg
Normal file
5
src/components/Menu/search.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="11" cy="11" r="7" stroke="#333333" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M20 20L16.65 16.65" stroke="#333333" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 367 B |
|
|
@ -80,7 +80,46 @@
|
||||||
max-height: 1000px;
|
max-height: 1000px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 1100px) {
|
.search {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchInput {
|
||||||
|
width: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid transparent;
|
||||||
|
background: transparent;
|
||||||
|
color: inherit;
|
||||||
|
font: inherit;
|
||||||
|
outline: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
transition: width 0.3s ease-in-out, padding 0.3s ease-in-out, border-color 0.3s ease-in-out;
|
||||||
|
|
||||||
|
&::-webkit-search-cancel-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search:hover .searchInput,
|
||||||
|
.search:focus-within .searchInput {
|
||||||
|
width: 180px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-bottom-color: $shade1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchButton {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1400px) {
|
||||||
.nav {
|
.nav {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 15px 15px;
|
padding: 15px 15px;
|
||||||
|
|
@ -116,4 +155,16 @@
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchInput,
|
||||||
|
.search:hover .searchInput,
|
||||||
|
.search:focus-within .searchInput {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
border-bottom-color: $border-color-light;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import { BlogSliderBlock } from '@/compositions/Blocks/BlogSliderBlock'
|
||||||
import { MassTimesBlock } from '@/compositions/Blocks/MassTimesBlock'
|
import { MassTimesBlock } from '@/compositions/Blocks/MassTimesBlock'
|
||||||
import { EventsBlock } from '@/compositions/Blocks/EventsBlock'
|
import { EventsBlock } from '@/compositions/Blocks/EventsBlock'
|
||||||
import { ImageCardsBlock } from '@/compositions/Blocks/ImageCardsBlock'
|
import { ImageCardsBlock } from '@/compositions/Blocks/ImageCardsBlock'
|
||||||
|
import { ClassifiedsFromApi } from '@/components/Classifieds/ClassifiedsFromApi'
|
||||||
|
|
||||||
type BlocksProps = {
|
type BlocksProps = {
|
||||||
content: Blog['content']['content'] | NonNullable<Page['content']>
|
content: Blog['content']['content'] | NonNullable<Page['content']>
|
||||||
|
|
@ -239,6 +240,16 @@ export function Blocks({ content }: BlocksProps) {
|
||||||
return <ImageCardsBlock key={item.id} items={item.items} />
|
return <ImageCardsBlock key={item.id} items={item.items} />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item.blockType === 'classifieds') {
|
||||||
|
return (
|
||||||
|
<Section key={item.id} padding={'small'}>
|
||||||
|
<Container>
|
||||||
|
<ClassifiedsFromApi />
|
||||||
|
</Container>
|
||||||
|
</Section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (item.blockType === 'contactPersonBlock') {
|
if (item.blockType === 'contactPersonBlock') {
|
||||||
const contact = typeof item.contact === 'object'
|
const contact = typeof item.contact === 'object'
|
||||||
? item.contact
|
? item.contact
|
||||||
|
|
|
||||||
23
src/fetch/search.ts
Normal file
23
src/fetch/search.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { getPayload } from 'payload'
|
||||||
|
import config from '@/payload.config'
|
||||||
|
import { Search } from '@/payload-types'
|
||||||
|
|
||||||
|
export async function fetchSearchResults(query: string): Promise<Search[]> {
|
||||||
|
const trimmed = query.trim()
|
||||||
|
if (!trimmed) return []
|
||||||
|
|
||||||
|
const payload = await getPayload({ config })
|
||||||
|
const result = await payload.find({
|
||||||
|
collection: 'search',
|
||||||
|
where: {
|
||||||
|
title: {
|
||||||
|
contains: trimmed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
depth: 1,
|
||||||
|
limit: 50,
|
||||||
|
sort: 'priority',
|
||||||
|
})
|
||||||
|
|
||||||
|
return result.docs
|
||||||
|
}
|
||||||
21487
src/migrations/20260413_093410_search.json
Normal file
21487
src/migrations/20260413_093410_search.json
Normal file
File diff suppressed because it is too large
Load diff
59
src/migrations/20260413_093410_search.ts
Normal file
59
src/migrations/20260413_093410_search.ts
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
|
||||||
|
|
||||||
|
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
|
||||||
|
await db.execute(sql`
|
||||||
|
CREATE TABLE "search" (
|
||||||
|
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||||
|
"title" varchar,
|
||||||
|
"priority" numeric,
|
||||||
|
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||||
|
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "search_rels" (
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"order" integer,
|
||||||
|
"parent_id" uuid NOT NULL,
|
||||||
|
"path" varchar NOT NULL,
|
||||||
|
"parish_id" uuid,
|
||||||
|
"blog_id" uuid,
|
||||||
|
"event_id" uuid,
|
||||||
|
"group_id" uuid
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE "announcement" ALTER COLUMN "date" SET DEFAULT '2026-04-19T09:34:10.248Z';
|
||||||
|
ALTER TABLE "calendar" ALTER COLUMN "date" SET DEFAULT '2026-04-19T09:34:10.532Z';
|
||||||
|
ALTER TABLE "classifieds" ALTER COLUMN "until" SET DEFAULT '2026-05-13T09:34:10.591Z';
|
||||||
|
ALTER TABLE "payload_locked_documents_rels" ADD COLUMN "search_id" uuid;
|
||||||
|
ALTER TABLE "search_rels" ADD CONSTRAINT "search_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."search"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "search_rels" ADD CONSTRAINT "search_rels_parish_fk" FOREIGN KEY ("parish_id") REFERENCES "public"."parish"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "search_rels" ADD CONSTRAINT "search_rels_blog_fk" FOREIGN KEY ("blog_id") REFERENCES "public"."blog"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "search_rels" ADD CONSTRAINT "search_rels_event_fk" FOREIGN KEY ("event_id") REFERENCES "public"."event"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "search_rels" ADD CONSTRAINT "search_rels_group_fk" FOREIGN KEY ("group_id") REFERENCES "public"."group"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
CREATE INDEX "search_updated_at_idx" ON "search" USING btree ("updated_at");
|
||||||
|
CREATE INDEX "search_created_at_idx" ON "search" USING btree ("created_at");
|
||||||
|
CREATE INDEX "search_rels_order_idx" ON "search_rels" USING btree ("order");
|
||||||
|
CREATE INDEX "search_rels_parent_idx" ON "search_rels" USING btree ("parent_id");
|
||||||
|
CREATE INDEX "search_rels_path_idx" ON "search_rels" USING btree ("path");
|
||||||
|
CREATE INDEX "search_rels_parish_id_idx" ON "search_rels" USING btree ("parish_id");
|
||||||
|
CREATE INDEX "search_rels_blog_id_idx" ON "search_rels" USING btree ("blog_id");
|
||||||
|
CREATE INDEX "search_rels_event_id_idx" ON "search_rels" USING btree ("event_id");
|
||||||
|
CREATE INDEX "search_rels_group_id_idx" ON "search_rels" USING btree ("group_id");
|
||||||
|
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_search_fk" FOREIGN KEY ("search_id") REFERENCES "public"."search"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
CREATE INDEX "payload_locked_documents_rels_search_id_idx" ON "payload_locked_documents_rels" USING btree ("search_id");`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
|
||||||
|
await db.execute(sql`
|
||||||
|
ALTER TABLE "search" DISABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "search_rels" DISABLE ROW LEVEL SECURITY;
|
||||||
|
DROP TABLE "search" CASCADE;
|
||||||
|
DROP TABLE "search_rels" CASCADE;
|
||||||
|
ALTER TABLE "payload_locked_documents_rels" DROP CONSTRAINT "payload_locked_documents_rels_search_fk";
|
||||||
|
|
||||||
|
DROP INDEX "payload_locked_documents_rels_search_id_idx";
|
||||||
|
ALTER TABLE "announcement" ALTER COLUMN "date" SET DEFAULT '2026-04-12T12:46:39.042Z';
|
||||||
|
ALTER TABLE "calendar" ALTER COLUMN "date" SET DEFAULT '2026-04-12T12:46:39.348Z';
|
||||||
|
ALTER TABLE "classifieds" ALTER COLUMN "until" SET DEFAULT '2026-05-10T12:46:39.403Z';
|
||||||
|
ALTER TABLE "payload_locked_documents_rels" DROP COLUMN "search_id";`)
|
||||||
|
}
|
||||||
21521
src/migrations/20260413_094618_search_pages.json
Normal file
21521
src/migrations/20260413_094618_search_pages.json
Normal file
File diff suppressed because it is too large
Load diff
22
src/migrations/20260413_094618_search_pages.ts
Normal file
22
src/migrations/20260413_094618_search_pages.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
|
||||||
|
|
||||||
|
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
|
||||||
|
await db.execute(sql`
|
||||||
|
ALTER TABLE "announcement" ALTER COLUMN "date" SET DEFAULT '2026-04-19T09:46:18.288Z';
|
||||||
|
ALTER TABLE "calendar" ALTER COLUMN "date" SET DEFAULT '2026-04-19T09:46:18.569Z';
|
||||||
|
ALTER TABLE "classifieds" ALTER COLUMN "until" SET DEFAULT '2026-05-13T09:46:18.627Z';
|
||||||
|
ALTER TABLE "search_rels" ADD COLUMN "pages_id" uuid;
|
||||||
|
ALTER TABLE "search_rels" ADD CONSTRAINT "search_rels_pages_fk" FOREIGN KEY ("pages_id") REFERENCES "public"."pages"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
CREATE INDEX "search_rels_pages_id_idx" ON "search_rels" USING btree ("pages_id");`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
|
||||||
|
await db.execute(sql`
|
||||||
|
ALTER TABLE "search_rels" DROP CONSTRAINT "search_rels_pages_fk";
|
||||||
|
|
||||||
|
DROP INDEX "search_rels_pages_id_idx";
|
||||||
|
ALTER TABLE "announcement" ALTER COLUMN "date" SET DEFAULT '2026-04-19T09:34:10.248Z';
|
||||||
|
ALTER TABLE "calendar" ALTER COLUMN "date" SET DEFAULT '2026-04-19T09:34:10.532Z';
|
||||||
|
ALTER TABLE "classifieds" ALTER COLUMN "until" SET DEFAULT '2026-05-13T09:34:10.591Z';
|
||||||
|
ALTER TABLE "search_rels" DROP COLUMN "pages_id";`)
|
||||||
|
}
|
||||||
21734
src/migrations/20260413_122020.json
Normal file
21734
src/migrations/20260413_122020.json
Normal file
File diff suppressed because it is too large
Load diff
44
src/migrations/20260413_122020.ts
Normal file
44
src/migrations/20260413_122020.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
|
||||||
|
|
||||||
|
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
|
||||||
|
await db.execute(sql`
|
||||||
|
CREATE TABLE "pages_blocks_classifieds" (
|
||||||
|
"_order" integer NOT NULL,
|
||||||
|
"_parent_id" uuid NOT NULL,
|
||||||
|
"_path" text NOT NULL,
|
||||||
|
"id" varchar PRIMARY KEY NOT NULL,
|
||||||
|
"block_name" varchar
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "_pages_v_blocks_classifieds" (
|
||||||
|
"_order" integer NOT NULL,
|
||||||
|
"_parent_id" uuid NOT NULL,
|
||||||
|
"_path" text NOT NULL,
|
||||||
|
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||||
|
"_uuid" varchar,
|
||||||
|
"block_name" varchar
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE "announcement" ALTER COLUMN "date" SET DEFAULT '2026-04-19T12:20:19.986Z';
|
||||||
|
ALTER TABLE "calendar" ALTER COLUMN "date" SET DEFAULT '2026-04-19T12:20:20.277Z';
|
||||||
|
ALTER TABLE "classifieds" ALTER COLUMN "until" SET DEFAULT '2026-05-13T12:20:20.339Z';
|
||||||
|
ALTER TABLE "pages_blocks_classifieds" ADD CONSTRAINT "pages_blocks_classifieds_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "_pages_v_blocks_classifieds" ADD CONSTRAINT "_pages_v_blocks_classifieds_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_pages_v"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
CREATE INDEX "pages_blocks_classifieds_order_idx" ON "pages_blocks_classifieds" USING btree ("_order");
|
||||||
|
CREATE INDEX "pages_blocks_classifieds_parent_id_idx" ON "pages_blocks_classifieds" USING btree ("_parent_id");
|
||||||
|
CREATE INDEX "pages_blocks_classifieds_path_idx" ON "pages_blocks_classifieds" USING btree ("_path");
|
||||||
|
CREATE INDEX "_pages_v_blocks_classifieds_order_idx" ON "_pages_v_blocks_classifieds" USING btree ("_order");
|
||||||
|
CREATE INDEX "_pages_v_blocks_classifieds_parent_id_idx" ON "_pages_v_blocks_classifieds" USING btree ("_parent_id");
|
||||||
|
CREATE INDEX "_pages_v_blocks_classifieds_path_idx" ON "_pages_v_blocks_classifieds" USING btree ("_path");`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
|
||||||
|
await db.execute(sql`
|
||||||
|
ALTER TABLE "pages_blocks_classifieds" DISABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "_pages_v_blocks_classifieds" DISABLE ROW LEVEL SECURITY;
|
||||||
|
DROP TABLE "pages_blocks_classifieds" CASCADE;
|
||||||
|
DROP TABLE "_pages_v_blocks_classifieds" CASCADE;
|
||||||
|
ALTER TABLE "announcement" ALTER COLUMN "date" SET DEFAULT '2026-04-19T09:46:18.288Z';
|
||||||
|
ALTER TABLE "calendar" ALTER COLUMN "date" SET DEFAULT '2026-04-19T09:46:18.569Z';
|
||||||
|
ALTER TABLE "classifieds" ALTER COLUMN "until" SET DEFAULT '2026-05-13T09:46:18.627Z';`)
|
||||||
|
}
|
||||||
|
|
@ -30,6 +30,9 @@ import * as migration_20260409_083638 from './20260409_083638';
|
||||||
import * as migration_20260410_114057_imagecards_block from './20260410_114057_imagecards_block';
|
import * as migration_20260410_114057_imagecards_block from './20260410_114057_imagecards_block';
|
||||||
import * as migration_20260410_115248_parish_title_block from './20260410_115248_parish_title_block';
|
import * as migration_20260410_115248_parish_title_block from './20260410_115248_parish_title_block';
|
||||||
import * as migration_20260410_124639 from './20260410_124639';
|
import * as migration_20260410_124639 from './20260410_124639';
|
||||||
|
import * as migration_20260413_093410_search from './20260413_093410_search';
|
||||||
|
import * as migration_20260413_094618_search_pages from './20260413_094618_search_pages';
|
||||||
|
import * as migration_20260413_122020 from './20260413_122020';
|
||||||
|
|
||||||
export const migrations = [
|
export const migrations = [
|
||||||
{
|
{
|
||||||
|
|
@ -190,6 +193,21 @@ export const migrations = [
|
||||||
{
|
{
|
||||||
up: migration_20260410_124639.up,
|
up: migration_20260410_124639.up,
|
||||||
down: migration_20260410_124639.down,
|
down: migration_20260410_124639.down,
|
||||||
name: '20260410_124639'
|
name: '20260410_124639',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
up: migration_20260413_093410_search.up,
|
||||||
|
down: migration_20260413_093410_search.down,
|
||||||
|
name: '20260413_093410_search',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
up: migration_20260413_094618_search_pages.up,
|
||||||
|
down: migration_20260413_094618_search_pages.down,
|
||||||
|
name: '20260413_094618_search_pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
up: migration_20260413_122020.up,
|
||||||
|
down: migration_20260413_122020.down,
|
||||||
|
name: '20260413_122020'
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ export const Parish = (
|
||||||
<Events events={tranformWorship(worship)} n={4}/>
|
<Events events={tranformWorship(worship)} n={4}/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col>
|
<Col>
|
||||||
<Title title={"Ansprechpersonen"} size={"md"} color={"contrast"} />
|
<Title title={"Kontakt"} size={"md"} color={"contrast"} />
|
||||||
|
|
||||||
<ContactPersonList persons={contactPersons} />
|
<ContactPersonList persons={contactPersons} />
|
||||||
</Col>
|
</Col>
|
||||||
|
|
|
||||||
59
src/pageComponents/Search/SearchPage.tsx
Normal file
59
src/pageComponents/Search/SearchPage.tsx
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { Search } from '@/payload-types'
|
||||||
|
import { Section } from '@/components/Section/Section'
|
||||||
|
import { Container } from '@/components/Container/Container'
|
||||||
|
import { PageHeader } from '@/compositions/PageHeader/PageHeader'
|
||||||
|
import { getSearchResultUrl } from '@/utils/getSearchResultUrl'
|
||||||
|
import styles from './styles.module.scss'
|
||||||
|
|
||||||
|
const RELATION_LABELS: Record<Search['doc']['relationTo'], string> = {
|
||||||
|
pages: 'Seite',
|
||||||
|
blog: 'Nachricht',
|
||||||
|
parish: 'Gemeinde',
|
||||||
|
event: 'Veranstaltung',
|
||||||
|
group: 'Gruppe',
|
||||||
|
}
|
||||||
|
|
||||||
|
type SearchPageProps = {
|
||||||
|
query: string
|
||||||
|
results: Search[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SearchPage = ({ query, results }: SearchPageProps) => {
|
||||||
|
const linkedResults = results
|
||||||
|
.map((result) => ({ result, href: getSearchResultUrl(result.doc) }))
|
||||||
|
.filter((r): r is { result: Search; href: string } => r.href !== null)
|
||||||
|
|
||||||
|
const description = query
|
||||||
|
? `Suchergebnisse für „${query}"`
|
||||||
|
: 'Bitte geben Sie einen Suchbegriff ein.'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PageHeader title="Suche" description={description} />
|
||||||
|
|
||||||
|
<Section padding="small">
|
||||||
|
<Container>
|
||||||
|
{query && linkedResults.length === 0 && (
|
||||||
|
<p className={styles.empty}>Keine Ergebnisse für „{query}“.</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{linkedResults.length > 0 && (
|
||||||
|
<ul className={styles.results}>
|
||||||
|
{linkedResults.map(({ result, href }) => (
|
||||||
|
<li key={result.id} className={styles.result}>
|
||||||
|
<Link href={href} className={styles.resultLink}>
|
||||||
|
<span className={styles.resultTitle}>{result.title}</span>
|
||||||
|
<span className={styles.resultType}>
|
||||||
|
{RELATION_LABELS[result.doc.relationTo]}
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</Container>
|
||||||
|
</Section>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
49
src/pageComponents/Search/styles.module.scss
Normal file
49
src/pageComponents/Search/styles.module.scss
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
@import "template.scss";
|
||||||
|
|
||||||
|
.results {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result {
|
||||||
|
border-bottom: 1px solid $border-color-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resultLink {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 20px;
|
||||||
|
padding: 16px 0;
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: opacity 100ms ease-in;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.resultTitle {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resultType {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $base-color;
|
||||||
|
opacity: 0.6;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
font-size: 18px;
|
||||||
|
color: $base-color;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
@ -87,6 +87,7 @@ export interface Config {
|
||||||
documents: Document;
|
documents: Document;
|
||||||
media: Media;
|
media: Media;
|
||||||
users: User;
|
users: User;
|
||||||
|
search: Search;
|
||||||
'payload-kv': PayloadKv;
|
'payload-kv': PayloadKv;
|
||||||
'payload-jobs': PayloadJob;
|
'payload-jobs': PayloadJob;
|
||||||
'payload-locked-documents': PayloadLockedDocument;
|
'payload-locked-documents': PayloadLockedDocument;
|
||||||
|
|
@ -115,6 +116,7 @@ export interface Config {
|
||||||
documents: DocumentsSelect<false> | DocumentsSelect<true>;
|
documents: DocumentsSelect<false> | DocumentsSelect<true>;
|
||||||
media: MediaSelect<false> | MediaSelect<true>;
|
media: MediaSelect<false> | MediaSelect<true>;
|
||||||
users: UsersSelect<false> | UsersSelect<true>;
|
users: UsersSelect<false> | UsersSelect<true>;
|
||||||
|
search: SearchSelect<false> | SearchSelect<true>;
|
||||||
'payload-kv': PayloadKvSelect<false> | PayloadKvSelect<true>;
|
'payload-kv': PayloadKvSelect<false> | PayloadKvSelect<true>;
|
||||||
'payload-jobs': PayloadJobsSelect<false> | PayloadJobsSelect<true>;
|
'payload-jobs': PayloadJobsSelect<false> | PayloadJobsSelect<true>;
|
||||||
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
||||||
|
|
@ -611,6 +613,11 @@ export interface Page {
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'imageCards';
|
blockType: 'imageCards';
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: 'classifieds';
|
||||||
|
}
|
||||||
)[]
|
)[]
|
||||||
| null;
|
| null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
|
|
@ -1068,6 +1075,40 @@ export interface User {
|
||||||
| null;
|
| null;
|
||||||
password?: string | null;
|
password?: string | null;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 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".
|
||||||
|
*/
|
||||||
|
export interface Search {
|
||||||
|
id: string;
|
||||||
|
title?: string | null;
|
||||||
|
priority?: number | null;
|
||||||
|
doc:
|
||||||
|
| {
|
||||||
|
relationTo: 'parish';
|
||||||
|
value: string | Parish;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
relationTo: 'pages';
|
||||||
|
value: string | Page;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
relationTo: 'blog';
|
||||||
|
value: string | Blog;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
relationTo: 'event';
|
||||||
|
value: string | Event;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
relationTo: 'group';
|
||||||
|
value: string | Group;
|
||||||
|
};
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "payload-kv".
|
* via the `definition` "payload-kv".
|
||||||
|
|
@ -1272,6 +1313,10 @@ export interface PayloadLockedDocument {
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'users';
|
relationTo: 'users';
|
||||||
value: string | User;
|
value: string | User;
|
||||||
|
} | null)
|
||||||
|
| ({
|
||||||
|
relationTo: 'search';
|
||||||
|
value: string | Search;
|
||||||
} | null);
|
} | null);
|
||||||
globalSlug?: string | null;
|
globalSlug?: string | null;
|
||||||
user: {
|
user: {
|
||||||
|
|
@ -1925,6 +1970,12 @@ export interface PagesSelect<T extends boolean = true> {
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
};
|
};
|
||||||
|
classifieds?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
id?: T;
|
||||||
|
blockName?: T;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
|
|
@ -2062,6 +2113,17 @@ export interface UsersSelect<T extends boolean = true> {
|
||||||
expiresAt?: T;
|
expiresAt?: T;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "search_select".
|
||||||
|
*/
|
||||||
|
export interface SearchSelect<T extends boolean = true> {
|
||||||
|
title?: T;
|
||||||
|
priority?: T;
|
||||||
|
doc?: T;
|
||||||
|
updatedAt?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "payload-kv_select".
|
* via the `definition` "payload-kv_select".
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ import { Pages } from '@/collections/Pages'
|
||||||
import { Prayers } from '@/collections/Prayers'
|
import { Prayers } from '@/collections/Prayers'
|
||||||
import { siteConfig } from '@/config/site'
|
import { siteConfig } from '@/config/site'
|
||||||
import { generateRecurringMassesTask } from '@/jobs/generateRecurringMasses'
|
import { generateRecurringMassesTask } from '@/jobs/generateRecurringMasses'
|
||||||
|
import { searchPlugin } from '@payloadcms/plugin-search'
|
||||||
|
|
||||||
const filename = fileURLToPath(import.meta.url)
|
const filename = fileURLToPath(import.meta.url)
|
||||||
const dirname = path.dirname(filename)
|
const dirname = path.dirname(filename)
|
||||||
|
|
@ -196,5 +197,35 @@ export default buildConfig({
|
||||||
options: {},
|
options: {},
|
||||||
acl: undefined,
|
acl: undefined,
|
||||||
}),
|
}),
|
||||||
|
searchPlugin({
|
||||||
|
collections: ['parish', 'pages', 'blog', 'event', 'group'],
|
||||||
|
defaultPriorities: {
|
||||||
|
parish: 10,
|
||||||
|
pages: 15,
|
||||||
|
group: 20,
|
||||||
|
event: 30,
|
||||||
|
blog: 40,
|
||||||
|
},
|
||||||
|
// The plugin only reads `title` from the source doc, but Parish and
|
||||||
|
// Group use `name` instead. Map `name` → `title` so those collections
|
||||||
|
// get an indexed, searchable title.
|
||||||
|
beforeSync: ({ originalDoc, searchDoc }) => ({
|
||||||
|
...searchDoc,
|
||||||
|
title: originalDoc.title || originalDoc.name || searchDoc.title || '',
|
||||||
|
}),
|
||||||
|
// The plugin hardcodes `maxDepth: 0` on the polymorphic `doc` field,
|
||||||
|
// so `doc.value` is always returned as a string ID — even with depth>0
|
||||||
|
// at query time. We need it populated so the search results page can
|
||||||
|
// read each referenced document's `slug` to build URLs like
|
||||||
|
// /gemeinde/[slug] and /gruppe/[slug].
|
||||||
|
searchOverrides: {
|
||||||
|
fields: ({ defaultFields }) =>
|
||||||
|
defaultFields.map((field) =>
|
||||||
|
'name' in field && field.name === 'doc' && field.type === 'relationship'
|
||||||
|
? { ...field, maxDepth: 1 }
|
||||||
|
: field,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
})
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
|
||||||
20
src/utils/getSearchResultUrl.ts
Normal file
20
src/utils/getSearchResultUrl.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { Search } from '@/payload-types'
|
||||||
|
|
||||||
|
export function getSearchResultUrl(doc: Search['doc']): string | null {
|
||||||
|
if (!doc || typeof doc.value === 'string') return null
|
||||||
|
|
||||||
|
switch (doc.relationTo) {
|
||||||
|
case 'pages':
|
||||||
|
return doc.value.slug ? `/${doc.value.slug}` : null
|
||||||
|
case 'blog':
|
||||||
|
return `/blog/${doc.value.id}`
|
||||||
|
case 'parish':
|
||||||
|
return doc.value.slug ? `/gemeinde/${doc.value.slug}` : null
|
||||||
|
case 'event':
|
||||||
|
return `/veranstaltungen/${doc.value.id}`
|
||||||
|
case 'group':
|
||||||
|
return doc.value.slug ? `/gruppe/${doc.value.slug}` : null
|
||||||
|
default:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue