Added Discord message for reports, Fixed type on docs

This commit is contained in:
PxlLoewe
2025-06-13 23:50:19 -07:00
parent 999daf17ad
commit 98cc1d6089
9 changed files with 114 additions and 11 deletions

View File

@@ -0,0 +1,67 @@
import { prisma } from "@repo/db";
import { Embed, EmbedBuilder } from "discord.js";
import { Router } from "express";
import client from "modules/discord";
if (!process.env.DISCORD_REPORT_CHANNEL)
throw new Error("DISCORD_REPORT_CHANNEL environment variable is not set.");
const router: Router = Router();
router.post("/admin-embed", async (req, res) => {
const { reportId } = req.body;
if (!reportId) {
res.status(400).json({ error: "reportId is required" });
return;
}
const report = await prisma.report.findUnique({
where: {
id: Number(reportId),
},
include: {
Reported: true,
Sender: true,
},
});
if (!report) {
res.status(404).json({ error: "Report not found" });
return;
}
const embed = new EmbedBuilder()
.setTitle(`Report #${report.id}`)
.setURL(`${process.env.NEXT_PUBLIC_HUB_URL}/admin/report/${report.id}`)
.setDescription(report.text)
.addFields(
{
name: "gemeldeter Nutzer",
value: `${report.Reported?.firstname} ${report.Reported?.lastname} (${report.Reported?.publicId})`,
inline: true,
},
{ name: "angemeldet als", value: report.reportedUserRole, inline: true },
{
name: "gemeldet von",
value: `${report.Sender?.firstname} ${report.Sender?.lastname} (${report.Sender?.publicId})`,
},
)
.setFooter({
text: "",
})
.setTimestamp(new Date(report.timestamp))
.setColor("DarkRed");
const reportsChannel = await client.channels.fetch(process.env.DISCORD_REPORT_CHANNEL!);
if (!reportsChannel || !reportsChannel.isSendable()) {
res.status(500).json({ error: "Reports channel not found or is not a text channel" });
return;
}
const message = await reportsChannel.send({ embeds: [embed] });
message.react("🫡").catch(console.error);
message.react("✅").catch(console.error);
res.json({
message: "Report embed sent to Discord channel",
});
});
export default router;

View File

@@ -1,10 +1,12 @@
import { Router } from "express"; import { Router } from "express";
import memberRouter from "./member"; import memberRouter from "./member";
import helperRouter from "./helper"; import helperRouter from "./helper";
import reportRouter from "./report";
const router: Router = Router(); const router: Router = Router();
router.use("/member", memberRouter); router.use("/member", memberRouter);
router.use("/helper", helperRouter); router.use("/helper", helperRouter);
router.use("/report", reportRouter);
export default router; export default router;

View File

@@ -36,3 +36,13 @@ export const removeRolesFromMember = async (memberId: string, roleIds: string[])
console.error("Error removing roles from member:", error); console.error("Error removing roles from member:", error);
}); });
}; };
export const sendReportEmbed = async (reportId: number) => {
discordAxiosClient
.post("/report/admin-embed", {
reportId,
})
.catch((error) => {
console.error("Error removing roles from member:", error);
});
};

View File

@@ -1,6 +1,7 @@
import { Router } from "express"; import { Router } from "express";
import { prisma } from "@repo/db"; import { prisma } from "@repo/db";
import { sendReportEmbed } from "modules/discord";
const router: Router = Router(); const router: Router = Router();
@@ -11,6 +12,9 @@ router.put("/", async (req, res) => {
}); });
// TODO: send link to report on admin page to user // TODO: send link to report on admin page to user
sendReportEmbed(report.id).catch((error) => {
console.error("Error sending report embed to Discord:", error);
});
res.json(report); res.json(report);
} catch (error) { } catch (error) {

View File

@@ -109,12 +109,16 @@ export const Report = () => {
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
if (message.length < 1 || !selectedPlayer) return; if (message.length < 1 || !selectedPlayer) return;
const dispatcher = filteredDispatcher?.find((d) => d.userId === selectedPlayer) const selectedDispatcher = filteredDispatcher?.find(
? "Disponent" (d) => d.userId === selectedPlayer,
: null; );
const pilot = filteredAircrafts?.find((a) => a.userId === selectedPlayer) const dispatcher = selectedDispatcher ? selectedDispatcher.zone : null;
? "Pilot"
: null; const selectedAircraft = filteredAircrafts?.find(
(a) => a.userId === selectedPlayer,
);
const pilot = selectedAircraft ? selectedAircraft.Station.bosCallsignShort : null;
setSending(true); setSending(true);
sendReportAPI({ sendReportAPI({
text: message, text: message,

View File

@@ -3,7 +3,7 @@
Was ist neu? Quasi alles. Was ist neu? Quasi alles.
Nicht nur die Leitstelle erhält eine V2, nahezu alle Systeme und Vorgänge wurden überarbeitet. Nicht nur die Leitstelle erhält eine V2, nahezu alle Systeme und Vorgänge wurden überarbeitet.
In diesem Beitrag gehen wir auf die wchtigsten einzelheiten ein. In diesem Beitrag gehen wir auf die wichtigsten einzelheiten ein.
### Das neue HUB ### Das neue HUB

View File

@@ -25,7 +25,8 @@ export const ReportSenderInfo = ({
return ( return (
<div className="card-body"> <div className="card-body">
<Link href={`/admin/user/${Reported?.id}`} className="card-title link link-hover"> <Link href={`/admin/user/${Reported?.id}`} className="card-title link link-hover">
{Reported?.firstname} {Reported?.lastname} ({Reported?.publicId}) {Reported?.firstname} {Reported?.lastname} ({Reported?.publicId}) als{" "}
<span className="text-primary">{report.reportedUserRole}</span>
</Link> </Link>
<div className="textarea w-full text-left">{report.text}</div> <div className="textarea w-full text-left">{report.text}</div>
<Link <Link

View File

@@ -1,9 +1,10 @@
"use client"; "use client";
import { Check, Eye, X } from "lucide-react"; import { Check, Eye, ShieldQuestion, X } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import { PaginatedTable } from "_components/PaginatedTable"; import { PaginatedTable } from "_components/PaginatedTable";
import { Report, User } from "@repo/db"; import { Report, User } from "@repo/db";
import { ColumnDef } from "@tanstack/react-table"; import { ColumnDef } from "@tanstack/react-table";
import { Workflow, Plane } from "lucide-react";
export default function ReportPage() { export default function ReportPage() {
return ( return (
@@ -40,6 +41,20 @@ export default function ReportPage() {
return `${user.firstname} ${user.lastname} (${user.publicId})`; return `${user.firstname} ${user.lastname} (${user.publicId})`;
}, },
}, },
{
accessorKey: "reportedUserRole",
header: "Rolle des gemeldeten Nutzers",
cell: ({ row }) => {
const role = row.getValue("reportedUserRole") as string | undefined;
const Icon = role ? (role.startsWith("LST") ? Workflow : Plane) : ShieldQuestion;
return (
<span className="flex items-center gap-2">
<Icon className="w-4 h-4" />
{role || "Unbekannt"}
</span>
);
},
},
{ {
accessorKey: "Reported", accessorKey: "Reported",
header: "Reported", header: "Reported",

View File

@@ -74,8 +74,8 @@ export const PasswortReset = () => {
: ""} : ""}
</p> </p>
<span className="text-sm font-medium flex justify-end"> <span className="text-sm font-medium flex justify-end">
<Link href="/passwort-reset" className="link link-accent link-hover "> <Link href="/login" className="link link-accent link-hover ">
neues Passwort anfordern zum Login
</Link> </Link>
</span> </span>
<div className="card-actions mt-6"> <div className="card-actions mt-6">