Bugfixes + Manuelle reports #127
111
apps/hub/app/(app)/admin/report/_components/NewReport.tsx
Normal file
111
apps/hub/app/(app)/admin/report/_components/NewReport.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
"use client";
|
||||
import { createReport } from "(app)/admin/report/actions";
|
||||
import { getUser } from "(app)/admin/user/action";
|
||||
import { Prisma } from "@repo/db";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Select } from "_components/ui/Select";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { TriangleAlert } from "lucide-react";
|
||||
import toast from "react-hot-toast";
|
||||
import { Button } from "@repo/shared-components";
|
||||
|
||||
export const NewReportForm = ({
|
||||
defaultValues,
|
||||
}: {
|
||||
defaultValues?: Partial<Prisma.XOR<Prisma.ReportCreateInput, Prisma.ReportUncheckedCreateInput>>;
|
||||
}) => {
|
||||
const session = useSession();
|
||||
const [search, setSearch] = useState("");
|
||||
const { data: user } = useQuery({
|
||||
queryKey: ["newReport"],
|
||||
queryFn: async () =>
|
||||
getUser({
|
||||
OR: [
|
||||
{ firstname: { contains: search, mode: "insensitive" } },
|
||||
{ lastname: { contains: search, mode: "insensitive" } },
|
||||
{ publicId: { contains: search, mode: "insensitive" } },
|
||||
{ id: defaultValues?.reportedUserId || "" },
|
||||
],
|
||||
}),
|
||||
enabled: search.length > 0,
|
||||
refetchOnWindowFocus: false,
|
||||
});
|
||||
const router = useRouter();
|
||||
const form = useForm<Prisma.ReportUncheckedCreateInput>({
|
||||
defaultValues: {
|
||||
text: "",
|
||||
reportedUserRole: "USER",
|
||||
senderUserId: session.data?.user.id,
|
||||
...defaultValues,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<form
|
||||
className="flex flex-wrap gap-3"
|
||||
onSubmit={form.handleSubmit(async (values) => {
|
||||
console.log("Form submitted with values:", values);
|
||||
const newReport = await createReport(values);
|
||||
toast.success("Report erfolgreich erstellt!");
|
||||
router.push(`/admin/report/${newReport.id}`);
|
||||
})}
|
||||
>
|
||||
<div className="card bg-base-200 flex-1 basis-[800px] shadow-xl">
|
||||
<div className="card-body">
|
||||
<h2 className="card-title">
|
||||
<TriangleAlert /> Neuen Report erstellen
|
||||
</h2>
|
||||
<Select
|
||||
form={form}
|
||||
options={
|
||||
user?.map((u) => ({
|
||||
label: `${u.firstname} ${u.lastname} (${u.publicId})`,
|
||||
value: u.id,
|
||||
})) || [
|
||||
{
|
||||
label: "Kein Nutzer gefunden",
|
||||
value: "",
|
||||
disabled: true,
|
||||
},
|
||||
]
|
||||
}
|
||||
onInputChange={(v) => setSearch(v)}
|
||||
name="reportedUserId"
|
||||
label="Nutzer"
|
||||
/>
|
||||
<Select
|
||||
form={form}
|
||||
options={[
|
||||
{ label: "Nutzer", value: "Nutzer" },
|
||||
{ label: "Pilot", value: "Pilot" },
|
||||
{ label: "Disponent", value: "Disponent" },
|
||||
]}
|
||||
name="reportedUserRole"
|
||||
label="Rolle des Nutzers"
|
||||
/>
|
||||
<textarea
|
||||
{...form.register("text")}
|
||||
className="textarea w-full"
|
||||
placeholder="Beschreibe den Vorfall"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card bg-base-200 flex-1 basis-[800px] shadow-xl">
|
||||
<div className="card-body">
|
||||
<div className="flex w-full gap-4">
|
||||
<Button
|
||||
isLoading={form.formState.isSubmitting}
|
||||
type="submit"
|
||||
className="btn btn-primary flex-1"
|
||||
>
|
||||
Speichern
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
@@ -26,15 +26,15 @@ export const ReportSenderInfo = ({
|
||||
return (
|
||||
<div className="card-body">
|
||||
<h2 className="card-title">
|
||||
<Link href={`/admin/user/${Reported?.id}`} className=" link link-hover">
|
||||
<Link href={`/admin/user/${Reported?.id}`} className="link link-hover">
|
||||
{Reported?.firstname} {Reported?.lastname} ({Reported?.publicId}) als{" "}
|
||||
</Link>
|
||||
<span className="text-primary">{report.reportedUserRole}</span>
|
||||
</h2>
|
||||
<div className="textarea w-full text-left">{report.text}</div>
|
||||
<Link
|
||||
href={`/admin/user/${Reported?.id}`}
|
||||
className="text-sm text-gray-600 text-right link link-hover"
|
||||
href={`/admin/user/${Sender?.id}`}
|
||||
className="link link-hover text-right text-sm text-gray-600"
|
||||
>
|
||||
gemeldet von Nutzer {Sender?.firstname} {Sender?.lastname} ({Sender?.publicId}) am{" "}
|
||||
{new Date(report.timestamp).toLocaleString()}
|
||||
@@ -82,7 +82,7 @@ export const ReportAdmin = ({
|
||||
>
|
||||
<h2 className="card-title">Staff Kommentar</h2>
|
||||
<textarea {...form.register("reviewerComment")} className="textarea w-full" placeholder="" />
|
||||
<p className="text-sm text-gray-600 text-right">
|
||||
<p className="text-right text-sm text-gray-600">
|
||||
{report.Reviewer &&
|
||||
`Kommentar von ${Reviewer?.firstname} ${Reviewer?.lastname} (${Reviewer?.publicId})`}
|
||||
</p>
|
||||
@@ -141,7 +141,7 @@ export const ReportPenalties = ({
|
||||
return (
|
||||
<div className="card-body">
|
||||
<h2 className="card-title">
|
||||
<Shield className="w-5 h-5" /> Strafen zu diesem Report
|
||||
<Shield className="h-5 w-5" /> Strafen zu diesem Report
|
||||
</h2>
|
||||
<PaginatedTable
|
||||
prismaModel="penalty"
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
"use server";
|
||||
import { Prisma, prisma } from "@repo/db";
|
||||
|
||||
export const editReport = async (
|
||||
id: number,
|
||||
data: Prisma.ReportUncheckedUpdateInput,
|
||||
) => {
|
||||
export const editReport = async (id: number, data: Prisma.ReportUncheckedUpdateInput) => {
|
||||
return await prisma.report.update({
|
||||
where: {
|
||||
id: id,
|
||||
@@ -12,3 +9,11 @@ export const editReport = async (
|
||||
data,
|
||||
});
|
||||
};
|
||||
|
||||
export const createReport = async (
|
||||
data: Prisma.XOR<Prisma.ReportCreateInput, Prisma.ReportUncheckedCreateInput>,
|
||||
) => {
|
||||
return await prisma.report.create({
|
||||
data,
|
||||
});
|
||||
};
|
||||
|
||||
21
apps/hub/app/(app)/admin/report/new/page.tsx
Normal file
21
apps/hub/app/(app)/admin/report/new/page.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { NewReportForm } from "(app)/admin/report/_components/NewReport";
|
||||
|
||||
const Page = async ({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams?: {
|
||||
[key: string]: string | undefined;
|
||||
};
|
||||
}) => {
|
||||
const params = await searchParams;
|
||||
console.log("searchParams", params);
|
||||
return (
|
||||
<NewReportForm
|
||||
defaultValues={{
|
||||
reportedUserId: params?.reportedUserId || "",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
Eye,
|
||||
LockKeyhole,
|
||||
PlaneIcon,
|
||||
Plus,
|
||||
ShieldUser,
|
||||
Timer,
|
||||
Trash2,
|
||||
@@ -484,9 +485,16 @@ export const UserPenalties = ({ user }: { user: User }) => {
|
||||
export const UserReports = ({ user }: { user: User }) => {
|
||||
return (
|
||||
<div className="card-body">
|
||||
<h2 className="card-title">
|
||||
<ExclamationTriangleIcon className="h-5 w-5" /> Nutzer Reports
|
||||
</h2>
|
||||
<div className="card-title flex justify-between">
|
||||
<h2 className="flex items-center gap-2">
|
||||
<ExclamationTriangleIcon className="h-5 w-5" /> Nutzer Reports
|
||||
</h2>
|
||||
<Link href={`/admin/report/new?reportedUserId=${user.id}`}>
|
||||
<button className={"btn btn-xs btn-square btn-soft cursor-pointer"}>
|
||||
<Plus />
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
<PaginatedTable
|
||||
prismaModel="report"
|
||||
filter={{
|
||||
|
||||
@@ -3,6 +3,12 @@ import { prisma, Prisma } from "@repo/db";
|
||||
import bcrypt from "bcryptjs";
|
||||
import { sendMailByTemplate } from "../../../../helper/mail";
|
||||
|
||||
export const getUser = async (where: Prisma.UserWhereInput) => {
|
||||
return await prisma.user.findMany({
|
||||
where,
|
||||
});
|
||||
};
|
||||
|
||||
export const editUser = async (id: string, data: Prisma.UserUpdateInput) => {
|
||||
return await prisma.user.update({
|
||||
where: {
|
||||
|
||||
Reference in New Issue
Block a user