basic search working
This commit is contained in:
29
src/app/(frontend)/_components/DiplomarbeitSearch.tsx
Normal file
29
src/app/(frontend)/_components/DiplomarbeitSearch.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
"use client";
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Input } from '@/components/ui/input';
|
||||
|
||||
export default function DiplomarbeitSearch() {
|
||||
const [search, setSearch] = useState('');
|
||||
const [results, setResults] = useState<string[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const timeoutId = setTimeout(async () => {
|
||||
const response = await fetch(`/api/diplomarbeiten?search=${search}`);
|
||||
const data = await response.json();
|
||||
setResults(data.titles || []);
|
||||
}, 500); // 500ms cooldown period
|
||||
|
||||
return () => clearTimeout(timeoutId);
|
||||
}, [search]);
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<Input type="text" value={search} placeholder="Suche" onChange={(e) => setSearch(e.target.value)} className="w-full" />
|
||||
{results.map((title, index) => (
|
||||
<div key={index}>
|
||||
<h2>{title}</h2>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
15
src/app/(frontend)/_components/Footer.tsx
Normal file
15
src/app/(frontend)/_components/Footer.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from 'react'
|
||||
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<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">
|
||||
<p className="text-6xl text-white">HTL Dornbirn</p>
|
||||
</div>
|
||||
<div className="w-auto">
|
||||
<p className="text-white">© 2021 HTL Dornbirn</p>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
27
src/app/(frontend)/_components/Header.tsx
Normal file
27
src/app/(frontend)/_components/Header.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import Image from 'next/image'
|
||||
import HTLDLogo from '@/app/(frontend)/htld.svg'
|
||||
import { SignOutButton } from '@/app/(frontend)/_components/SignOutButton'
|
||||
import { SignInButton } from '@/app/(frontend)/_components/SignInButton'
|
||||
import React from 'react'
|
||||
import { getPayloadSession } from 'payload-authjs'
|
||||
|
||||
|
||||
export default async function Header(){
|
||||
const session = await getPayloadSession();
|
||||
return (
|
||||
<header 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">
|
||||
<Image
|
||||
className="h-16 w-auto"
|
||||
src={HTLDLogo}
|
||||
alt={"HTL Dornbirn Logo"}
|
||||
/>
|
||||
<p className="text-6xl text-white">Diplom- und Abschlussarbeiten</p>
|
||||
</div>
|
||||
<div className="w-auto">
|
||||
{session ? <SignOutButton /> : <SignInButton />}
|
||||
</div>
|
||||
|
||||
</header>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { signIn } from "@/auth";
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
export function SignInButton() {
|
||||
return (
|
||||
@@ -8,7 +9,7 @@ export function SignInButton() {
|
||||
await signIn("github");
|
||||
}}
|
||||
>
|
||||
<button type="submit">Sign In</button>
|
||||
<Button type="submit">Sign In</Button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import type { CollectionSlug } from "payload";
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
export function SignOutButton({
|
||||
userCollectionSlug = "users",
|
||||
@@ -8,7 +9,7 @@ export function SignOutButton({
|
||||
userCollectionSlug?: CollectionSlug;
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
onClick={async () => {
|
||||
await fetch(`/api/${userCollectionSlug}/logout`, {
|
||||
@@ -21,6 +22,6 @@ export function SignOutButton({
|
||||
}}
|
||||
>
|
||||
Sign Out
|
||||
</button>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1 +1,124 @@
|
||||
@import "tailwindcss";
|
||||
@import "tw-animate-css";
|
||||
|
||||
@theme {
|
||||
--color-htl-red: #e4534dff;
|
||||
}
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
:root {
|
||||
--radius: 0.625rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.145 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.145 0 0);
|
||||
--primary: oklch(0.205 0 0);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.97 0 0);
|
||||
--secondary-foreground: oklch(0.205 0 0);
|
||||
--muted: oklch(0.97 0 0);
|
||||
--muted-foreground: oklch(0.556 0 0);
|
||||
--accent: oklch(0.97 0 0);
|
||||
--accent-foreground: oklch(0.205 0 0);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.922 0 0);
|
||||
--input: oklch(0.922 0 0);
|
||||
--ring: oklch(0.708 0 0);
|
||||
--chart-1: oklch(0.646 0.222 41.116);
|
||||
--chart-2: oklch(0.6 0.118 184.704);
|
||||
--chart-3: oklch(0.398 0.07 227.392);
|
||||
--chart-4: oklch(0.828 0.189 84.429);
|
||||
--chart-5: oklch(0.769 0.188 70.08);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.145 0 0);
|
||||
--sidebar-primary: oklch(0.205 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.97 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||
--sidebar-border: oklch(0.922 0 0);
|
||||
--sidebar-ring: oklch(0.708 0 0);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.205 0 0);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.922 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.556 0 0);
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
--sidebar: oklch(0.205 0 0);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.269 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.556 0 0);
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-card: var(--card);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-border: var(--border);
|
||||
--color-input: var(--input);
|
||||
--color-ring: var(--ring);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
43
src/app/(frontend)/htld.svg
Normal file
43
src/app/(frontend)/htld.svg
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 116.6 137.2" style="enable-background:new 0 0 116.6 137.2;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{fill:#E4534D;}
|
||||
.st2{fill:#3D4C5A;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M111.4,0H5.1C2.3,0,0,2.3,0,5.1v127c0,2.8,2.3,5.1,5.1,5.1h106.3c2.8,0,5.1-2.3,5.1-5.1V5.1
|
||||
C116.6,2.3,114.2,0,111.4,0"/>
|
||||
<path class="st1" d="M109.2,100.6c0,2.5-2,4.5-4.5,4.5H12c-2.5,0-4.5-2-4.5-4.5V11.7c0-2.5,2-4.5,4.5-4.5h92.8
|
||||
c2.5,0,4.5,2,4.5,4.5V100.6z"/>
|
||||
</g>
|
||||
<polygon class="st0" points="26.8,81.3 20.3,81.3 20.3,70.1 15.1,70.1 15.1,97.5 20.3,97.5 20.3,85.9 26.8,85.9 26.8,97.5 32,97.5
|
||||
32,70.1 26.8,70.1 "/>
|
||||
<polygon class="st0" points="35.5,74.7 40.8,74.7 40.8,97.5 45.9,97.5 45.9,74.7 51.2,74.7 51.2,70.1 35.5,70.1 "/>
|
||||
<polygon class="st0" points="59.9,70.1 54.8,70.1 54.8,97.5 68.9,97.5 68.9,92.8 59.9,92.8 "/>
|
||||
<g>
|
||||
<path class="st2" d="M69.1,126.3h-1.9v-5.1h1.8c2,0,2.9,0.8,2.9,2.6C71.8,125.4,70.8,126.3,69.1,126.3 M67.2,114.9h1.9
|
||||
c1.5,0,2.3,0.6,2.3,1.9c0,1.2-0.8,1.9-2.2,1.9h-2V114.9z M72.6,120.2l-0.5-0.2l0.5-0.3c1.2-0.6,1.9-1.7,1.9-3
|
||||
c0-2.7-2.1-4.4-5.4-4.4h-4.9v16.5h4.7c4,0,6.1-1.8,6.1-5.1C74.9,122.2,74.1,120.9,72.6,120.2"/>
|
||||
<path class="st2" d="M27.2,126.3c-2,0-2.9-1.9-2.9-5.7c0-2.4,0.3-5.7,2.9-5.7c1.9,0,2.9,1.9,2.9,5.7
|
||||
C30,124.5,29.1,126.3,27.2,126.3 M27.2,112.1c-3.8,0-6.1,3.2-6.1,8.5c0,5.4,2.2,8.5,6.1,8.5c3.8,0,6.1-3.2,6.1-8.5
|
||||
C33.2,115.3,30.9,112.1,27.2,112.1"/>
|
||||
<path class="st2" d="M41,119.9h-1.7v-4.8h1.6c1.9,0,2.9,0.8,2.9,2.4C43.7,118.6,43.2,119.9,41,119.9 M46.8,117.5
|
||||
c0-3.2-2.1-5.1-5.8-5.1h-4.8v16.5h3.1v-6.3h1.2l2.9,6.3h3.4l-3.2-6.9l0.3-0.1C45.2,121.3,46.8,120,46.8,117.5"/>
|
||||
<path class="st2" d="M57.7,119.2c0,0.7,0,1.7,0.1,2.5l0,0.3l0,1.3l-0.5-1.2c-0.3-0.8-0.8-1.8-1.1-2.5l-3.2-7.3h-3.2v16.5h2.8v-6.9
|
||||
c0-0.7,0-1.8,0-2.8l0-1.2l0.5,1.1c0.4,1,0.8,2,1,2.4l3.4,7.4h3v-16.5h-2.8V119.2z"/>
|
||||
<path class="st2" d="M89.6,119.9h-1.7v-4.8h1.6c1.9,0,2.9,0.8,2.9,2.4C92.3,118.6,91.8,119.9,89.6,119.9 M95.4,117.5
|
||||
c0-3.2-2.1-5.1-5.8-5.1h-4.8v16.5h3.1v-6.3h1.3l2.9,6.3h3.4l-3.2-6.9l0.2-0.1C93.9,121.3,95.4,120,95.4,117.5"/>
|
||||
<path class="st2" d="M106.3,112.4v6.8c0,0.7,0,1.8,0.1,2.7l0,0.2l0,1.2l-0.5-1.1c-0.3-0.8-0.8-1.8-1.1-2.5l-3.2-7.3h-3.2v16.5h2.8
|
||||
v-6.9c0-0.8,0-1.8,0-2.8l0-1.3l0.5,1.2c0.4,1,0.8,2,1,2.4l3.4,7.4h3v-16.5H106.3z"/>
|
||||
</g>
|
||||
<rect x="77.9" y="112.4" class="st2" width="3.1" height="16.5"/>
|
||||
<g>
|
||||
<path class="st2" d="M11.7,126.2h-1v-11.1h1c2.6,0,3.8,1.8,3.8,5.6C15.5,124.5,14.4,126.2,11.7,126.2 M11.7,112.4H7.6v16.5h4
|
||||
c3.2,0,7.1-1.4,7.1-8.3C18.7,115.2,16.4,112.4,11.7,112.4"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
@@ -1,19 +1,23 @@
|
||||
import React from 'react'
|
||||
import './globals.css'
|
||||
import React from "react";
|
||||
import "./globals.css";
|
||||
import Header from "@/app/(frontend)/_components/Header";
|
||||
import Footer from "@/app/(frontend)/_components/Footer";
|
||||
|
||||
export const metadata = {
|
||||
description: 'A blank template using Payload in a Next.js app.',
|
||||
title: 'Payload Blank Template',
|
||||
}
|
||||
description: "A blank template using Payload in a Next.js app.",
|
||||
title: "Payload Blank Template",
|
||||
};
|
||||
|
||||
export default async function RootLayout(props: { children: React.ReactNode }) {
|
||||
const { children } = props
|
||||
const { children } = props;
|
||||
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<html lang="de-AT">
|
||||
<body className="w-full min-h-full">
|
||||
<Header />
|
||||
<main>{children}</main>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,18 +1,39 @@
|
||||
import { auth } from "@/auth";
|
||||
import { getPayloadSession } from "payload-authjs";
|
||||
import { SignInButton } from "./_components/SignInButton";
|
||||
import { SignOutButton } from "./_components/SignOutButton";
|
||||
import { getPayload } from 'payload'
|
||||
import config from '@payload-config'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import DiplomarbeitSearch from '@/app/(frontend)/_components/DiplomarbeitSearch'
|
||||
|
||||
const payload = await getPayload({ config })
|
||||
|
||||
|
||||
const Page = async () => {
|
||||
const authjsSession = await auth();
|
||||
const payloadSession = await getPayloadSession();
|
||||
|
||||
|
||||
|
||||
|
||||
const media = await payload.find({
|
||||
collection: 'media',
|
||||
})
|
||||
|
||||
return (
|
||||
<main>
|
||||
<div className="bg-red-500 bg-">
|
||||
<h2 className="text-4xl">Mooongo Geiss</h2>
|
||||
<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>
|
||||
{payloadSession ? <SignOutButton /> : <SignInButton />}
|
||||
|
||||
{media.docs.map((med) => (
|
||||
<div key={med.id}>
|
||||
<h2>{med.url}</h2>
|
||||
<p>{med.alt}</p>
|
||||
</div>
|
||||
))}
|
||||
<br />
|
||||
<h3>Auth.js Session</h3>
|
||||
<pre>{JSON.stringify(authjsSession, null, 2)}</pre>
|
||||
|
||||
32
src/app/(payload)/api/diplomarbeiten/route.ts
Normal file
32
src/app/(payload)/api/diplomarbeiten/route.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getPayload } from 'payload';
|
||||
import config from '@payload-config';
|
||||
|
||||
const payload = await getPayload({ config });
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const searchParams = req.nextUrl.searchParams;
|
||||
const search = searchParams.get('search');
|
||||
|
||||
if (!search) {
|
||||
return NextResponse.json({ error: 'No documents matching search found' }, { status: 404 });
|
||||
}
|
||||
|
||||
const response = await payload.find({
|
||||
collection: 'papers',
|
||||
where: {
|
||||
or: [
|
||||
{ title: { contains: search } },
|
||||
{ issue: { contains: search } },
|
||||
{ goal: { contains: search } },
|
||||
{ 'technologies.description': { contains: search } },
|
||||
{ 'prototype.description': { contains: search } },
|
||||
{ 'authors.description': { contains: search } },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const titles = response.docs.map((doc) => doc.title);
|
||||
return NextResponse.json({ titles });
|
||||
}
|
||||
@@ -8,7 +8,18 @@ export const Papers: CollectionConfig = {
|
||||
plural: 'Diplomarbeiten',
|
||||
},
|
||||
access: {
|
||||
create: ({ req: { user } }) => {
|
||||
return Boolean(user?.type == "admin") // <-- Check if the user is authenticated
|
||||
},
|
||||
delete: ({ req: { user } }) => {
|
||||
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 });
|
||||
return paper.authors.some(author => author.user === user.id); // Check if the user is an author
|
||||
},
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
|
||||
Reference in New Issue
Block a user