diff --git a/apps/dispatch-server/routes/report.ts b/apps/dispatch-server/routes/report.ts new file mode 100644 index 00000000..00ea447c --- /dev/null +++ b/apps/dispatch-server/routes/report.ts @@ -0,0 +1,24 @@ +import { Router } from "express"; + +import { prisma } from "@repo/db"; + +const router = Router(); + +router.put("/", async (req, res) => { + try { + const report = await prisma.report.create({ + data: req.body, + }); + + // TODO: send link to report on admin page to user + + res.json(report); + } catch (error) { + res.status(500).json({ + message: "Error creating report", + error: error instanceof Error ? error.message : String(error), + }); + } +}); + +export default router; diff --git a/apps/dispatch-server/routes/router.ts b/apps/dispatch-server/routes/router.ts index be4ad1af..968cf6cd 100644 --- a/apps/dispatch-server/routes/router.ts +++ b/apps/dispatch-server/routes/router.ts @@ -4,6 +4,7 @@ import dispatcherRotuer from "./dispatcher"; import missionRouter from "./mission"; import statusRouter from "./status"; import aircraftsRouter from "./aircraft"; +import reportRouter from "./report"; const router = Router(); @@ -12,5 +13,6 @@ router.use("/dispatcher", dispatcherRotuer); router.use("/mission", missionRouter); router.use("/status", statusRouter); router.use("/aircrafts", aircraftsRouter); +router.use("/report", reportRouter); export default router; diff --git a/apps/dispatch/app/_components/left/Report.tsx b/apps/dispatch/app/_components/left/Report.tsx index 6cb79038..14573c5a 100644 --- a/apps/dispatch/app/_components/left/Report.tsx +++ b/apps/dispatch/app/_components/left/Report.tsx @@ -3,11 +3,12 @@ import { ExclamationTriangleIcon, PaperPlaneIcon } from "@radix-ui/react-icons"; import { useSession } from "next-auth/react"; import { useEffect, useState } from "react"; import { cn } from "helpers/cn"; -import { serverApi } from "helpers/axios"; +import { toast } from "react-hot-toast"; import { useLeftMenuStore } from "_store/leftMenuStore"; import { asPublicUser } from "@repo/db"; import { useQuery } from "@tanstack/react-query"; import { getConnectedUserAPI } from "querys/connected-user"; +import { sendReportAPI } from "querys/report"; export const Report = () => { const { setChatOpen, setReportTabOpen, reportTabOpen, setOwnId } = @@ -106,18 +107,18 @@ export const Report = () => { e.preventDefault(); if (message.length < 1 || !selectedPlayer) return; setSending(true); - serverApi("/report", { - method: "POST", - data: { - message, - to: selectedPlayer, - }, + sendReportAPI({ + text: message, + senderUserId: session.data!.user.id, + reportedUserId: selectedPlayer, }) .then(() => { + toast.success("Report gesendet"); setMessage(""); setSending(false); }) - .catch(() => { + .catch((err) => { + toast.error(`Fehler beim Senden des Reports: ${err}`); setSending(false); }); }} diff --git a/apps/dispatch/app/querys/report.ts b/apps/dispatch/app/querys/report.ts new file mode 100644 index 00000000..539a43f6 --- /dev/null +++ b/apps/dispatch/app/querys/report.ts @@ -0,0 +1,19 @@ +import { Prisma, Report } from "@repo/db"; +import { serverApi } from "helpers/axios"; + +export const sendReportAPI = async ( + report: + | (Prisma.Without< + Prisma.ReportCreateInput, + Prisma.ReportUncheckedCreateInput + > & + Prisma.ReportUncheckedCreateInput) + | (Prisma.Without< + Prisma.ReportUncheckedCreateInput, + Prisma.ReportCreateInput + > & + Prisma.ReportCreateInput), +) => { + const repsonse = await serverApi.put("/report", report); + return repsonse.data as Report; +}; diff --git a/apps/hub/app/(app)/admin/report/[id]/page.tsx b/apps/hub/app/(app)/admin/report/[id]/page.tsx index f16cb7aa..fa85447c 100644 --- a/apps/hub/app/(app)/admin/report/[id]/page.tsx +++ b/apps/hub/app/(app)/admin/report/[id]/page.tsx @@ -1,81 +1,44 @@ -"use client"; -import { useEffect, useState } from "react"; -import { Eye } from "lucide-react"; -import { fetchReportDetails, handleMarkAsResolved } from "../actions"; +import { ExclamationTriangleIcon } from "@radix-ui/react-icons"; +import { prisma } from "@repo/db"; +import { Error } from "_components/Error"; +import { + ReportAdmin, + ReportSenderInfo, +} from "(app)/admin/report/_components/form"; -export default function ReportDetailsPage({ - params: paramsPromise, +export default async function ReportDetailsPage({ + params, }: { - params: Promise<{ id: number }>; + params: { id: string }; }) { - const [params, setParams] = useState<{ id: number } | null>(null); - const [report, setReport] = useState(null); - const [loading, setLoading] = useState(true); + const { id } = await params; - useEffect(() => { - async function unwrapParams() { - const resolvedParams = await paramsPromise; - setParams(resolvedParams); - } - unwrapParams(); - }, [paramsPromise]); + const report = await prisma.report.findUnique({ + where: { + id: Number(id), + }, + include: { + Reported: true, + Reviewer: true, + Sender: true, + }, + }); - useEffect(() => { - if (!params) return; - - async function loadReport() { - if (!params) return; - const fetchedReport = await fetchReportDetails(parseInt(params.id)); - setReport(fetchedReport); - setLoading(false); - } - loadReport(); - }, [params]); - - if (!params || loading) return
Loading...
; - if (!report) return
Report not found
; + if (!report) return ; return ( -
-

- Report Details -

-
-
-

- Sender: {report.sender.firstname} {report.sender.lastname} ( - {report.sender.publicId}) -

-

- Reported: {report.reported.firstname}{" "} - {report.reported.lastname} ({report.reported.publicId}) -

-

- Timestamp: {new Date(report.timestamp).toLocaleString()} -

-
-
-

- Message: -

-

{report.text}

-
+
+
+

+ + Report #{report.id} +

-
- - +
+ +
+
+
); diff --git a/apps/hub/app/(app)/admin/report/_components/form.tsx b/apps/hub/app/(app)/admin/report/_components/form.tsx new file mode 100644 index 00000000..a94afa76 --- /dev/null +++ b/apps/hub/app/(app)/admin/report/_components/form.tsx @@ -0,0 +1,138 @@ +"use client"; +import { editReport } from "(app)/admin/report/actions"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { Report as IReport, User } from "@repo/db"; +import { ReportSchema, Report as IReportZod } from "@repo/db/zod"; +import { Button } from "_components/ui/Button"; +import { Switch } from "_components/ui/Switch"; +import { Trash } from "lucide-react"; +import { useSession } from "next-auth/react"; +import Link from "next/link"; +import { useRouter } from "next/navigation"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import toast from "react-hot-toast"; + +export const ReportSenderInfo = ({ + report, +}: { + report: IReport & { + Reported?: User; + Sender?: User; + }; +}) => { + const { Reported, Sender } = report; + return ( +
+ + {Reported?.firstname} {Reported?.lastname} ({Reported?.publicId}) + +
{report.text}
+ + Meldet Nutzer {Sender?.firstname} {Sender?.lastname} ({Sender?.publicId} + ) am {new Date(report.timestamp).toLocaleString()} + +
+ ); +}; + +export const ReportAdmin = ({ + report, +}: { + report: IReport & { + Reported?: User; + Sender?: User; + Reviewer?: User | null; + }; +}) => { + const { Reviewer } = report; + const [isEditLoading, setIsEditLoading] = useState(false); + + const session = useSession(); + const router = useRouter(); + const form = useForm({ + resolver: zodResolver(ReportSchema), + defaultValues: report, + }); + return ( +
{ + setIsEditLoading(true); + const newReport = await editReport(values.id, { + reviewerUserId: session.data?.user.id, + reviewerComment: values.reviewerComment, + reviewed: values.reviewed, + }); + form.reset(newReport); + setIsEditLoading(false); + router.refresh(); + toast.success("Deine Änderungen wurden gespeichert!", { + style: { + background: "var(--color-base-100)", + color: "var(--color-base-content)", + }, + }); + })} + > +

Staff Kommentar

+