init
This commit is contained in:
@@ -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 { 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>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;
|
||||
@@ -0,0 +1,19 @@
|
||||
import { hasPermission, Role } from "@/lib/validation/has-permission";
|
||||
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";
|
||||
|
||||
async function Page({ params }: { params: Promise<{ slug: string }> }) {
|
||||
const { slug } = await params;
|
||||
const session = await auth();
|
||||
const isEditor = session?.user
|
||||
? hasPermission(session.user.role, Role.EDITOR)
|
||||
: false;
|
||||
const article = await api.article.get({ slug: slug });
|
||||
if (!article || !isEditor) return notFound();
|
||||
return <Editor server_article={article} />;
|
||||
}
|
||||
|
||||
export default Page;
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { hasPermission, Role } from "@/lib/validation/has-permission";
|
||||
import { auth } from "@/server/auth";
|
||||
import { api } from "@/trpc/server";
|
||||
import { Edit, Edit2Icon, Edit3, MoreVertical, Trash } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import React from "react";
|
||||
|
||||
async function Page({ params }: { params: Promise<{ slug: string }> }) {
|
||||
const { slug } = await params;
|
||||
const article = await api.article.get({ slug: slug });
|
||||
if (!article) return notFound();
|
||||
const session = await auth();
|
||||
const isEditor = session?.user
|
||||
? hasPermission(session.user.role, Role.EDITOR)
|
||||
: false;
|
||||
return (
|
||||
<div>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<h1 className="text-4xl font-bold">{article.title}</h1>
|
||||
{isEditor && (
|
||||
<div className="space-x-2">
|
||||
<Button asChild variant={"outline"}>
|
||||
<Link href={`/artikel/${article.slug}/edit`}>
|
||||
<Edit className="size-4" />
|
||||
<span>Bearbeiten</span>
|
||||
</Link>
|
||||
</Button>
|
||||
<Button size={"icon"} variant={"outline"}>
|
||||
<MoreVertical className="size-4" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Page;
|
||||
@@ -0,0 +1,9 @@
|
||||
import React from 'react'
|
||||
|
||||
function Page() {
|
||||
return (
|
||||
<div>Alle Artikel Page</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Page
|
||||
@@ -0,0 +1,8 @@
|
||||
import React from "react";
|
||||
|
||||
async function Page({ params }: { params: Promise<{ name: string }> }) {
|
||||
const { name } = await params;
|
||||
return <div>Kategorie {name}</div>;
|
||||
}
|
||||
|
||||
export default Page;
|
||||
@@ -0,0 +1,7 @@
|
||||
import React from "react";
|
||||
|
||||
function Page() {
|
||||
return <div>Alle Kategorien</div>;
|
||||
}
|
||||
|
||||
export default Page;
|
||||
@@ -0,0 +1,18 @@
|
||||
import { AppSidebar } from "@/components/layout/app-sidebar";
|
||||
import Navbar from "@/components/layout/navbar";
|
||||
import { SidebarProvider } from "@/components/ui/sidebar";
|
||||
import React from "react";
|
||||
|
||||
function Layout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<SidebarProvider>
|
||||
<AppSidebar />
|
||||
<div className="w-full">
|
||||
<Navbar />
|
||||
<main className="p-4">{children}</main>
|
||||
</div>
|
||||
</SidebarProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default Layout;
|
||||
@@ -0,0 +1,21 @@
|
||||
import { auth } from "@/server/auth";
|
||||
import { api } from "@/trpc/server";
|
||||
import Link from "next/link";
|
||||
|
||||
export default async function Home() {
|
||||
const session = await auth();
|
||||
const articles = await api.article.getAllPreviews();
|
||||
return (
|
||||
<div className="">
|
||||
<h1 className="text-4xl font-bold">Anti Rechts Wiki</h1>
|
||||
|
||||
<menu>
|
||||
{articles.map((article) => (
|
||||
<li key={article.slug}>
|
||||
<Link href={`/artikel/${article.slug}`}>{article.title}</Link>
|
||||
</li>
|
||||
))}
|
||||
</menu>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import { handlers } from "@/server/auth";
|
||||
|
||||
export const { GET, POST } = handlers;
|
||||
@@ -0,0 +1,34 @@
|
||||
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
|
||||
import { type NextRequest } from "next/server";
|
||||
|
||||
import { env } from "@/env";
|
||||
import { appRouter } from "@/server/api/root";
|
||||
import { createTRPCContext } from "@/server/api/trpc";
|
||||
|
||||
/**
|
||||
* This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when
|
||||
* handling a HTTP request (e.g. when you make requests from Client Components).
|
||||
*/
|
||||
const createContext = async (req: NextRequest) => {
|
||||
return createTRPCContext({
|
||||
headers: req.headers,
|
||||
});
|
||||
};
|
||||
|
||||
const handler = (req: NextRequest) =>
|
||||
fetchRequestHandler({
|
||||
endpoint: "/api/trpc",
|
||||
req,
|
||||
router: appRouter,
|
||||
createContext: () => createContext(req),
|
||||
onError:
|
||||
env.NODE_ENV === "development"
|
||||
? ({ path, error }) => {
|
||||
console.error(
|
||||
`❌ tRPC failed on ${path ?? "<no-path>"}: ${error.message}`
|
||||
);
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
|
||||
export { handler as GET, handler as POST };
|
||||
@@ -0,0 +1,24 @@
|
||||
import "@/styles/globals.css";
|
||||
|
||||
import { GeistSans } from "geist/font/sans";
|
||||
import { type Metadata } from "next";
|
||||
|
||||
import { TRPCReactProvider } from "@/trpc/react";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create T3 App",
|
||||
description: "Generated by create-t3-app",
|
||||
icons: [{ rel: "icon", url: "/favicon.ico" }],
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{ children: React.ReactNode }>) {
|
||||
return (
|
||||
<html lang="en" className={`${GeistSans.variable}`}>
|
||||
<body>
|
||||
<TRPCReactProvider>{children}</TRPCReactProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user