dev
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { ExclamationTriangleIcon } from "@radix-ui/react-icons";
|
||||
import { ReasonForm } from "(app)/admin/penalty/_components/form";
|
||||
import { prisma } from "@repo/db";
|
||||
import { Error } from "_components/Error";
|
||||
import { Shield } from "lucide-react";
|
||||
|
||||
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
|
||||
const { id } = await params;
|
||||
@@ -15,16 +16,62 @@ export default async function Page({ params }: { params: Promise<{ id: string }>
|
||||
},
|
||||
});
|
||||
|
||||
if (!penalty) return <Error statusCode={404} title="User not found" />;
|
||||
const userReports = await prisma.report.findMany({
|
||||
where: {
|
||||
reportedUserId: penalty?.User.id,
|
||||
},
|
||||
include: {
|
||||
Reported: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!penalty) return <Error statusCode={404} title="Penalty not found" />;
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-6 gap-4">
|
||||
<div className="col-span-full">
|
||||
<p className="text-2xl font-semibold text-left flex items-center gap-2">
|
||||
<ExclamationTriangleIcon className="w-5 h-5" />
|
||||
<Shield className="w-5 h-5" />
|
||||
Strafe #{penalty.id}
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-span-6 md:col-span-3">
|
||||
<div className="card bg-base-200 shadow-md p-4">
|
||||
<p className="text-lg font-semibold mb-2">Details</p>
|
||||
<hr className="mb-4" />
|
||||
<p>
|
||||
<span className="font-semibold">Benutzer:</span> {penalty.User.firstname}{" "}
|
||||
{penalty.User.lastname} ({penalty.User.publicId})
|
||||
</p>
|
||||
<p>
|
||||
<span className="font-semibold">Erstellt von:</span> {penalty.CreatedUser.firstname}{" "}
|
||||
{penalty.CreatedUser.lastname} ({penalty.CreatedUser.publicId})
|
||||
</p>
|
||||
<p>
|
||||
<span className="font-semibold">Typ:</span> {penalty.type}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<span className="font-semibold">Erstellt am:</span>{" "}
|
||||
{new Date(penalty.timestamp).toLocaleString("de-DE", {
|
||||
dateStyle: "medium",
|
||||
timeStyle: "short",
|
||||
})}
|
||||
</p>
|
||||
{penalty.until && (
|
||||
<p>
|
||||
<span className="font-semibold">Gültig bis:</span>{" "}
|
||||
{new Date(penalty.until).toLocaleString("de-DE", {
|
||||
dateStyle: "medium",
|
||||
timeStyle: "short",
|
||||
})}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="card bg-base-200 shadow-xl mb-4 col-span-6 xl:col-span-3">
|
||||
<ReasonForm penalty={penalty} userReports={userReports} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
"use client";
|
||||
import { editPenalty } from "(app)/admin/penalty/actions";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Penalty, Report, User } from "@repo/db";
|
||||
import { PenaltyOptionalDefaults, PenaltyOptionalDefaultsSchema } from "@repo/db/zod";
|
||||
import { Button } from "_components/ui/Button";
|
||||
import { Switch } from "_components/ui/Switch";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
export const ReasonForm = ({
|
||||
penalty,
|
||||
userReports: suerReportsOfDay,
|
||||
}: {
|
||||
penalty: Penalty;
|
||||
userReports: (Report & { Reported: User })[];
|
||||
}) => {
|
||||
const form = useForm<PenaltyOptionalDefaults>({
|
||||
defaultValues: penalty,
|
||||
resolver: zodResolver(PenaltyOptionalDefaultsSchema),
|
||||
});
|
||||
const isLoading = form.formState.isSubmitting;
|
||||
|
||||
return (
|
||||
<>
|
||||
<form
|
||||
className="card-body"
|
||||
onSubmit={form.handleSubmit(async (penalty) => {
|
||||
if (!penalty.id) return;
|
||||
const newPenalty = await editPenalty(penalty.id, penalty);
|
||||
form.reset(newPenalty);
|
||||
})}
|
||||
>
|
||||
<h2 className="card-title">Begründung</h2>
|
||||
|
||||
<div>
|
||||
<textarea className="textarea textarea-bordered w-full" {...form.register("reason")} />
|
||||
{form.formState.errors.reason && (
|
||||
<p className="text-error">{form.formState.errors.reason.message}</p>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className="tooltip tooltip-bottom"
|
||||
data-tip="Wenn diese Option aktiviert ist, wird die Strafe so behandelt, als wäre sie abgelaufen."
|
||||
>
|
||||
<Switch
|
||||
form={form}
|
||||
name="suspended"
|
||||
label="Strafe aussetzen"
|
||||
className={form.watch("suspended") ? "toggle-error text-error" : ""}
|
||||
/>
|
||||
</div>
|
||||
<fieldset className="fieldset">
|
||||
<legend className="fieldset-legend text-left">Zugeordneter Report</legend>
|
||||
<select
|
||||
className="select select-bordered w-full"
|
||||
{...form.register("reportId", {
|
||||
valueAsNumber: true,
|
||||
})}
|
||||
>
|
||||
<option value="">Kein Report</option>
|
||||
{suerReportsOfDay.map((report) => (
|
||||
<option key={report.id} value={report.id}>
|
||||
Report #{report.id} - {report.reportedUserRole} - {report.Reported.firstname}{" "}
|
||||
{report.Reported.lastname} ({report.Reported.publicId})
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<p className="label">
|
||||
Bitte währe ein Report aus damit diese Maßname bei dem Report angezeigt wird
|
||||
</p>
|
||||
</fieldset>
|
||||
<Button
|
||||
className="btn-primary"
|
||||
type="submit"
|
||||
disabled={isLoading || !form.formState.isDirty}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
Speichern
|
||||
</Button>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -6,3 +6,12 @@ export const addPenalty = async (data: Prisma.PenaltyCreateInput) => {
|
||||
data,
|
||||
});
|
||||
};
|
||||
|
||||
export const editPenalty = async (id: number, data: Prisma.PenaltyUpdateInput) => {
|
||||
return await prisma.penalty.update({
|
||||
where: {
|
||||
id: id,
|
||||
},
|
||||
data,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,11 +1,92 @@
|
||||
"use client";
|
||||
import { Eye, LockKeyhole, RedoDot, Timer } from "lucide-react";
|
||||
import { LockKeyhole, RedoDot, Shield, Timer, TriangleAlert } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { PaginatedTable } from "_components/PaginatedTable";
|
||||
import { Penalty, PenaltyType, Report, User } from "@repo/db";
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import { formatDistance } from "date-fns";
|
||||
import { de } from "date-fns/locale";
|
||||
import { cn } from "../../../../helper/cn";
|
||||
|
||||
export const penaltyColumns: ColumnDef<Penalty & { Report: Report }>[] = [
|
||||
{
|
||||
accessorKey: "type",
|
||||
header: "Typ",
|
||||
|
||||
cell: ({ row }) => {
|
||||
switch (row.getValue("type") as PenaltyType) {
|
||||
case "KICK":
|
||||
return (
|
||||
<div
|
||||
className={cn("text-warning flex gap-3", row.original.suspended && "text-gray-400")}
|
||||
>
|
||||
<RedoDot />
|
||||
Kick {row.original.suspended && "(ausgesetzt)"}
|
||||
</div>
|
||||
);
|
||||
case "TIME_BAN": {
|
||||
const length = formatDistance(
|
||||
new Date(row.original.timestamp),
|
||||
new Date(row.original.until || Date.now()),
|
||||
{ locale: de },
|
||||
);
|
||||
return (
|
||||
<div
|
||||
className={cn("text-warning flex gap-3", row.original.suspended && "text-gray-400")}
|
||||
>
|
||||
<Timer />
|
||||
Zeit Sperre ({length}) {row.original.suspended && "(ausgesetzt)"}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
case "BAN":
|
||||
return (
|
||||
<div className={cn("text-error flex gap-3", row.original.suspended && "text-gray-400")}>
|
||||
<LockKeyhole /> Bann {row.original.suspended && "(ausgesetzt)"}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "CreatedUser",
|
||||
header: "Bestraft durch",
|
||||
cell: ({ row }) => {
|
||||
const user = row.getValue("CreatedUser") as User;
|
||||
return `${user.firstname} ${user.lastname} (${user.publicId})`;
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "timestamp",
|
||||
header: "Time",
|
||||
cell: ({ row }) => new Date(row.getValue("timestamp")).toLocaleString(),
|
||||
},
|
||||
{
|
||||
accessorKey: "actions",
|
||||
header: "Actions",
|
||||
cell: ({ row }) => {
|
||||
const report = row.original.Report;
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
<Link href={`/admin/penalty/${row.original.id}`}>
|
||||
<button className="btn btn-sm btn-outline btn-info flex items-center gap-2">
|
||||
<Shield className="w-4 h-4" />
|
||||
Anzeigen
|
||||
</button>
|
||||
</Link>
|
||||
{report && (
|
||||
<Link href={`/admin/report/${report.id}`}>
|
||||
<button className="btn btn-sm btn-outliney flex items-center gap-2">
|
||||
<TriangleAlert className="w-4 h-4" />
|
||||
Report Anzeigen
|
||||
</button>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default function ReportPage() {
|
||||
return (
|
||||
@@ -15,74 +96,7 @@ export default function ReportPage() {
|
||||
CreatedUser: true,
|
||||
Report: true,
|
||||
}}
|
||||
columns={
|
||||
[
|
||||
{
|
||||
accessorKey: "type",
|
||||
header: "Typ",
|
||||
|
||||
cell: ({ row }) => {
|
||||
switch (row.getValue("type") as PenaltyType) {
|
||||
case "KICK":
|
||||
return (
|
||||
<div className="text-warning flex gap-3">
|
||||
<RedoDot />
|
||||
Kick
|
||||
</div>
|
||||
);
|
||||
case "TIME_BAN": {
|
||||
const length = formatDistance(
|
||||
new Date(row.original.timestamp),
|
||||
new Date(row.original.until || Date.now()),
|
||||
{ locale: de },
|
||||
);
|
||||
return (
|
||||
<div className="text-warning flex gap-3">
|
||||
<Timer />
|
||||
Zeit Sperre ({length})
|
||||
</div>
|
||||
);
|
||||
}
|
||||
case "BAN":
|
||||
return (
|
||||
<div className="text-error flex gap-3">
|
||||
<LockKeyhole /> Bann
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "CreatedUser",
|
||||
header: "Bestraft durch",
|
||||
cell: ({ row }) => {
|
||||
const user = row.getValue("CreatedUser") as User;
|
||||
return `${user.firstname} ${user.lastname} (${user.publicId})`;
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "timestamp",
|
||||
header: "Time",
|
||||
cell: ({ row }) => new Date(row.getValue("timestamp")).toLocaleString(),
|
||||
},
|
||||
{
|
||||
accessorKey: "actions",
|
||||
header: "Actions",
|
||||
cell: ({ row }) => {
|
||||
const report = row.original.Report;
|
||||
if (!report[0]) return null;
|
||||
return (
|
||||
<Link href={`/admin/report/${report[0].id}`}>
|
||||
<button className="btn btn-sm btn-outline btn-info flex items-center gap-2">
|
||||
<Eye className="w-4 h-4" />
|
||||
Report Anzeigen
|
||||
</button>
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
},
|
||||
] as ColumnDef<Penalty & { Report: Report[] }>[]
|
||||
}
|
||||
columns={penaltyColumns}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user