Add Admin Reports page #2

This commit is contained in:
nocnico
2025-04-28 22:59:46 +02:00
parent 7670843613
commit 0d7f0ad2b8
6 changed files with 273 additions and 7 deletions

View File

@@ -0,0 +1,82 @@
"use client";
import { useEffect, useState } from "react";
import { Eye } from "lucide-react";
import { fetchReportDetails, handleMarkAsResolved } from "../actions";
export default function ReportDetailsPage({
params: paramsPromise,
}: {
params: Promise<{ id: number }>;
}) {
const [params, setParams] = useState<{ id: number } | null>(null);
const [report, setReport] = useState<any>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function unwrapParams() {
const resolvedParams = await paramsPromise;
setParams(resolvedParams);
}
unwrapParams();
}, [paramsPromise]);
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 <div>Loading...</div>;
if (!report) return <div>Report not found</div>;
return (
<div className="p-4">
<h1 className="text-2xl font-bold mb-4 flex items-center gap-2">
<Eye className="w-6 h-6" /> Report Details
</h1>
<div className="grid grid-cols-2 gap-4">
<div>
<p>
<b>Sender:</b> {report.sender.firstname} {report.sender.lastname} (
{report.sender.publicId})
</p>
<p>
<b>Reported:</b> {report.reported.firstname}{" "}
{report.reported.lastname} ({report.reported.publicId})
</p>
<p>
<b>Timestamp:</b> {new Date(report.timestamp).toLocaleString()}
</p>
</div>
<div>
<p>
<b>Message:</b>
</p>
<p>{report.text}</p>
</div>
</div>
<div className="mt-4 flex gap-2">
<button
className="btn btn-success btn-outline"
onClick={async () => {
await handleMarkAsResolved(report.id);
}}
>
Erledigen
</button>
<button
className="btn btn-primary btn-outline"
onClick={() => window.history.back()}
>
Zurück
</button>
</div>
</div>
);
}

View File

@@ -0,0 +1,36 @@
"use server";
import { prisma } from "@repo/db";
export const markAsResolved = async (id: number) => {
await prisma.reportMessage.update({
where: { id: id },
data: { erledigt: true },
});
};
// New function to handle marking a report as resolved
export const handleMarkAsResolved = async (id: number) => {
try {
await markAsResolved(id);
return { success: true };
} catch (error) {
console.error("Error marking report as resolved:", error);
return { success: false, error: error };
}
};
export const getReports = async () => {
return prisma.reportMessage.findMany({
include: {
sender: true,
reported: true,
},
});
};
export const fetchReportDetails = async (id: number) => {
return prisma.reportMessage.findUnique({
where: { id },
include: { sender: true, reported: true },
});
};

View File

@@ -0,0 +1,24 @@
import { prisma } from "@repo/db";
import { Error } from "_components/Error";
import { getServerSession } from "api/auth/[...nextauth]/auth";
export default async function ReportLayout({
children,
}: {
children: React.ReactNode;
}) {
const session = await getServerSession();
if (!session) return <Error title="Nicht eingeloggt" statusCode={401} />;
const user = await prisma.user.findUnique({
where: {
id: session.user.id,
},
});
if (!user?.permissions.includes("ADMIN_EVENT"))
return <Error title="Keine Berechtigung" statusCode={403} />;
return <>{children}</>;
}

View File

@@ -0,0 +1,122 @@
"use client";
import { useEffect, useState } from "react";
import { Check, Eye, X } from "lucide-react";
import Link from "next/link";
import { getReports, handleMarkAsResolved } from "./actions";
export default function ReportPage() {
const [reports, setReports] = useState<
{
id: number;
timestamp: string;
erledigt: boolean;
sender: {
id: string;
firstname: string;
lastname: string;
publicId: string;
};
reported: {
id: string;
firstname: string;
lastname: string;
publicId: string;
};
}[]
>([]);
useEffect(() => {
const fetchReports = async () => {
const reps = await getReports();
const transformedReports = reps.map((report) => ({
id: report.id,
timestamp: report.timestamp.toISOString(),
erledigt: report.erledigt,
sender: {
id: report.sender.id,
firstname: report.sender.firstname,
lastname: report.sender.lastname,
publicId: report.sender.publicId,
},
reported: {
id: report.reported.id,
firstname: report.reported.firstname,
lastname: report.reported.lastname,
publicId: report.reported.publicId,
},
}));
setReports(transformedReports);
};
fetchReports();
}, []);
return (
<>
<div className="flex items-center gap-2 mb-4">
<Eye className="w-5 h-5" />{" "}
<span className="text-lg font-bold">Reports</span>
</div>
<div className="overflow-x-auto">
<table className="table table-zebra w-full">
<thead>
<tr>
<th>Erledigt</th>
<th>Sender</th>
<th>Reported</th>
<th>Time</th>
<th>ID</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{reports.map((report) => (
<tr key={report.id}>
<td className="text-center">
{report.erledigt ? (
<Check className="text-green-500 w-5 h-5" />
) : (
<X className="text-red-500 w-5 h-5" />
)}
</td>
<td>{`${report.sender.firstname} ${report.sender.lastname} (${report.sender.publicId})`}</td>
<td>{`${report.reported.firstname} ${report.reported.lastname} (${report.reported.publicId})`}</td>
<td>{new Date(report.timestamp).toLocaleString()}</td>
<td>{report.id}</td>
<td>
<div className="flex gap-2">
<Link href={`/admin/report/${report.id}`}>
<button className="btn btn-sm btn-outline btn-info flex items-center gap-2">
<Eye className="w-4 h-4" /> Anzeigen
</button>
</Link>
{!report.erledigt && (
<button
className="btn btn-sm btn-outline btn-success flex items-center gap-2"
onClick={async () => {
const result = await handleMarkAsResolved(report.id);
if (result.success) {
setReports((prevReports) =>
prevReports.map((r) =>
r.id === report.id
? { ...r, erledigt: true }
: r,
),
);
} else {
alert("Error: " + result.error);
}
}}
>
<Check className="w-4 h-4" /> Erledigen
</button>
)}
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
</>
);
}

View File

@@ -57,6 +57,9 @@ export const VerticalNav = () => {
<li>
<Link href="/admin/message">Service Nachrichten</Link>
</li>
<li>
<Link href="/admin/report">Reports</Link>
</li>
</ul>
</details>
</li>

View File

@@ -1,11 +1,10 @@
model ReportMessage {
id Int @id @default(autoincrement())
text String
senderId String
reportedId String
timestamp DateTime @default(now())
reportedName String
senderName String
id Int @id @default(autoincrement())
text String
senderId String
reportedId String
timestamp DateTime @default(now())
erledigt Boolean @default(false)
// relations:
sender User @relation("SentReports", fields: [senderId], references: [id])