feature: sort + filter mass time locations

This commit is contained in:
Benno Tielen 2026-04-17 09:57:45 +02:00
parent 130d5b89df
commit 024cdad9dc
7 changed files with 24473 additions and 12 deletions

View file

@ -26,5 +26,20 @@ export const MassTimesBlock: Block = {
de: 'Untertitel',
},
},
{
name: 'churches',
label: {
de: 'Kirchengebäuden',
},
type: 'relationship',
relationTo: 'church',
hasMany: true,
admin: {
allowCreate: false,
description: {
de: 'Leer lassen, um alle Kirchen alphabetisch anzuzeigen.',
},
},
},
],
}

View file

@ -196,6 +196,7 @@ export function Blocks({ content }: BlocksProps) {
key={item.id}
title={item.title}
subtitle={item.subtitle}
churches={item.churches}
/>
)
}

View file

@ -1,4 +1,4 @@
import { Worship } from '@/payload-types'
import { Church, Worship } from '@/payload-types'
import { fetchWorship } from '@/fetch/worship'
import { fetchLastAnnouncements } from '@/fetch/announcement'
import { fetchLastCalendars } from '@/fetch/calendar'
@ -15,19 +15,22 @@ import styles from './massTimesBlock.module.scss'
type MassTimesBlockProps = {
title?: string | null
subtitle?: string | null
churches?: (string | Church)[] | null
}
type LocationGroup = { name: string; masses: Worship[] }
const sortWorship = (worship: Worship[]) => {
const map = new Map<string, Worship[]>()
const map = new Map<string, LocationGroup>()
worship.map((w) => {
if (typeof w.location === 'object') {
const title = w.location.name
const { id, name } = w.location
if (map.has(title)) {
map.get(title)?.push(w)
if (map.has(id)) {
map.get(id)?.masses.push(w)
} else {
map.set(title, [w])
map.set(id, { name, masses: [w] })
}
}
})
@ -38,13 +41,19 @@ const sortWorship = (worship: Worship[]) => {
export async function MassTimesBlock({
title = 'Nächste Gottesdienste',
subtitle,
churches,
}: MassTimesBlockProps) {
const fromDate = moment().isoWeekday(1).hours(0).minutes(0)
const tillDate = moment().isoWeekday(7).hours(23).minutes(59)
const churchIds = churches
?.map((c) => (typeof c === 'object' ? c.id : c))
.filter((id): id is string => !!id)
const worship = await fetchWorship({
fromDate: fromDate.toDate(),
tillDate: tillDate.toDate(),
locations: churchIds?.length ? churchIds : undefined,
})
const announcements = await fetchLastAnnouncements()
const calendars = await fetchLastCalendars()
@ -53,9 +62,15 @@ export async function MassTimesBlock({
const announcementsLinks = announcements ? perParish(announcements) : []
const calendarsLinks = calendars ? perParish(calendars) : []
const worshipPerLocation = Array.from(
sortWorship(worshipDocs).entries(),
).sort((a, b) => a[0].localeCompare(b[0]))
const grouped = sortWorship(worshipDocs)
const worshipPerLocation = churchIds?.length
? churchIds
.map((id) => [id, grouped.get(id)] as const)
.filter((entry): entry is [string, LocationGroup] => !!entry[1])
: Array.from(grouped.entries()).sort((a, b) =>
a[1].name.localeCompare(b[1].name),
)
return (
<Section paddingBottom={'medium'}>
@ -71,8 +86,8 @@ export async function MassTimesBlock({
{worshipPerLocation.map((value) => (
<MassTable
key={value[0]}
location={value[0]}
masses={value[1]}
location={value[1].name}
masses={value[1].masses}
/>
))}
</MassGrid>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,29 @@
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-19T07:51:55.082Z';
ALTER TABLE "calendar" ALTER COLUMN "date" SET DEFAULT '2026-04-19T07:51:55.418Z';
ALTER TABLE "classifieds" ALTER COLUMN "until" SET DEFAULT '2026-05-17T07:51:55.487Z';
ALTER TABLE "pages_rels" ADD COLUMN "church_id" uuid;
ALTER TABLE "_pages_v_rels" ADD COLUMN "church_id" uuid;
ALTER TABLE "pages_rels" ADD CONSTRAINT "pages_rels_church_fk" FOREIGN KEY ("church_id") REFERENCES "public"."church"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "_pages_v_rels" ADD CONSTRAINT "_pages_v_rels_church_fk" FOREIGN KEY ("church_id") REFERENCES "public"."church"("id") ON DELETE cascade ON UPDATE no action;
CREATE INDEX "pages_rels_church_id_idx" ON "pages_rels" USING btree ("church_id");
CREATE INDEX "_pages_v_rels_church_id_idx" ON "_pages_v_rels" USING btree ("church_id");`)
}
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
await db.execute(sql`
ALTER TABLE "pages_rels" DROP CONSTRAINT "pages_rels_church_fk";
ALTER TABLE "_pages_v_rels" DROP CONSTRAINT "_pages_v_rels_church_fk";
DROP INDEX "pages_rels_church_id_idx";
DROP INDEX "_pages_v_rels_church_id_idx";
ALTER TABLE "announcement" ALTER COLUMN "date" SET DEFAULT '2026-04-19T07:28:46.077Z';
ALTER TABLE "calendar" ALTER COLUMN "date" SET DEFAULT '2026-04-19T07:28:46.368Z';
ALTER TABLE "classifieds" ALTER COLUMN "until" SET DEFAULT '2026-05-17T07:28:46.426Z';
ALTER TABLE "pages_rels" DROP COLUMN "church_id";
ALTER TABLE "_pages_v_rels" DROP COLUMN "church_id";`)
}

View file

@ -37,6 +37,7 @@ import * as migration_20260416_090954_group_image_cards from './20260416_090954_
import * as migration_20260416_115446 from './20260416_115446';
import * as migration_20260416_121451 from './20260416_121451';
import * as migration_20260417_072846 from './20260417_072846';
import * as migration_20260417_075155 from './20260417_075155';
export const migrations = [
{
@ -232,6 +233,11 @@ export const migrations = [
{
up: migration_20260417_072846.up,
down: migration_20260417_072846.down,
name: '20260417_072846'
name: '20260417_072846',
},
{
up: migration_20260417_075155.up,
down: migration_20260417_075155.down,
name: '20260417_075155'
},
];

View file

@ -621,6 +621,10 @@ export interface Page {
| {
title?: string | null;
subtitle?: string | null;
/**
* Leer lassen, um alle Kirchen alphabetisch anzuzeigen.
*/
churches?: (string | Church)[] | null;
id?: string | null;
blockName?: string | null;
blockType: 'massTimes';
@ -2120,6 +2124,7 @@ export interface PagesSelect<T extends boolean = true> {
| {
title?: T;
subtitle?: T;
churches?: T;
id?: T;
blockName?: T;
};