feature: admin menu

This commit is contained in:
Benno Tielen 2025-02-10 14:54:43 +01:00
parent 8f90781acc
commit 81614e4449
15 changed files with 169 additions and 26 deletions

View file

@ -9,6 +9,8 @@ import { HR } from '@/components/HorizontalRule/HorizontalRule'
import Image from 'next/image'
import styles from "./styles.module.scss"
import { Blocks } from '@/compositions/Blocks/Blocks'
import { isAuthenticated } from '@/utils/auth'
import { AdminMenu } from '@/components/AdminMenu/AdminMenu'
async function fetchBlog(id: string) {
const res = await fetch(`http://localhost:3000/api/blog/${id}`)
@ -21,6 +23,7 @@ export default async function BlogPage({ params }: { params: Promise<{id: string
const id = (await params).id;
const data = await fetchBlog(id) as Blog;
const url = typeof data.photo === 'object' && data.photo?.sizes?.banner?.url;
const authenticated = await isAuthenticated();
if(!data) {
notFound();
@ -51,6 +54,11 @@ export default async function BlogPage({ params }: { params: Promise<{id: string
</Section>
<Blocks content={data.content} />
<AdminMenu
collection={"worship"}
id={id}
isAuthenticated={authenticated}
/>
</>
)

View file

@ -6,6 +6,8 @@ import { P } from '@/components/Text/Paragraph'
import { PageHeader } from '@/compositions/PageHeader/PageHeader'
import { NextPrevButtons } from '@/components/NextPrevButtons/NextPrevButtons'
import { notFound } from 'next/navigation'
import { isAuthenticated } from '@/utils/auth'
import { AdminMenu } from '@/components/AdminMenu/AdminMenu'
const months: Record<string, string> = {
"01": "Januar",
@ -105,6 +107,8 @@ export default async function PrayerIntentionPage({ params }: { params: Promise<
notFound();
}
const authenticated = await isAuthenticated()
return (
<>
<PageHeader
@ -138,6 +142,11 @@ export default async function PrayerIntentionPage({ params }: { params: Promise<
}}
/>
</Section>
<AdminMenu
collection={"popePrayerIntentions"}
id={prayer.id}
isAuthenticated={authenticated}
/>
</>
)

View file

@ -6,6 +6,8 @@ import { fetchParish } from '@/fetch/parish'
import { fetchLastAnnouncement } from '@/fetch/announcement'
import { transformGallery } from '@/utils/dto/gallery'
import { fetchLastCalendar } from '@/fetch/calendar'
import { isAuthenticated } from '@/utils/auth'
import { AdminMenu } from '@/components/AdminMenu/AdminMenu'
export default async function ParishPage ({ params }: { params: Promise<{slug: string}>}) {
@ -32,20 +34,29 @@ export default async function ParishPage ({ params }: { params: Promise<{slug: s
const worship = await fetchWorship({ locations: churchIds })
const announcement = await fetchLastAnnouncement(id);
const calendar = await fetchLastCalendar(id);
const authenticated = await isAuthenticated();
return (
<Parish
title={name}
slug={slug}
image={typeof photo === "string" ? photo : photo.url || "notfound"}
description={description}
history={history}
contactPersons={contactPersons || []}
contact={contact}
events={events?.docs || []}
worship={worship?.docs || []}
announcement={announcement && typeof announcement.document === "object" ? announcement.document.url || undefined : undefined}
calendar={calendar && typeof calendar.document === "object" ? calendar.document.url || undefined : undefined}
gallery={gallery ? transformGallery(gallery) : undefined}
/>
<>
<Parish
title={name}
slug={slug}
image={typeof photo === "string" ? photo : photo.url || "notfound"}
description={description}
history={history}
contactPersons={contactPersons || []}
contact={contact}
events={events?.docs || []}
worship={worship?.docs || []}
announcement={announcement && typeof announcement.document === "object" ? announcement.document.url || undefined : undefined}
calendar={calendar && typeof calendar.document === "object" ? calendar.document.url || undefined : undefined}
gallery={gallery ? transformGallery(gallery) : undefined}
/>
<AdminMenu
collection={"parish"}
id={id}
isAuthenticated={authenticated}
/>
</>
)
}

View file

@ -1,6 +1,8 @@
import { notFound } from 'next/navigation'
import { Worship as WorshipType } from '@/payload-types'
import { Worship } from '@/pageComponents/Worship/Worship'
import { isAuthenticated } from '@/utils/auth'
import { AdminMenu } from '@/components/AdminMenu/AdminMenu'
export default async function WorshipPage({ params }: { params: Promise<{id: string}>}) {
@ -9,10 +11,20 @@ export default async function WorshipPage({ params }: { params: Promise<{id: str
if (!res.ok) {
notFound()
}
const authenticated = await isAuthenticated();
const worship = await res.json() as WorshipType;
return (
<Worship worship={worship} />
<>
<Worship
worship={worship}
/>
<AdminMenu
collection={"worship"}
id={worship.id}
isAuthenticated={authenticated}
/>
</>
)
}

View file

@ -9,6 +9,8 @@ import { EventRow } from '@/components/EventRow/EventRow'
import Error from '@/pages/_error'
import { fetchWorship } from '@/fetch/worship'
import { tranformWorship } from '@/utils/dto/worship'
import { AdminMenu } from '@/components/AdminMenu/AdminMenu'
import { isAuthenticated } from '@/utils/auth'
export default async function WorshipPage({searchParams}: {
@ -20,6 +22,7 @@ export default async function WorshipPage({searchParams}: {
if (!week) {
week = weekNumber(moment());
}
const authenticated = await isAuthenticated();
const fromDate = moment(week, true);
@ -76,6 +79,10 @@ export default async function WorshipPage({searchParams}: {
}
</Container>
</Section>
<AdminMenu
collection={"worship"}
isAuthenticated={authenticated}
/>
{events.length > 0 &&
<Section padding={"small"}>

View file

@ -15,6 +15,8 @@ import { Row } from '@/components/Flex/Row'
import { RawHTML } from '@/components/RawHTML/RawHTML'
import { Blocks } from '@/compositions/Blocks/Blocks'
import { getPhoto } from '@/utils/dto/gallery'
import { isAuthenticated } from '@/utils/auth'
import { AdminMenu } from '@/components/AdminMenu/AdminMenu'
export default async function GroupPage({ params }: { params: Promise<{slug: string}>}) {
@ -29,6 +31,7 @@ export default async function GroupPage({ params }: { params: Promise<{slug: str
const media = getPhoto("tablet", photo)
const events = await fetchEvents({groupId: id})
const authenticated = await isAuthenticated();
return (
<>
@ -90,6 +93,12 @@ export default async function GroupPage({ params }: { params: Promise<{slug: str
{ content && content.length > 0 &&
<Blocks content={content} />
}
<AdminMenu
collection={"group"}
id={id}
isAuthenticated={authenticated}
/>
</>
)
}

View file

@ -60,7 +60,7 @@ export default function RootLayout({
items: [
{
title: "Kathoccino",
description: "Brunchgruppe für Jungerwachsene",
description: "Brunchgruppe für junge Erwachsene",
href: "/gruppe/kathocchino"
},
{
@ -127,7 +127,7 @@ export default function RootLayout({
},
groups: [
{
title: "Sakramenten",
title: "Sakramente",
items: [
{
title: "Taufe",
@ -146,7 +146,7 @@ export default function RootLayout({
},
{
title: "Ehe",
description: "Bund in Liebe, Treue",
description: "Bund in Liebe und Treue",
href: "/sakramente/ehe"
},
{
@ -196,7 +196,7 @@ export default function RootLayout({
items: [
{
title: "Alphakurs",
description: "Freude am glauben entdecken",
description: "Freude am Glauben entdecken",
href: "/gruppe/alphakurs"
},
{
@ -228,11 +228,11 @@ export default function RootLayout({
{
href: '/gruppe/waermestube',
title: 'Wärmestube',
description: 'Kälteschutz für Bedurftigen',
description: 'Kälteschutz für Bedurftige',
},
{
href: '/gruppe/essen-ist-fertig',
title: 'Essen ist Fertig',
title: 'Essen ist fertig',
description: 'Essensausgabe Neukölln',
},
{
@ -252,8 +252,8 @@ export default function RootLayout({
},
{
href: '/gruppe/die-unterstuetzer',
title: 'Anpacken & gutes tun',
description: 'Kinderbertreuung, Hilfe bei.., Kirchenreinigung',
title: 'Anpacken & Gutes tun',
description: 'Hilfe bei Kirchenreinigung und anderem',
},
]
}

View file

@ -3,6 +3,8 @@ import { Event } from '@/payload-types'
import { EventPage } from '@/pageComponents/Event/Event'
import { stringify } from 'qs-esm'
import { getPhoto } from '@/utils/dto/gallery'
import { cookies } from 'next/headers'
import { isAuthenticated } from '@/utils/auth'
export default async function Page({ params }: { params: Promise<{id: string}>}) {
@ -33,12 +35,15 @@ export default async function Page({ params }: { params: Promise<{id: string}>})
notFound()
}
const authenticated = await isAuthenticated();
const event = await res.json() as Event;
const group = Array.isArray(event.group) && event.group.length > 0 && typeof event.group[0] == "object" ? event.group[0].slug : undefined;
const photo = getPhoto("tablet", event.photo);
return (
<EventPage
id={event.id}
title={event.title}
date={event.date}
createdAt={event.createdAt}
@ -52,6 +57,7 @@ export default async function Page({ params }: { params: Promise<{id: string}>})
rsvpLink={event.rsvpLink || undefined}
flyer={typeof event.flyer === 'object' ? event.flyer || undefined : undefined}
photo={photo}
isAuthenticated={authenticated}
/>
)
}

View file

@ -13,12 +13,15 @@ import { Row } from '@/components/Flex/Row'
import { Col } from '@/components/Flex/Col'
import { highlightLink } from '@/utils/dto/highlight'
import Error from '@/pages/_error'
import { AdminMenu } from '@/components/AdminMenu/AdminMenu'
import { isAuthenticated } from '@/utils/auth'
export default async function EventsPage({searchParams}: {
searchParams: Promise<{ week: string | undefined }>
}) {
const authenticated = await isAuthenticated();
const query = await searchParams;
const limit = 100;
let week = query.week;
@ -116,6 +119,10 @@ export default async function EventsPage({searchParams}: {
</Row>
</Container>
</Section>
<AdminMenu
collection={"event"}
isAuthenticated={authenticated}
/>
{/*prevents bots indexing till infinity*/}
{ events.length > 0 &&

View file

@ -0,0 +1,26 @@
import styles from "./styles.module.scss"
type AdminEditButtonProps = {
collection: string,
id?: string,
isAuthenticated: boolean,
}
export const AdminMenu = ({ collection, id, isAuthenticated}: AdminEditButtonProps) => {
if(!isAuthenticated)
return null;
return (
<div className={styles.menu}>
<a href={`/admin`}>Admin</a>
<a href={`/admin/collections/${collection}/create`}>Neu erstellen</a>
{id &&
<a href={`/admin/collections/${collection}/${id}`}>Bearbeiten</a>
}
<a href={`/admin/logout`}>Abmelden</a>
</div>
)
}

View file

@ -0,0 +1,19 @@
.menu {
position: fixed;
bottom: 0;
width: 100%;
box-sizing: border-box;
left: 0;
background-color: rgba(238, 238, 238, 0.60);
backdrop-filter: blur(8px);
display: flex;
gap: 20px;
justify-content: flex-end;
padding-right: 20px;
}
.menu a {
padding: 10px 0;
font-size: 14px;
color: #23362c;
}

View file

@ -31,7 +31,7 @@ export const Footer = () => {
</p>
<ul className={styles.list}>
<li><Link href={"/kontakt"}>Kontakt</Link></li>
<li><Link href={"/gottesdienst"}>Gottesdiensten</Link></li>
<li><Link href={"/gottesdienst"}>Gottesdienste</Link></li>
<li><Link href={"/datenschutz"}>Datenschutz</Link></li>
<li><Link href={"/schutzkonzept"}>Schutzkonzept</Link></li>
<li><Link href={"/impressum"}>Impressum</Link></li>

View file

@ -11,6 +11,7 @@ export default meta
export const Default: Story = {
args: {
id: "testid",
title: "Sing & Pray",
date: "2024-12-02T09:21:19Z",
createdAt: "2024-12-02T09:21:19Z",

View file

@ -15,8 +15,10 @@ import { StaticImageData } from 'next/image'
import { locationString } from '@/utils/dto/location'
import { ContactPerson2 } from '@/components/ContactPerson2/ContactPerson2'
import { getPhoto } from '@/utils/dto/gallery'
import { AdminMenu } from '@/components/AdminMenu/AdminMenu'
type EventProps = {
id: string,
title: string,
date: string,
createdAt: string,
@ -29,11 +31,13 @@ type EventProps = {
group?: string,
flyer?: Document,
photo?: StaticImageData,
rsvpLink?: string
rsvpLink?: string,
isAuthenticated: boolean
}
export function EventPage(
{
id,
title,
date,
createdAt,
@ -46,7 +50,8 @@ export function EventPage(
flyer,
group,
photo,
rsvpLink
rsvpLink,
isAuthenticated
}: EventProps
) {
const published = useDate(createdAt)
@ -165,6 +170,11 @@ export function EventPage(
</Section>
<Section/>
<AdminMenu
collection={"event"}
id={id}
isAuthenticated={isAuthenticated}
/>
</>
)
}

18
src/utils/auth.ts Normal file
View file

@ -0,0 +1,18 @@
import { cookies } from 'next/headers'
/**
* Check if the current user is (trying to be) authenticated by
* checking if the `payload-token` is present.
*
* Warning: DO NOT USE THIS FUNCTION TO SECURE PARTS OF THE APPLICATION
*/
export const isAuthenticated = async (): Promise<boolean> => {
const cookieStore = await cookies();
const token = cookieStore.get("payload-token");
if(typeof token === "undefined") {
return false;
}
return true;
}