From 66aadfd04dc95773fce56bb50de9a78d2ac455ad Mon Sep 17 00:00:00 2001 From: shrt Date: Sat, 8 Mar 2025 12:48:18 +0100 Subject: [PATCH] create category dialog --- README.md | 3 + docs/start.md | 12 ++ .../_components/article/article-card.tsx | 17 +++ .../article/create-article-dialog.tsx | 2 +- .../_components/category/category-card.tsx | 17 +++ .../category/create-category-dialog.tsx | 81 ++++++++++++ src/app/(PAGES)/_components/global-stats.tsx | 45 +++++++ src/app/(PAGES)/kategorie/[name]/page.tsx | 55 ++++++++- src/app/(PAGES)/layout.tsx | 2 +- src/app/(PAGES)/page.tsx | 42 +++++-- src/components/bread-navigator.tsx | 42 +++++++ src/components/layout/navbar.tsx | 15 ++- src/components/ui/breadcrumb.tsx | 115 ++++++++++++++++++ src/components/ui/card.tsx | 76 ++++++++++++ src/server/actions/category.ts | 3 +- src/server/api/root.ts | 2 + src/server/api/routers/article.ts | 18 ++- src/server/api/routers/author.ts | 7 ++ src/server/api/routers/category.ts | 6 +- 19 files changed, 535 insertions(+), 25 deletions(-) create mode 100644 docs/start.md create mode 100644 src/app/(PAGES)/_components/article/article-card.tsx create mode 100644 src/app/(PAGES)/_components/category/category-card.tsx create mode 100644 src/app/(PAGES)/_components/category/create-category-dialog.tsx create mode 100644 src/app/(PAGES)/_components/global-stats.tsx create mode 100644 src/components/bread-navigator.tsx create mode 100644 src/components/ui/breadcrumb.tsx create mode 100644 src/components/ui/card.tsx create mode 100644 src/server/api/routers/author.ts diff --git a/README.md b/README.md index 67943c7..30e884f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +https://docs.google.com/document/d/1z1sCHzXE0cN2vAPtI4rkddCkIfzXs2ihKtYowZ9JjBw/edit?usp=sharing + + # Create T3 App This is a [T3 Stack](https://create.t3.gg/) project bootstrapped with `create-t3-app`. diff --git a/docs/start.md b/docs/start.md new file mode 100644 index 0000000..ea5247a --- /dev/null +++ b/docs/start.md @@ -0,0 +1,12 @@ +# Docs + +### How things are +##### Images +Stored in public subfolder "/uploads" + + +### How things cahnge if ... +##### Images +Cloudeflare R2 with custom domain + + diff --git a/src/app/(PAGES)/_components/article/article-card.tsx b/src/app/(PAGES)/_components/article/article-card.tsx new file mode 100644 index 0000000..c81a73a --- /dev/null +++ b/src/app/(PAGES)/_components/article/article-card.tsx @@ -0,0 +1,17 @@ +import { Article } from "@/server/db/schema"; +import Link from "next/link"; +import React from "react"; + +function ArticleCard({ + title, + slug, + createdAt, +}: Pick) { + return ( + +
{title}
+ + ); +} + +export default ArticleCard; diff --git a/src/app/(PAGES)/_components/article/create-article-dialog.tsx b/src/app/(PAGES)/_components/article/create-article-dialog.tsx index 47a1f57..aed4b63 100644 --- a/src/app/(PAGES)/_components/article/create-article-dialog.tsx +++ b/src/app/(PAGES)/_components/article/create-article-dialog.tsx @@ -47,7 +47,7 @@ function CreateArticleDialog() { return ( diff --git a/src/app/(PAGES)/_components/category/category-card.tsx b/src/app/(PAGES)/_components/category/category-card.tsx new file mode 100644 index 0000000..545bbfc --- /dev/null +++ b/src/app/(PAGES)/_components/category/category-card.tsx @@ -0,0 +1,17 @@ +import { Category } from "@/server/db/schema"; +import Link from "next/link"; +import React from "react"; + +function CategoryCard({ + name, + slug, + createdAt, +}: Pick) { + return ( + +
{name}
+ + ); +} + +export default CategoryCard; diff --git a/src/app/(PAGES)/_components/category/create-category-dialog.tsx b/src/app/(PAGES)/_components/category/create-category-dialog.tsx new file mode 100644 index 0000000..7af0eb9 --- /dev/null +++ b/src/app/(PAGES)/_components/category/create-category-dialog.tsx @@ -0,0 +1,81 @@ +"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 { categorySchema } from "@/lib/validation/zod/category"; +import { createCategory } from "@/server/actions/category"; + +const formSchema = categorySchema.pick({ + name: true, +}); + +function CreateCategoryDialog() { + const [open, setOpen] = React.useState(false); + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + name: "", + }, + }); + + // 2. Define a submit handler. + async function onSubmit(values: z.infer) { + setOpen(false); + await createCategory(values); + } + return ( + + + + + Neue Kategorie Erstellen + + +
+ + ( + + Name + + + + + + + )} + /> + + + +
+
+ ); +} + +export default CreateCategoryDialog; diff --git a/src/app/(PAGES)/_components/global-stats.tsx b/src/app/(PAGES)/_components/global-stats.tsx new file mode 100644 index 0000000..5708cbe --- /dev/null +++ b/src/app/(PAGES)/_components/global-stats.tsx @@ -0,0 +1,45 @@ +import React from "react"; +import { api } from "@/trpc/server"; +import { Card, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; + +const StatCard = ({ + title, + value, + children, +}: { + title: string; + value: string; + children?: React.ReactNode; +}) => { + return ( + + + {value} + {title} + + {children && {children}} + + ); +}; + +async function GlobalStats() { + const articleCount = await api.article.getCount(); + const authorCount = await api.author.getCount(); + const categoryCount = await api.category.getCount(); + return ( +
+ + {/* */} + + + +
+ ); +} + +export default GlobalStats; diff --git a/src/app/(PAGES)/kategorie/[name]/page.tsx b/src/app/(PAGES)/kategorie/[name]/page.tsx index cc3142d..8b5ed37 100644 --- a/src/app/(PAGES)/kategorie/[name]/page.tsx +++ b/src/app/(PAGES)/kategorie/[name]/page.tsx @@ -1,8 +1,61 @@ +import BreadNavigator from "@/components/bread-navigator"; +import { Button } from "@/components/ui/button"; +import { hasPermission, Role } from "@/lib/validation/permissions"; +import { auth } from "@/server/auth"; +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"; async function Page({ params }: { params: Promise<{ name: string }> }) { const { name } = await params; - return
Kategorie {name}
; + const category = await api.category.get({ slug: name }); + if (!category) return notFound(); + const articles = await api.article.getAll({ categoryId: category.id }); + const session = await auth(); + const isEditor = session?.user + ? hasPermission(session.user.role, Role.EDITOR) + : false; + return ( + <> +
+ + + {isEditor && ( + + )} +
+

{name}

+

keine Beschreibung

+ +

Artikel

+ {articles.length ? ( + + {articles.map((article) => ( +
  • + +
  • + ))} +
    + ) : ( +

    + Noch keine Artikel in dieser Kategorie. +

    + )} + + ); } export default Page; diff --git a/src/app/(PAGES)/layout.tsx b/src/app/(PAGES)/layout.tsx index 7dbcd98..02b409e 100644 --- a/src/app/(PAGES)/layout.tsx +++ b/src/app/(PAGES)/layout.tsx @@ -9,7 +9,7 @@ function Layout({ children }: { children: React.ReactNode }) {
    -
    {children}
    +
    {children}
    ); diff --git a/src/app/(PAGES)/page.tsx b/src/app/(PAGES)/page.tsx index 7f342d5..85dc710 100644 --- a/src/app/(PAGES)/page.tsx +++ b/src/app/(PAGES)/page.tsx @@ -1,21 +1,43 @@ 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 CategoryCard from "./_components/category/category-card"; export default async function Home() { const session = await auth(); const articles = await api.article.getAllPreviews(); + const categories = await api.category.getAll(); return ( -
    -

    Anti Rechts Wiki

    + <> +
    +

    Kategorien

    + + {categories.map((category) => ( +
  • + +
  • + ))} +
    +
    + {/*

    Anti Rechts Wiki

    */} +
    + Artikel Suche +
    - - {articles.map((article) => ( -
  • - {article.title} -
  • - ))} -
    -
    + {/* */} + +
    +

    Artikel

    + + {articles.map((article) => ( +
  • + +
  • + ))} +
    +
    + ); } diff --git a/src/components/bread-navigator.tsx b/src/components/bread-navigator.tsx new file mode 100644 index 0000000..eb3c845 --- /dev/null +++ b/src/components/bread-navigator.tsx @@ -0,0 +1,42 @@ +import React from "react"; +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbList, + BreadcrumbPage, + BreadcrumbSeparator, +} from "@/components/ui/breadcrumb"; + +function BreadNavigator({ + links, +}: { + links: { label: string; href: string }[]; +}) { + return ( + + + {links.map(({ label, href }, idx) => { + if (idx < links.length - 1) + return ( + + + + {label} + + + + + ); + return ( + + {label} + + ); + })} + + + ); +} + +export default BreadNavigator; diff --git a/src/components/layout/navbar.tsx b/src/components/layout/navbar.tsx index 16864b8..1d0540d 100644 --- a/src/components/layout/navbar.tsx +++ b/src/components/layout/navbar.tsx @@ -11,6 +11,7 @@ import { PopoverTrigger, } from "@/components/ui/popover"; import CreateArticleDialog from "@/app/(PAGES)/_components/article/create-article-dialog"; +import CreateCategoryDialog from "@/app/(PAGES)/_components/category/create-category-dialog"; async function Navbar() { const session = await auth(); @@ -25,11 +26,6 @@ async function Navbar() {
    - {isAdmin && ( - - )} {isEditor && ( @@ -42,12 +38,15 @@ async function Navbar() { // side="left" > - + )} + {isAdmin && ( + + )} {session ? ( & { + separator?: React.ReactNode + } +>(({ ...props }, ref) =>