fixed some role based logic bugs; fixed ui parts
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 0s
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 0s
This commit is contained in:
parent
5732554fe2
commit
9dcb3fd851
@ -2,6 +2,7 @@ import BreadNavigator from "@/components/bread-navigator";
|
||||
import RenderContent from "@/components/editor/render-content";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { appRoutes } from "@/config";
|
||||
import { hasPermission, Role } from "@/lib/validation/permissions";
|
||||
import { auth } from "@/server/auth";
|
||||
@ -20,8 +21,8 @@ async function Page({ params }: { params: Promise<{ slug: string }> }) {
|
||||
? hasPermission(session.user.role, Role.EDITOR)
|
||||
: false;
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="">
|
||||
{/* <div className="flex w-full items-center justify-between">
|
||||
<div className="flex w-full items-center gap-4">
|
||||
<BreadNavigator
|
||||
className="w-full"
|
||||
@ -39,29 +40,50 @@ async function Page({ params }: { params: Promise<{ slug: string }> }) {
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
{isEditor && (
|
||||
<div className="flex w-full items-center justify-end space-x-2">
|
||||
<Badge
|
||||
className="size-max"
|
||||
variant={article.published ? "outline" : "destructive"}
|
||||
>
|
||||
{article.published ? "Veröffentlicht" : "Draft"}
|
||||
</Badge>
|
||||
|
||||
<Button asChild variant={"outline"}>
|
||||
<Link href={appRoutes.editArticle(article.slug)}>
|
||||
<Edit className="size-4" />
|
||||
<span>Bearbeiten</span>
|
||||
</Link>
|
||||
</Button>
|
||||
<Button size={"icon"} variant={"outline"}>
|
||||
<MoreVertical className="size-4" />
|
||||
</Button>
|
||||
</div> */}
|
||||
<div className="flex w-full flex-col gap-4 xl:flex-row">
|
||||
<div className="w-full space-y-4">
|
||||
<h1 className="text-4xl font-bold">{article.title}</h1>
|
||||
<div className="w-full">
|
||||
{article.content && <RenderContent content={article.content} />}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Separator
|
||||
orientation="vertical"
|
||||
className="sticky top-0 hidden h-screen -translate-y-4 xl:block"
|
||||
/>
|
||||
<div
|
||||
className={
|
||||
"top-4 h-max w-full max-w-xl space-y-4 xl:sticky xl:max-w-sm"
|
||||
}
|
||||
>
|
||||
<div className="relative min-h-96 w-full space-y-4 overflow-hidden rounded-md border bg-background p-4">
|
||||
<p className="text-center text-muted-foreground">
|
||||
Kommentare bald verfügbar
|
||||
</p>
|
||||
</div>
|
||||
{isEditor && (
|
||||
<div className="flex w-full items-center justify-end space-x-2">
|
||||
<Badge
|
||||
className="size-max"
|
||||
variant={article.published ? "outline" : "destructive"}
|
||||
>
|
||||
{article.published ? "Veröffentlicht" : "Draft"}
|
||||
</Badge>
|
||||
|
||||
<Button asChild variant={"outline"}>
|
||||
<Link href={appRoutes.editArticle(article.slug)}>
|
||||
<Edit className="size-4" />
|
||||
<span>Bearbeiten</span>
|
||||
</Link>
|
||||
</Button>
|
||||
<Button size={"icon"} variant={"outline"}>
|
||||
<MoreVertical className="size-4" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<h1 className="text-4xl font-bold">{article.title}</h1>
|
||||
{article.content && <RenderContent content={article.content} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -24,6 +24,7 @@ async function Page({ params }: CategoryPageProps) {
|
||||
articles: true,
|
||||
},
|
||||
});
|
||||
console.log(category);
|
||||
|
||||
if (!category) return notFound();
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@ function UserForm({ server_user, cb }: { server_user: User; cb?: () => void }) {
|
||||
const form = useForm<z.infer<typeof userSchema>>({
|
||||
resolver: zodResolver(userSchema),
|
||||
defaultValues: {
|
||||
email: server_user.email ?? "",
|
||||
name: server_user?.name ?? "",
|
||||
},
|
||||
});
|
||||
|
||||
@ -15,7 +15,7 @@ export default async function Home() {
|
||||
return (
|
||||
<>
|
||||
<Alert>
|
||||
<div className="flex gap-2">
|
||||
<div className="flex flex-col gap-2 sm:flex-row">
|
||||
<Icons.logo className="size-8" />
|
||||
<div>
|
||||
<AlertTitle className="font-bold">
|
||||
@ -25,7 +25,7 @@ export default async function Home() {
|
||||
Lorem ipsum, dolor sit amet consectetur adipisicing elit.
|
||||
</AlertDescription>
|
||||
</div>
|
||||
<Button asChild className="ml-auto">
|
||||
<Button asChild className="sm:ml-auto">
|
||||
<Link href={appRoutes.about}>
|
||||
Erfahre mehr über {appConfig.name}
|
||||
</Link>
|
||||
|
||||
@ -68,7 +68,7 @@ export default ({ server_article }: { server_article: Article }) => {
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="flex w-full flex-col-reverse gap-4 space-y-4 xl:flex-row"
|
||||
className="flex w-full flex-col-reverse gap-4 xl:flex-row"
|
||||
>
|
||||
<div className="flex w-full max-w-3xl flex-col gap-2">
|
||||
<FormField
|
||||
@ -112,12 +112,7 @@ export default ({ server_article }: { server_article: Article }) => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
"top-4 h-max w-full max-w-xl xl:sticky xl:max-w-md",
|
||||
// loading && "border-t-blue-600",
|
||||
)}
|
||||
>
|
||||
<div className={"top-4 h-max w-full max-w-xl xl:sticky xl:max-w-md"}>
|
||||
<div className="relative w-full space-y-4 overflow-hidden rounded-md border bg-background p-4">
|
||||
<div
|
||||
className={cn(
|
||||
|
||||
@ -6,18 +6,31 @@ import { Icons } from "../icons";
|
||||
|
||||
function CategorySelect(props: Partial<ComboboxProps>) {
|
||||
const { data: categories } = api.category.getMany.useQuery();
|
||||
const initialValue = categories?.find(
|
||||
(c) => c.id === props?.initialValue,
|
||||
)?.slug;
|
||||
|
||||
return (
|
||||
<Combobox
|
||||
{...(props as ComboboxProps)}
|
||||
initialValue={initialValue}
|
||||
messageUi={{
|
||||
select: "Kategorie auswählen...",
|
||||
selectIcon: Icons.category,
|
||||
placeholder: "Kategorie suchen...",
|
||||
empty: "Keine Kategorien gefunden",
|
||||
}}
|
||||
onSelect={(value) => {
|
||||
const id = categories?.find((c) => {
|
||||
return c.slug === value;
|
||||
})?.id!;
|
||||
props?.onSelect?.(id);
|
||||
}}
|
||||
data={
|
||||
categories?.map(({ name, slug }) => ({ label: name, value: slug })) ??
|
||||
[]
|
||||
categories?.map(({ name, slug }) => ({
|
||||
label: name,
|
||||
value: slug,
|
||||
})) ?? []
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -58,22 +58,18 @@ const updatedImage = UpdatedImage.configure({
|
||||
},
|
||||
});
|
||||
|
||||
const taskList = TaskList.configure({
|
||||
HTMLAttributes: {
|
||||
class: cx("not-prose pl-2 "),
|
||||
},
|
||||
});
|
||||
const taskList = TaskList.configure({});
|
||||
|
||||
const taskItem = TaskItem.configure({
|
||||
HTMLAttributes: {
|
||||
class: cx("flex gap-2 items-start my-4"),
|
||||
class: cx("flex gap-2 items-start "),
|
||||
},
|
||||
nested: true,
|
||||
});
|
||||
|
||||
const horizontalRule = HorizontalRule.configure({
|
||||
HTMLAttributes: {
|
||||
class: cx("mt-4 mb-6 border-t border-muted-foreground"),
|
||||
class: cx("mt-4 mb-6 border border-border"),
|
||||
},
|
||||
});
|
||||
|
||||
@ -85,6 +81,21 @@ const starterKit = StarterKit.configure({
|
||||
heading: {
|
||||
levels: [2, 3, 4, 5, 6],
|
||||
},
|
||||
bulletList: {
|
||||
HTMLAttributes: {
|
||||
class: cx("list-disc ml-4 px-2 "),
|
||||
},
|
||||
},
|
||||
orderedList: {
|
||||
HTMLAttributes: {
|
||||
class: cx("list-decimal ml-4 px-2 "),
|
||||
},
|
||||
},
|
||||
blockquote: {
|
||||
HTMLAttributes: {
|
||||
class: cx("m-4 border-l-4 border-border pl-4"),
|
||||
},
|
||||
},
|
||||
gapcursor: false,
|
||||
horizontalRule: false,
|
||||
});
|
||||
|
||||
@ -17,18 +17,38 @@
|
||||
margin: 0.75rem 0;
|
||||
}
|
||||
|
||||
.tiptap ul,
|
||||
.tiptap ol {
|
||||
margin-left: 1rem;
|
||||
padding: 0.25rem;
|
||||
ul[data-type="taskList"] li > label input[type="checkbox"] {
|
||||
@apply size-4 border border-border bg-muted;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
margin: 0;
|
||||
margin-bottom: 0.75rem;
|
||||
margin-top: 0.75rem;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transform: translateY(0.17rem);
|
||||
display: grid;
|
||||
border-radius: 0.25rem;
|
||||
place-content: center;
|
||||
}
|
||||
|
||||
.tiptap ul {
|
||||
list-style-type: disc;
|
||||
ul[data-type="taskList"] li > label input[type="checkbox"]::before {
|
||||
content: "";
|
||||
width: 0.65em;
|
||||
height: 0.65em;
|
||||
transform: scale(0);
|
||||
transition: 120ms transform ease-in-out;
|
||||
box-shadow: inset 1em 1em;
|
||||
transform-origin: center;
|
||||
clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
|
||||
}
|
||||
|
||||
.tiptap ol {
|
||||
list-style-type: decimal;
|
||||
ul[data-type="taskList"] li > label input[type="checkbox"]:checked::before {
|
||||
transform: scale(1);
|
||||
}
|
||||
ul[data-type="taskList"] li[data-checked="true"] > div > p {
|
||||
@apply text-foreground/75;
|
||||
text-decoration: line-through;
|
||||
text-decoration-thickness: 2px;
|
||||
}
|
||||
|
||||
/* Heading styles */
|
||||
@ -65,17 +85,9 @@
|
||||
font-size: 1.125rem; /* Equivalent to text-lg */
|
||||
}
|
||||
|
||||
.tiptap blockquote {
|
||||
@apply m-4 border-l-4 border-border pl-4;
|
||||
/* border-left: 1rem solid var(--primary);
|
||||
padding-left: 1rem; */
|
||||
}
|
||||
|
||||
.tiptap hr {
|
||||
@apply border-border;
|
||||
/* border-color: var(--border); */
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
/* .tiptap blockquote {
|
||||
@apply ;
|
||||
} */
|
||||
|
||||
::selection {
|
||||
background-color: #5abbf7;
|
||||
@ -89,52 +101,6 @@ img {
|
||||
@apply rounded-md;
|
||||
}
|
||||
|
||||
/* Task List */
|
||||
ul[data-type="taskList"] li {
|
||||
@apply m-0 my-3 flex items-center;
|
||||
}
|
||||
|
||||
ul[data-type="taskList"] li p {
|
||||
@apply m-0 my-0 flex items-center;
|
||||
}
|
||||
|
||||
ul[data-type="taskList"] li > label input[type="checkbox"] {
|
||||
@apply size-4 border border-border bg-muted;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
|
||||
display: grid;
|
||||
border-radius: 0.25rem;
|
||||
place-content: center;
|
||||
}
|
||||
/* ul[data-type="taskList"] li > label input[type="checkbox"]:hover {
|
||||
background-color: #000;
|
||||
}
|
||||
ul[data-type="taskList"] li > label input[type="checkbox"]:active {
|
||||
background-color: #000;
|
||||
} */
|
||||
ul[data-type="taskList"] li > label input[type="checkbox"]::before {
|
||||
content: "";
|
||||
width: 0.65em;
|
||||
height: 0.65em;
|
||||
transform: scale(0);
|
||||
transition: 120ms transform ease-in-out;
|
||||
box-shadow: inset 1em 1em;
|
||||
transform-origin: center;
|
||||
clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
|
||||
}
|
||||
ul[data-type="taskList"] li > label input[type="checkbox"]:checked::before {
|
||||
transform: scale(1);
|
||||
}
|
||||
ul[data-type="taskList"] li[data-checked="true"] > div > p {
|
||||
@apply text-foreground/75;
|
||||
text-decoration: line-through;
|
||||
text-decoration-thickness: 2px;
|
||||
}
|
||||
|
||||
.ProseMirror:not(.dragging) .ProseMirror-selectednode {
|
||||
outline: none !important;
|
||||
background-color: var(--novel-highlight-blue);
|
||||
|
||||
@ -4,7 +4,7 @@ import { createCallerFactory, createTRPCRouter } from "@/server/api/trpc";
|
||||
import { usersRouter } from "./routers/users";
|
||||
import { authorRouter } from "./routers/author";
|
||||
import { appRouter as globalRouter } from "./routers/app";
|
||||
import { authRouter } from "./routers/auth";
|
||||
// import { authRouter } from "./routers/auth";
|
||||
/**
|
||||
* This is the primary router for your server.
|
||||
*
|
||||
@ -16,7 +16,7 @@ export const appRouter = createTRPCRouter({
|
||||
users: usersRouter,
|
||||
author: authorRouter,
|
||||
app: globalRouter,
|
||||
auth: authRouter,
|
||||
// auth: authRouter,
|
||||
});
|
||||
|
||||
// export type definition of API
|
||||
|
||||
@ -162,10 +162,15 @@ export const articleRouter = createTRPCRouter({
|
||||
)
|
||||
.query(async ({ ctx, input }) => {
|
||||
const limit = input?.limit ?? 50;
|
||||
return (await ctx.db.query.articles.findMany({
|
||||
where: input?.categoryId
|
||||
const where = and(
|
||||
input?.categoryId
|
||||
? eq(articles.categoryId, input.categoryId)
|
||||
: undefined,
|
||||
eq(articles.published, true),
|
||||
);
|
||||
|
||||
return (await ctx.db.query.articles.findMany({
|
||||
where,
|
||||
limit: limit,
|
||||
columns: {
|
||||
title: true,
|
||||
|
||||
@ -1,54 +1,54 @@
|
||||
import { passwordSchema, userSchema } from "@/lib/validation/zod/user";
|
||||
import { createTRPCRouter, publicProcedure } from "../trpc";
|
||||
import { z } from "zod";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { users } from "@/server/db/schema";
|
||||
import argon from "argon2";
|
||||
// import { passwordSchema, userSchema } from "@/lib/validation/zod/user";
|
||||
// import { createTRPCRouter, publicProcedure } from "../trpc";
|
||||
// import { z } from "zod";
|
||||
// import { eq } from "drizzle-orm";
|
||||
// import { users } from "@/server/db/schema";
|
||||
// import argon from "argon2";
|
||||
|
||||
export const authRouter = createTRPCRouter({
|
||||
register: publicProcedure
|
||||
.input(
|
||||
z.object({
|
||||
user: userSchema,
|
||||
password: passwordSchema,
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const {
|
||||
password,
|
||||
user: { email, name },
|
||||
} = input;
|
||||
// Check if user already exists
|
||||
try {
|
||||
const existingUser = await ctx.db.query.users.findFirst({
|
||||
where: eq(users.email, email),
|
||||
});
|
||||
// export const authRouter = createTRPCRouter({
|
||||
// register: publicProcedure
|
||||
// .input(
|
||||
// z.object({
|
||||
// user: userSchema,
|
||||
// password: passwordSchema,
|
||||
// }),
|
||||
// )
|
||||
// .mutation(async ({ ctx, input }) => {
|
||||
// const {
|
||||
// password,
|
||||
// user: { email, name },
|
||||
// } = input;
|
||||
// // Check if user already exists
|
||||
// try {
|
||||
// const existingUser = await ctx.db.query.users.findFirst({
|
||||
// where: eq(users.email, email),
|
||||
// });
|
||||
|
||||
if (existingUser) {
|
||||
return { success: false, message: "User already exists" };
|
||||
}
|
||||
// if (existingUser) {
|
||||
// return { success: false, message: "User already exists" };
|
||||
// }
|
||||
|
||||
// Hash the password (12 is a good cost factor)
|
||||
const hashedPassword = await argon.hash(password);
|
||||
// // Hash the password (12 is a good cost factor)
|
||||
// const hashedPassword = await argon.hash(password);
|
||||
|
||||
// Create user in database
|
||||
const [user] = await ctx.db
|
||||
.insert(users)
|
||||
.values({
|
||||
name,
|
||||
email,
|
||||
password: hashedPassword,
|
||||
})
|
||||
.returning({ id: users.id });
|
||||
console.log(user);
|
||||
// // Create user in database
|
||||
// const [user] = await ctx.db
|
||||
// .insert(users)
|
||||
// .values({
|
||||
// name,
|
||||
// email,
|
||||
// password: hashedPassword,
|
||||
// })
|
||||
// .returning({ id: users.id });
|
||||
// console.log(user);
|
||||
|
||||
if (user) {
|
||||
return { success: true, message: "User created successfully" };
|
||||
}
|
||||
return { success: false, message: "Error creating user" };
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return { success: false, message: "Error creating user" };
|
||||
}
|
||||
}),
|
||||
});
|
||||
// if (user) {
|
||||
// return { success: true, message: "User created successfully" };
|
||||
// }
|
||||
// return { success: false, message: "Error creating user" };
|
||||
// } catch (e) {
|
||||
// console.error(e);
|
||||
// return { success: false, message: "Error creating user" };
|
||||
// }
|
||||
// }),
|
||||
// });
|
||||
|
||||
@ -85,6 +85,7 @@ export const categoryRouter = createTRPCRouter({
|
||||
name: true,
|
||||
slug: true,
|
||||
createdAt: true,
|
||||
id: true,
|
||||
},
|
||||
})) as Category[];
|
||||
}),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user