add: Layout, working permissions for papers
This commit is contained in:
@@ -1,29 +1,40 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
|
||||||
export default function DiplomarbeitSearch() {
|
export default function DiplomarbeitSearch() {
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
const [results, setResults] = useState<string[]>([]);
|
const [results, setResults] = useState<[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timeoutId = setTimeout(async () => {
|
const timeoutId = setTimeout(async () => {
|
||||||
const response = await fetch(`/api/diplomarbeiten?search=${search}`);
|
const response = await fetch(`/api/diplomarbeiten?search=${search}`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
setResults(data.titles || []);
|
setResults(data.docs || []);
|
||||||
}, 500); // 500ms cooldown period
|
}, 500); // 500ms cooldown period
|
||||||
|
|
||||||
return () => clearTimeout(timeoutId);
|
return () => clearTimeout(timeoutId);
|
||||||
}, [search]);
|
}, [search]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full flex flex-col gap-16">
|
||||||
<Input type="text" value={search} placeholder="Suche" onChange={(e) => setSearch(e.target.value)} className="w-full" />
|
<div className="w-1/3 flex flex-col items-center mx-auto">
|
||||||
{results.map((title, index) => (
|
<h2 className="text-5xl w-auto pb-2">Diplomarbeitensuche</h2>
|
||||||
<div key={index}>
|
<Input type="text" value={search} placeholder="Suche" onChange={(e) => setSearch(e.target.value)} className="w-full" />
|
||||||
<h2>{title}</h2>
|
</div>
|
||||||
</div>
|
|
||||||
))}
|
<div className="flex flex-col justify-center mx-auto w-2/3">
|
||||||
|
{results.map((result, index) => (
|
||||||
|
<Link key={index} href={"/diplomarbeit/" + result.id}>
|
||||||
|
<div key={index}>
|
||||||
|
<h2 className="text-3xl">{result.title}</h2>
|
||||||
|
<h3 className="text-xl">{result.year}</h3>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,27 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import Image from 'next/image'
|
||||||
|
import natterLogoBlack from "@/app/(frontend)/natter-logo.svg"
|
||||||
|
import natterLogoWhite from "@/app/(frontend)/natter-logo-white.svg"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default function Footer() {
|
export default function Footer() {
|
||||||
|
const currentYear = new Date().getFullYear();
|
||||||
return (
|
return (
|
||||||
<footer className="w-full h-20 bg-htl-red flex flex-row items-center p-2 gap-4 shadow-lg justify-between">
|
<footer className="w-full h-20 bg-htl-red flex flex-row items-center p-2 gap-4 shadow-lg justify-between">
|
||||||
<div className="w-auto flex items-center flex-row gap-4">
|
<div className="w-auto flex items-center flex-row gap-4">
|
||||||
<p className="text-6xl text-white">HTL Dornbirn</p>
|
<p className="text-xl text-white">Made with {"<3"} by</p>
|
||||||
|
<a href="https://natter.li" target="_blank">
|
||||||
|
<Image
|
||||||
|
className="h-6 w-auto"
|
||||||
|
src={natterLogoWhite}
|
||||||
|
alt={"Natter Logo"}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-auto">
|
<div className="w-auto">
|
||||||
<p className="text-white">© 2021 HTL Dornbirn</p>
|
<p className="text-white">© {currentYear} HTL Dornbirn</p>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
35
src/app/(frontend)/diplomarbeit/[id]/page.tsx
Normal file
35
src/app/(frontend)/diplomarbeit/[id]/page.tsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { getPayload } from 'payload';
|
||||||
|
import config from '@payload-config';
|
||||||
|
import Link from "next/link"
|
||||||
|
|
||||||
|
export default async function Page({
|
||||||
|
params,
|
||||||
|
}: {
|
||||||
|
params: Promise<{ id: number }>
|
||||||
|
}) {
|
||||||
|
const { id } = await params
|
||||||
|
|
||||||
|
|
||||||
|
const payload = await getPayload({ config });
|
||||||
|
|
||||||
|
|
||||||
|
const result = await payload.findByID({
|
||||||
|
collection: 'papers', // required
|
||||||
|
id: id, // required
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-2/3 mx-auto">
|
||||||
|
<Link href="/">{"<-"} Back</Link>
|
||||||
|
<h4 className="text-lg text-gray-600">Diplom- und Abschlussarbeiten ({result.year})</h4>
|
||||||
|
<h1 className="text-4xl font-semibold">{result.title}</h1>
|
||||||
|
<h2 className="text-3xl">Zielsetzung</h2>
|
||||||
|
<p className="text-lg">{result.goal}</p>
|
||||||
|
<h2 className="text-3xl">Problemstellung</h2>
|
||||||
|
<p className="text-lg">{result.issue}</p>
|
||||||
|
<h2 className="text-3xl">Ergebnisse</h2>
|
||||||
|
<p className="text-lg">{result.result}</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -4,8 +4,8 @@ import Header from "@/app/(frontend)/_components/Header";
|
|||||||
import Footer from "@/app/(frontend)/_components/Footer";
|
import Footer from "@/app/(frontend)/_components/Footer";
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
description: "A blank template using Payload in a Next.js app.",
|
description: "Diplomarbeiten - HTL Dornbirn",
|
||||||
title: "Payload Blank Template",
|
title: "Diplomarbeiten - HTL Dornbirn",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function RootLayout(props: { children: React.ReactNode }) {
|
export default async function RootLayout(props: { children: React.ReactNode }) {
|
||||||
@@ -13,10 +13,12 @@ export default async function RootLayout(props: { children: React.ReactNode }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang="de-AT">
|
<html lang="de-AT">
|
||||||
<body className="w-full min-h-full">
|
<body>
|
||||||
|
<div className="flex flex-col min-h-screen">
|
||||||
<Header />
|
<Header />
|
||||||
<main>{children}</main>
|
<main className="grow">{children}</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
24
src/app/(frontend)/natter-logo-black.svg
Normal file
24
src/app/(frontend)/natter-logo-black.svg
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 479.47 75">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.cls-1 {
|
||||||
|
fill: #231f20;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<g id="Layer_1-2" data-name="Layer_1">
|
||||||
|
<g>
|
||||||
|
<polygon class="cls-1" points="0 75 37.5 75 37.5 37.5 0 0 0 75"/>
|
||||||
|
<polygon class="cls-1" points="37.5 0 37.5 37.5 75 75 75 0 37.5 0"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="cls-1" d="M87.46,73.48V1.38h9.99l44.08,59.02-1.65.93c-.48-2.81-.88-6.08-1.18-9.79s-.53-7.79-.67-12.26c-.14-4.46-.21-9.2-.21-14.21V1.38h11.12v72.1h-10.2l-43.36-56.86,1.13-1.44c.48,5.22.86,9.49,1.13,12.82.27,3.33.48,5.99.62,7.98.14,1.99.22,3.55.26,4.69.03,1.13.05,2.08.05,2.83v29.97h-11.12Z"/>
|
||||||
|
<path class="cls-1" d="M161.62,73.48L189.53,1.38h11.33l27.71,72.1h-11.85l-16.48-43.47c-.21-.48-.6-1.65-1.18-3.5-.58-1.85-1.25-3.91-2.01-6.18-.76-2.27-1.44-4.34-2.06-6.23-.62-1.89-1.03-3.11-1.24-3.66l2.37-.1c-.41,1.17-.91,2.64-1.49,4.43-.58,1.79-1.2,3.67-1.85,5.67-.65,1.99-1.29,3.86-1.91,5.61-.62,1.75-1.13,3.21-1.54,4.38l-16.38,43.05h-11.33ZM173.98,55.56l4.12-10.71h32.75l4.74,10.71h-41.61Z"/>
|
||||||
|
<path class="cls-1" d="M254.93,73.48V12.09h-20.19V1.38h52.12v10.71h-20.81v61.39h-11.12Z"/>
|
||||||
|
<path class="cls-1" d="M314.26,73.48V12.09h-20.19V1.38h52.12v10.71h-20.81v61.39h-11.12Z"/>
|
||||||
|
<path class="cls-1" d="M359.89,73.48V1.38h45.42v10.71h-34.3v50.68h34.3v10.71h-45.42ZM364.94,41.86v-10.71h34.71v10.71h-34.71Z"/>
|
||||||
|
<path class="cls-1" d="M423.13,73.48V1.38h30.69c4.12,0,7.91,1.01,11.38,3.04,3.47,2.03,6.23,4.77,8.29,8.24,2.06,3.47,3.09,7.36,3.09,11.69,0,3.91-1.03,7.55-3.09,10.92-2.06,3.37-4.81,6.06-8.24,8.08-3.43,2.03-7.25,3.04-11.43,3.04h-19.57v27.09h-11.12ZM434.26,35.68h20.6c1.99,0,3.78-.5,5.36-1.49,1.58-.99,2.82-2.37,3.71-4.12.89-1.75,1.34-3.69,1.34-5.82,0-2.4-.57-4.51-1.7-6.33-1.13-1.82-2.71-3.25-4.74-4.27-2.03-1.03-4.31-1.54-6.85-1.54h-17.72v23.59ZM465.98,73.48l-18.33-32.55,11.43-2.58,20.39,35.23-13.49-.1Z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.9 KiB |
24
src/app/(frontend)/natter-logo-white.svg
Normal file
24
src/app/(frontend)/natter-logo-white.svg
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 479.47 75">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.cls-1 {
|
||||||
|
fill: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<g id="Layer_1-2" data-name="Layer_1">
|
||||||
|
<g>
|
||||||
|
<polygon class="cls-1" points="0 75 37.5 75 37.5 37.5 0 0 0 75"/>
|
||||||
|
<polygon class="cls-1" points="37.5 0 37.5 37.5 75 75 75 0 37.5 0"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="cls-1" d="M87.46,74.5V2.4h9.99l44.08,59.02-1.65.93c-.48-2.81-.88-6.08-1.18-9.79s-.53-7.79-.67-12.26c-.14-4.46-.21-9.2-.21-14.21V2.4h11.12v72.1h-10.2l-43.36-56.86,1.13-1.44c.48,5.22.86,9.49,1.13,12.82.27,3.33.48,5.99.62,7.98.14,1.99.22,3.55.26,4.69.03,1.13.05,2.08.05,2.83v29.97h-11.12Z"/>
|
||||||
|
<path class="cls-1" d="M161.62,74.5L189.53,2.4h11.33l27.71,72.1h-11.85l-16.48-43.47c-.21-.48-.6-1.65-1.18-3.5-.58-1.85-1.25-3.91-2.01-6.18-.76-2.27-1.44-4.34-2.06-6.23-.62-1.89-1.03-3.11-1.24-3.66l2.37-.1c-.41,1.17-.91,2.64-1.49,4.43-.58,1.79-1.2,3.67-1.85,5.67-.65,1.99-1.29,3.86-1.91,5.61-.62,1.75-1.13,3.21-1.54,4.38l-16.38,43.05h-11.33ZM173.98,56.58l4.12-10.71h32.75l4.74,10.71h-41.61Z"/>
|
||||||
|
<path class="cls-1" d="M254.93,74.5V13.11h-20.19V2.4h52.12v10.71h-20.81v61.39h-11.12Z"/>
|
||||||
|
<path class="cls-1" d="M314.26,74.5V13.11h-20.19V2.4h52.12v10.71h-20.81v61.39h-11.12Z"/>
|
||||||
|
<path class="cls-1" d="M359.89,74.5V2.4h45.42v10.71h-34.3v50.68h34.3v10.71h-45.42ZM364.94,42.88v-10.71h34.71v10.71h-34.71Z"/>
|
||||||
|
<path class="cls-1" d="M423.13,74.5V2.4h30.69c4.12,0,7.91,1.01,11.38,3.04,3.47,2.03,6.23,4.77,8.29,8.24,2.06,3.47,3.09,7.36,3.09,11.69,0,3.91-1.03,7.55-3.09,10.92-2.06,3.37-4.81,6.06-8.24,8.08-3.43,2.03-7.25,3.04-11.43,3.04h-19.57v27.09h-11.12ZM434.26,36.7h20.6c1.99,0,3.78-.5,5.36-1.49,1.58-.99,2.82-2.37,3.71-4.12.89-1.75,1.34-3.69,1.34-5.82,0-2.4-.57-4.51-1.7-6.33-1.13-1.82-2.71-3.25-4.74-4.27-2.03-1.03-4.31-1.54-6.85-1.54h-17.72v23.59ZM465.98,74.5l-18.33-32.55,11.43-2.58,20.39,35.23-13.49-.1Z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -1,45 +1,22 @@
|
|||||||
import { auth } from "@/auth";
|
|
||||||
import { getPayloadSession } from "payload-authjs";
|
|
||||||
import { getPayload } from 'payload'
|
|
||||||
import config from '@payload-config'
|
|
||||||
import { Input } from '@/components/ui/input'
|
|
||||||
import DiplomarbeitSearch from '@/app/(frontend)/_components/DiplomarbeitSearch'
|
import DiplomarbeitSearch from '@/app/(frontend)/_components/DiplomarbeitSearch'
|
||||||
|
|
||||||
const payload = await getPayload({ config })
|
|
||||||
|
|
||||||
|
|
||||||
const Page = async () => {
|
const Page = async () => {
|
||||||
const authjsSession = await auth();
|
|
||||||
const payloadSession = await getPayloadSession();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const media = await payload.find({
|
|
||||||
collection: 'media',
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
<div className="w-full flex justify-center pt-4">
|
|
||||||
<div className="w-1/3 flex flex-col items-center">
|
|
||||||
<h2 className="text-5xl w-auto ">Alle Diplomarbeiten</h2>
|
|
||||||
<DiplomarbeitSearch />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{media.docs.map((med) => (
|
|
||||||
<div key={med.id}>
|
<DiplomarbeitSearch />
|
||||||
<h2>{med.url}</h2>
|
|
||||||
<p>{med.alt}</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<br />
|
|
||||||
<h3>Auth.js Session</h3>
|
|
||||||
<pre>{JSON.stringify(authjsSession, null, 2)}</pre>
|
|
||||||
<br />
|
|
||||||
<h3>Payload CMS Session</h3>
|
|
||||||
<pre>{JSON.stringify(payloadSession, null, 2)}</pre>
|
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ export async function GET(req: NextRequest) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const docs = response.docs.map((doc) => ({ title: doc.title, year: doc.year, id: doc.id }));
|
||||||
const titles = response.docs.map((doc) => doc.title);
|
return NextResponse.json({ docs });
|
||||||
return NextResponse.json({ titles });
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,15 +23,7 @@ export const Media: CollectionConfig = {
|
|||||||
type: 'text',
|
type: 'text',
|
||||||
//required: true,
|
//required: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'caption',
|
|
||||||
type: 'richText',
|
|
||||||
editor: lexicalEditor({
|
|
||||||
features: ({ rootFeatures }) => {
|
|
||||||
return [...rootFeatures, FixedToolbarFeature(), InlineToolbarFeature()]
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
upload: {
|
upload: {
|
||||||
// Upload to the public/media directory in Next.js making them publicly accessible even outside of Payload
|
// Upload to the public/media directory in Next.js making them publicly accessible even outside of Payload
|
||||||
|
|||||||
@@ -1,133 +1,154 @@
|
|||||||
import type { CollectionConfig } from 'payload'
|
import type { CollectionConfig } from "payload";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const Papers: CollectionConfig = {
|
export const Papers: CollectionConfig = {
|
||||||
slug: 'papers',
|
slug: "papers",
|
||||||
labels: {
|
labels: {
|
||||||
singular: 'Diplomarbeit',
|
singular: "Diplomarbeit",
|
||||||
plural: 'Diplomarbeiten',
|
plural: "Diplomarbeiten",
|
||||||
},
|
},
|
||||||
access: {
|
access: {
|
||||||
create: ({ req: { user } }) => {
|
create: ({ req: { user } }) => Boolean(user?.type === "admin"),
|
||||||
return Boolean(user?.type == "admin") // <-- Check if the user is authenticated
|
delete: ({ req: { user } }) => Boolean(user?.type === "admin"),
|
||||||
},
|
update: async ({ req: { user, payload }, id }) => {
|
||||||
delete: ({ req: { user } }) => {
|
if (user?.type === "admin") return true;
|
||||||
return Boolean(user?.type == "admin") // <-- Check if the user is authenticated
|
|
||||||
},
|
|
||||||
update: async ({ req: { user }, id, findByID }) => {
|
|
||||||
if (user?.type == "admin") return true; // Admins can update any paper
|
|
||||||
|
|
||||||
const paper = await findByID({ collection: 'papers', id });
|
if (!user || !id) return false; // Explicitly handle missing ID
|
||||||
return paper.authors.some(author => author.user === user.id); // Check if the user is an author
|
|
||||||
|
const paper = await payload.findByID({
|
||||||
|
collection: 'papers',
|
||||||
|
id,
|
||||||
|
depth: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!paper) return false;
|
||||||
|
|
||||||
|
return paper.authors.some((author: any) => author.user.id === user.id);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
admin: {
|
admin: {
|
||||||
useAsTitle: 'title',
|
useAsTitle: "title",
|
||||||
},
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'title',
|
name: "title",
|
||||||
type: 'text',
|
type: "text",
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'issue',
|
name: "year",
|
||||||
label: 'Problemstellung',
|
type: "text",
|
||||||
type: 'textarea',
|
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'goal',
|
name: "issue",
|
||||||
label: 'Zielsetzung',
|
label: "Problemstellung",
|
||||||
type: 'textarea',
|
type: "textarea",
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'technologies',
|
name: "goal",
|
||||||
type: 'array',
|
label: "Zielsetzung",
|
||||||
|
type: "textarea",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "result",
|
||||||
|
label: "Ergebnis",
|
||||||
|
type: "textarea",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "technologies",
|
||||||
|
type: "array",
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'technology',
|
name: "technology",
|
||||||
type: 'relationship',
|
type: "relationship",
|
||||||
relationTo: 'technologies',
|
relationTo: "technologies",
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'description',
|
name: "description",
|
||||||
type: 'text',
|
type: "text",
|
||||||
required: true,
|
required: true,
|
||||||
admin: {
|
admin: {
|
||||||
placeholder: '... wurde für das Frontend verwendet',
|
placeholder: "... wurde für das Frontend verwendet",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'prototype',
|
name: "prototype",
|
||||||
type: 'group',
|
type: "group",
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'image',
|
name: "image",
|
||||||
type: 'upload',
|
type: "upload",
|
||||||
relationTo: 'media',
|
relationTo: "media",
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'description',
|
name: "description",
|
||||||
type: 'text',
|
type: "text",
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'authors',
|
name: "authors",
|
||||||
type: 'array',
|
type: "array",
|
||||||
label: 'Projektmitglieder',
|
label: "Projektmitglieder",
|
||||||
required: true,
|
required: true,
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'profilePicture',
|
name: "profilePicture",
|
||||||
type: 'upload',
|
type: "upload",
|
||||||
relationTo: 'media',
|
relationTo: "media",
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'user',
|
name: "user",
|
||||||
type: 'relationship',
|
type: "relationship",
|
||||||
relationTo: 'users',
|
relationTo: "users",
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'position',
|
name: "position",
|
||||||
type: 'select',
|
type: "select",
|
||||||
required: true,
|
required: true,
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
label: 'Projektleiter (PL)',
|
label: "Projektleiter (PL)",
|
||||||
value: 'leader',
|
value: "leader",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Projektmitarbeiter (PM)',
|
label: "Projektmitarbeiter (PM)",
|
||||||
value: 'member',
|
value: "member",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'description',
|
name: "description",
|
||||||
type: 'text',
|
type: "text",
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
],
|
],
|
||||||
validate: (authors) => {
|
validate: (authors) => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const leaders = authors.filter(author => author.position === 'leader')
|
const leaders = authors.filter(
|
||||||
|
(author) => author.position === "leader",
|
||||||
|
);
|
||||||
if (leaders.length > 1) {
|
if (leaders.length > 1) {
|
||||||
return 'Only one author can be the project leader.'
|
return "Only one author can be the project leader.";
|
||||||
}
|
}
|
||||||
return true
|
return true;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import type { CollectionConfig } from 'payload'
|
import type { CollectionConfig } from 'payload'
|
||||||
|
import path from 'path'
|
||||||
|
import { fileURLToPath } from 'url'
|
||||||
|
|
||||||
|
|
||||||
|
const filename = fileURLToPath(import.meta.url)
|
||||||
|
const dirname = path.dirname(filename)
|
||||||
|
|
||||||
export const Technologies: CollectionConfig = {
|
export const Technologies: CollectionConfig = {
|
||||||
slug: 'technologies',
|
slug: 'technologies',
|
||||||
labels: {
|
labels: {
|
||||||
@@ -11,7 +16,10 @@ export const Technologies: CollectionConfig = {
|
|||||||
useAsTitle: 'name',
|
useAsTitle: 'name',
|
||||||
},
|
},
|
||||||
access: {
|
access: {
|
||||||
|
read: () => true,
|
||||||
|
create: ({ req: { user } }) => Boolean(user),
|
||||||
|
update: ({ req: { user } }) => Boolean(user?.type === "admin"),
|
||||||
|
delete: ({ req: { user } }) => Boolean(user?.type === "admin"),
|
||||||
},
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
@@ -24,11 +32,22 @@ export const Technologies: CollectionConfig = {
|
|||||||
type: 'textarea',
|
type: 'textarea',
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'icon',
|
|
||||||
type: 'upload',
|
|
||||||
relationTo: 'media',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
|
upload: {
|
||||||
|
// Upload to the public/media directory in Next.js making them publicly accessible even outside of Payload
|
||||||
|
staticDir: path.resolve(dirname, '../../public/technology-icons'),
|
||||||
|
adminThumbnail: 'thumbnail',
|
||||||
|
focalPoint: true,
|
||||||
|
imageSizes: [
|
||||||
|
{
|
||||||
|
name: 'thumbnail',
|
||||||
|
width: 300,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'square',
|
||||||
|
width: 500,
|
||||||
|
height: 500,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,8 +124,10 @@ export interface UserAuthOperations {
|
|||||||
export interface Paper {
|
export interface Paper {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
|
year: string;
|
||||||
issue: string;
|
issue: string;
|
||||||
goal: string;
|
goal: string;
|
||||||
|
result: string;
|
||||||
technologies?:
|
technologies?:
|
||||||
| {
|
| {
|
||||||
technology: number | Technology;
|
technology: number | Technology;
|
||||||
@@ -155,9 +157,35 @@ export interface Technology {
|
|||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
icon: number | Media;
|
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
|
url?: string | null;
|
||||||
|
thumbnailURL?: string | null;
|
||||||
|
filename?: string | null;
|
||||||
|
mimeType?: string | null;
|
||||||
|
filesize?: number | null;
|
||||||
|
width?: number | null;
|
||||||
|
height?: number | null;
|
||||||
|
focalX?: number | null;
|
||||||
|
focalY?: number | null;
|
||||||
|
sizes?: {
|
||||||
|
thumbnail?: {
|
||||||
|
url?: string | null;
|
||||||
|
width?: number | null;
|
||||||
|
height?: number | null;
|
||||||
|
mimeType?: string | null;
|
||||||
|
filesize?: number | null;
|
||||||
|
filename?: string | null;
|
||||||
|
};
|
||||||
|
square?: {
|
||||||
|
url?: string | null;
|
||||||
|
width?: number | null;
|
||||||
|
height?: number | null;
|
||||||
|
mimeType?: string | null;
|
||||||
|
filesize?: number | null;
|
||||||
|
filename?: string | null;
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
@@ -166,21 +194,6 @@ export interface Technology {
|
|||||||
export interface Media {
|
export interface Media {
|
||||||
id: number;
|
id: number;
|
||||||
alt?: string | null;
|
alt?: string | null;
|
||||||
caption?: {
|
|
||||||
root: {
|
|
||||||
type: string;
|
|
||||||
children: {
|
|
||||||
type: string;
|
|
||||||
version: number;
|
|
||||||
[k: string]: unknown;
|
|
||||||
}[];
|
|
||||||
direction: ('ltr' | 'rtl') | null;
|
|
||||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
|
||||||
indent: number;
|
|
||||||
version: number;
|
|
||||||
};
|
|
||||||
[k: string]: unknown;
|
|
||||||
} | null;
|
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
@@ -344,8 +357,10 @@ export interface PayloadMigration {
|
|||||||
*/
|
*/
|
||||||
export interface PapersSelect<T extends boolean = true> {
|
export interface PapersSelect<T extends boolean = true> {
|
||||||
title?: T;
|
title?: T;
|
||||||
|
year?: T;
|
||||||
issue?: T;
|
issue?: T;
|
||||||
goal?: T;
|
goal?: T;
|
||||||
|
result?: T;
|
||||||
technologies?:
|
technologies?:
|
||||||
| T
|
| T
|
||||||
| {
|
| {
|
||||||
@@ -378,9 +393,41 @@ export interface PapersSelect<T extends boolean = true> {
|
|||||||
export interface TechnologiesSelect<T extends boolean = true> {
|
export interface TechnologiesSelect<T extends boolean = true> {
|
||||||
name?: T;
|
name?: T;
|
||||||
description?: T;
|
description?: T;
|
||||||
icon?: T;
|
|
||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
|
url?: T;
|
||||||
|
thumbnailURL?: T;
|
||||||
|
filename?: T;
|
||||||
|
mimeType?: T;
|
||||||
|
filesize?: T;
|
||||||
|
width?: T;
|
||||||
|
height?: T;
|
||||||
|
focalX?: T;
|
||||||
|
focalY?: T;
|
||||||
|
sizes?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
thumbnail?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
url?: T;
|
||||||
|
width?: T;
|
||||||
|
height?: T;
|
||||||
|
mimeType?: T;
|
||||||
|
filesize?: T;
|
||||||
|
filename?: T;
|
||||||
|
};
|
||||||
|
square?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
url?: T;
|
||||||
|
width?: T;
|
||||||
|
height?: T;
|
||||||
|
mimeType?: T;
|
||||||
|
filesize?: T;
|
||||||
|
filename?: T;
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
@@ -410,7 +457,6 @@ export interface UsersSelect<T extends boolean = true> {
|
|||||||
*/
|
*/
|
||||||
export interface MediaSelect<T extends boolean = true> {
|
export interface MediaSelect<T extends boolean = true> {
|
||||||
alt?: T;
|
alt?: T;
|
||||||
caption?: T;
|
|
||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
url?: T;
|
url?: T;
|
||||||
|
|||||||
Reference in New Issue
Block a user