This commit is contained in:
2025-03-11 15:51:31 +01:00
parent bccd089c7f
commit 7135e34699
34 changed files with 633 additions and 130 deletions
@@ -1,17 +0,0 @@
import { Article } from "@/server/db/schema";
import Link from "next/link";
import React from "react";
function ArticleCard({
title,
slug,
createdAt,
}: Pick<Article, "title" | "slug" | "createdAt">) {
return (
<Link href={`/artikel/${slug}`}>
<div className="rounded-md border p-4">{title}</div>
</Link>
);
}
export default ArticleCard;
@@ -1,81 +0,0 @@
"use client";
import React from "react";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { articleSchema } from "@/lib/validation/zod/article";
import { createArticle } from "@/server/actions/article";
const formSchema = articleSchema.pick({
title: true,
});
function CreateArticleDialog() {
const [open, setOpen] = React.useState<boolean>(false);
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
title: "",
},
});
// 2. Define a submit handler.
async function onSubmit(values: z.infer<typeof formSchema>) {
setOpen(false);
await createArticle(values);
}
return (
<Dialog open={open} onOpenChange={setOpen}>
<Button asChild>
<DialogTrigger className="w-full">Artikel Erstellen</DialogTrigger>
</Button>
<DialogContent>
<DialogHeader>
<DialogTitle>Neuen Artikel Erstellen</DialogTitle>
<DialogDescription></DialogDescription>
</DialogHeader>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel>Titel</FormLabel>
<FormControl>
<Input placeholder="Artikel Titel" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Artikel Erstellen</Button>
</form>
</Form>
</DialogContent>
</Dialog>
);
}
export default CreateArticleDialog;
@@ -1,3 +1,4 @@
import { appRoutes } from "@/config";
import { Category } from "@/server/db/schema";
import Link from "next/link";
import React from "react";
@@ -8,7 +9,7 @@ function CategoryCard({
createdAt,
}: Pick<Category, "name" | "slug" | "createdAt">) {
return (
<Link href={`/kategorie/${slug}`}>
<Link href={appRoutes.category(slug)}>
<div className="rounded-md border p-4">{name}</div>
</Link>
);
@@ -3,7 +3,7 @@ import { auth } from "@/server/auth";
import { api } from "@/trpc/server";
import { notFound } from "next/navigation";
import React from "react";
import Editor from "@/components/article/editor/article-form";
import ArticleEditor from "@/components/article/article-form";
async function Page({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
@@ -13,7 +13,7 @@ async function Page({ params }: { params: Promise<{ slug: string }> }) {
: false;
const article = await api.article.get({ slug: slug });
if (!article || !isEditor) return notFound();
return <Editor server_article={article} />;
return <ArticleEditor server_article={article} />;
}
export default Page;
+27 -4
View File
@@ -1,4 +1,7 @@
import RenderArticle from "@/components/article/render-article";
import BreadNavigator from "@/components/bread-navigator";
import { Button } from "@/components/ui/button";
import { appRoutes } from "@/config";
import { hasPermission, Role } from "@/lib/validation/permissions";
import { auth } from "@/server/auth";
import { api } from "@/trpc/server";
@@ -16,13 +19,28 @@ async function Page({ params }: { params: Promise<{ slug: string }> }) {
? hasPermission(session.user.role, Role.EDITOR)
: false;
return (
<div>
<div className="space-y-2">
<div className="flex w-full items-center justify-between">
<h1 className="text-4xl font-bold">{article.title}</h1>
<div className="w-full">
<BreadNavigator
links={[
...(article?.category
? [
{
label: article.category.name!,
href: appRoutes.category(article.category.slug),
},
]
: []),
{ label: "Artikel", href: appRoutes.allArticles },
{ label: article.title!, href: appRoutes.article(article.slug) },
]}
/>
</div>
{isEditor && (
<div className="space-x-2">
<div className="flex w-full justify-end space-x-2">
<Button asChild variant={"outline"}>
<Link href={`/artikel/${article.slug}/edit`}>
<Link href={appRoutes.editArticle(article.slug)}>
<Edit className="size-4" />
<span>Bearbeiten</span>
</Link>
@@ -33,6 +51,11 @@ async function Page({ params }: { params: Promise<{ slug: string }> }) {
</div>
)}
</div>
<h1 className="text-4xl font-bold">{article.title}</h1>
{article?.content?.length ? (
<RenderArticle content={article.content} />
) : null}
</div>
);
}
+4 -3
View File
@@ -6,7 +6,8 @@ import { api } from "@/trpc/server";
import { Edit } from "lucide-react";
import { notFound } from "next/navigation";
import React from "react";
import ArticleCard from "../../_components/article/article-card";
import ArticleCard from "../../../../components/article/article-card";
import { appRoutes } from "@/config";
async function Page({ params }: { params: Promise<{ name: string }> }) {
const { name } = await params;
@@ -22,10 +23,10 @@ async function Page({ params }: { params: Promise<{ name: string }> }) {
<div className="flex items-center justify-between">
<BreadNavigator
links={[
{ label: "Kategorie", href: "/kategorie" },
{ label: "Kategorie", href: appRoutes.allCategories },
{
label: name,
href: `/kategorie/${name}`,
href: appRoutes.category(name),
},
]}
/>
+1 -1
View File
@@ -2,7 +2,7 @@ import { auth } from "@/server/auth";
import { api } from "@/trpc/server";
import Link from "next/link";
import GlobalStats from "./_components/global-stats";
import ArticleCard from "./_components/article/article-card";
import ArticleCard from "../../components/article/article-card";
import CategoryCard from "./_components/category/category-card";
export default async function Home() {
+1 -1
View File
@@ -14,7 +14,7 @@ import Link from "next/link";
import SidebarLink from "@/components/layout/sidebar-link";
import { Button } from "@/components/ui/button";
import { LogOutIcon } from "lucide-react";
import { appConfig } from "@/app.config";
import { appConfig } from "@/config/app.config";
const data = {
navMain: [