feature: parish magazine

This commit is contained in:
Benno Tielen 2025-08-27 12:23:46 +02:00
parent 4c7825ce4e
commit f85c50757d
9 changed files with 6978 additions and 12 deletions

View file

@ -0,0 +1,47 @@
import { CollectionConfig } from 'payload'
import { isAdminOrEmployee } from '@/collections/access/admin'
export const Magazine: CollectionConfig = {
slug: 'magazine',
labels: {
singular: {
de: 'Nordlicht Ausgabe'
},
plural: {
de: 'Nordlicht Ausgaben'
}
},
fields: [
{
name: 'cover',
type: 'upload',
relationTo: 'media',
label: {
de: 'Titelblatt'
},
required: true
},
{
name: 'document',
type: 'upload',
relationTo: 'documents',
label: {
de: 'PDF-dokument'
},
required: true,
},
{
name: 'date',
type: 'date',
label: {
de: 'Datum'
},
required: true
}
],
access: {
read: () => true,
update: isAdminOrEmployee(),
delete: isAdminOrEmployee()
},
}

View file

@ -6,9 +6,9 @@ import { Title } from '@/components/Title/Title'
import { NewsletterItem } from '@/compositions/PublicationAndNewsletter/NewsletterItem'
import styles from "./styles.module.scss"
import Image from 'next/image'
import nordlicht from "./nordlicht.png"
import envelope from "./envelope.svg"
import Sandbox from '@nyariv/sandboxjs'
import { fetchLastMagazine } from '@/fetch/magazine'
type NewsletterData = {
guid: string,
@ -40,25 +40,33 @@ export const PublicationAndNewsletter = async () => {
console.error("Could not fetch newsletters. Please check PublicationAndNewsletter component")
}
const magazine = await fetchLastMagazine();
const magazine_url = magazine && typeof magazine.document === "object" ? magazine.document.url || undefined : undefined;
const magazine_cover = magazine && typeof magazine.cover === "object" ? magazine.cover : undefined;
return (
<Section backgroundColor={"off-white"}>
<Container>
<Row alignItems={"center"}>
<Col>
<a href={"https://storage.googleapis.com/dreikoenige/documents/nordlicht78-web-1.pdf"} target={"_blank"}>
<Image
className={styles.image}
src={nordlicht}
alt={"Pfarreimagazin Ausgabe 67"}
/>
</a>
{magazine_url && magazine_cover && magazine_cover.url &&
<a href={magazine_url} target={'_blank'}>
<Image
className={styles.image}
src={magazine_cover.url}
width={magazine_cover.width || 500}
height={magazine_cover.height || 600}
alt={'Pfarreimagazin Ausgabe'}
/>
</a>
}
</Col>
<Col>
<div className={styles.titleContainer}>
<Image src={envelope} alt={"Newsletter icon"}/>
<Image src={envelope} alt={'Newsletter icon'} />
<Title
title={"Newsletter aus dem Bistum"}
size={"md"}
title={'Newsletter aus dem Bistum'}
size={'md'}
/>
</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 743 KiB

27
src/fetch/magazine.ts Normal file
View file

@ -0,0 +1,27 @@
import { stringify } from 'qs-esm'
import { PaginatedDocs } from 'payload'
import { Magazine } from '@/payload-types'
/**
* Asynchronously fetches the last magazine entry from the specified API endpoint.
*
* This function sends a request to the specified API endpoint to fetch the last magazine based on the given sorting criteria.
*
* @returns {Promise<Magazine | undefined>} A Promise that resolves to the last fetched magazine entry if successful, or undefined if an error occurs.
*/
export const fetchLastMagazine = async (): Promise<Magazine | undefined> => {
const stringifiedQuery = stringify(
{
sort: "-date",
limit: 1,
},
{ addQueryPrefix: true },
)
const response = await fetch(`http://localhost:3000/api/magazine${stringifiedQuery}`)
if (!response.ok) return undefined
const announcements = await response.json() as PaginatedDocs<Magazine>
return announcements.docs[0]
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,54 @@
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
await db.execute(sql`
CREATE TABLE IF NOT EXISTS "magazine" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"cover_id" uuid NOT NULL,
"document_id" uuid NOT NULL,
"date" timestamp(3) with time zone NOT NULL,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
);
ALTER TABLE "announcement" ALTER COLUMN "date" SET DEFAULT '2025-08-31T09:35:59.090Z';
ALTER TABLE "calendar" ALTER COLUMN "date" SET DEFAULT '2025-08-31T09:35:59.222Z';
ALTER TABLE "classifieds" ALTER COLUMN "until" SET DEFAULT '2025-09-26T09:35:59.313Z';
ALTER TABLE "payload_locked_documents_rels" ADD COLUMN "magazine_id" uuid;
DO $$ BEGIN
ALTER TABLE "magazine" ADD CONSTRAINT "magazine_cover_id_media_id_fk" FOREIGN KEY ("cover_id") REFERENCES "public"."media"("id") ON DELETE set null ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
DO $$ BEGIN
ALTER TABLE "magazine" ADD CONSTRAINT "magazine_document_id_documents_id_fk" FOREIGN KEY ("document_id") REFERENCES "public"."documents"("id") ON DELETE set null ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
CREATE INDEX IF NOT EXISTS "magazine_cover_idx" ON "magazine" USING btree ("cover_id");
CREATE INDEX IF NOT EXISTS "magazine_document_idx" ON "magazine" USING btree ("document_id");
CREATE INDEX IF NOT EXISTS "magazine_updated_at_idx" ON "magazine" USING btree ("updated_at");
CREATE INDEX IF NOT EXISTS "magazine_created_at_idx" ON "magazine" USING btree ("created_at");
DO $$ BEGIN
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_magazine_fk" FOREIGN KEY ("magazine_id") REFERENCES "public"."magazine"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
CREATE INDEX IF NOT EXISTS "payload_locked_documents_rels_magazine_id_idx" ON "payload_locked_documents_rels" USING btree ("magazine_id");`)
}
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
await db.execute(sql`
ALTER TABLE "magazine" DISABLE ROW LEVEL SECURITY;
DROP TABLE "magazine" CASCADE;
ALTER TABLE "payload_locked_documents_rels" DROP CONSTRAINT "payload_locked_documents_rels_magazine_fk";
DROP INDEX IF EXISTS "payload_locked_documents_rels_magazine_id_idx";
ALTER TABLE "announcement" ALTER COLUMN "date" SET DEFAULT '2025-08-24T14:31:15.010Z';
ALTER TABLE "calendar" ALTER COLUMN "date" SET DEFAULT '2025-08-24T14:31:15.092Z';
ALTER TABLE "classifieds" ALTER COLUMN "until" SET DEFAULT '2025-09-17T14:31:15.112Z';
ALTER TABLE "payload_locked_documents_rels" DROP COLUMN IF EXISTS "magazine_id";`)
}

View file

@ -8,6 +8,7 @@ import * as migration_20250319_101337_donationbox from './20250319_101337_donati
import * as migration_20250322_134918_classifieds from './20250322_134918_classifieds';
import * as migration_20250608_105124_new_blocks from './20250608_105124_new_blocks';
import * as migration_20250818_143115_menu from './20250818_143115_menu';
import * as migration_20250827_093559_magazine from './20250827_093559_magazine';
export const migrations = [
{
@ -58,6 +59,11 @@ export const migrations = [
{
up: migration_20250818_143115_menu.up,
down: migration_20250818_143115_menu.down,
name: '20250818_143115_menu'
name: '20250818_143115_menu',
},
{
up: migration_20250827_093559_magazine.up,
down: migration_20250827_093559_magazine.down,
name: '20250827_093559_magazine'
},
];

View file

@ -24,6 +24,7 @@ export interface Config {
contactPerson: ContactPerson;
locations: Location;
group: Group;
magazine: Magazine;
documents: Document;
media: Media;
users: User;
@ -46,6 +47,7 @@ export interface Config {
contactPerson: ContactPersonSelect<false> | ContactPersonSelect<true>;
locations: LocationsSelect<false> | LocationsSelect<true>;
group: GroupSelect<false> | GroupSelect<true>;
magazine: MagazineSelect<false> | MagazineSelect<true>;
documents: DocumentsSelect<false> | DocumentsSelect<true>;
media: MediaSelect<false> | MediaSelect<true>;
users: UsersSelect<false> | UsersSelect<true>;
@ -532,6 +534,18 @@ export interface Classified {
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "magazine".
*/
export interface Magazine {
id: string;
cover: string | Media;
document: string | Document;
date: string;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users".
@ -611,6 +625,10 @@ export interface PayloadLockedDocument {
relationTo: 'group';
value: string | Group;
} | null)
| ({
relationTo: 'magazine';
value: string | Magazine;
} | null)
| ({
relationTo: 'documents';
value: string | Document;
@ -961,6 +979,17 @@ export interface GroupSelect<T extends boolean = true> {
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "magazine_select".
*/
export interface MagazineSelect<T extends boolean = true> {
cover?: T;
document?: T;
date?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "documents_select".

View file

@ -37,6 +37,7 @@ import { PopesPrayerIntentions } from '@/collections/PopesPrayerIntentions'
import { LiturgicalCalendar } from '@/collections/LiturgicalCalendar'
import { Classifieds } from '@/collections/Classifieds'
import { MenuGlobal } from '@/globals/Menu'
import { Magazine } from '@/collections/Magazine'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
@ -89,6 +90,7 @@ export default buildConfig({
ContactPerson,
Locations,
Groups,
Magazine,
Documents,
Media,
Users,