Penalty Übersicht für Nutzer und Penalty-Log
This commit is contained in:
@@ -13,7 +13,6 @@ export const Penalty = async () => {
|
|||||||
type: "TIME_BAN",
|
type: "TIME_BAN",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
console.log("Open Penaltys:", openPenaltys);
|
|
||||||
if (!openPenaltys[0]) {
|
if (!openPenaltys[0]) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
30
apps/hub/app/(app)/admin/penalty/[id]/page.tsx
Normal file
30
apps/hub/app/(app)/admin/penalty/[id]/page.tsx
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { ExclamationTriangleIcon } from "@radix-ui/react-icons";
|
||||||
|
import { prisma } from "@repo/db";
|
||||||
|
import { Error } from "_components/Error";
|
||||||
|
|
||||||
|
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
|
||||||
|
const { id } = await params;
|
||||||
|
|
||||||
|
const penalty = await prisma.penalty.findUnique({
|
||||||
|
where: {
|
||||||
|
id: Number(id),
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
User: true,
|
||||||
|
CreatedUser: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!penalty) return <Error statusCode={404} title="User 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" />
|
||||||
|
Strafe #{penalty.id}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
8
apps/hub/app/(app)/admin/penalty/actions.ts
Normal file
8
apps/hub/app/(app)/admin/penalty/actions.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
"use server";
|
||||||
|
import { Prisma, prisma } from "@repo/db";
|
||||||
|
|
||||||
|
export const addPenalty = async (data: Prisma.PenaltyCreateInput) => {
|
||||||
|
return await prisma.penalty.create({
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
};
|
||||||
15
apps/hub/app/(app)/admin/penalty/layout.tsx
Normal file
15
apps/hub/app/(app)/admin/penalty/layout.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
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 = session.user;
|
||||||
|
|
||||||
|
if (!user?.permissions.includes("ADMIN_EVENT"))
|
||||||
|
return <Error title="Keine Berechtigung" statusCode={403} />;
|
||||||
|
|
||||||
|
return <>{children}</>;
|
||||||
|
}
|
||||||
88
apps/hub/app/(app)/admin/penalty/page.tsx
Normal file
88
apps/hub/app/(app)/admin/penalty/page.tsx
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
"use client";
|
||||||
|
import { Eye, LockKeyhole, RedoDot, Timer } 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";
|
||||||
|
|
||||||
|
export default function ReportPage() {
|
||||||
|
return (
|
||||||
|
<PaginatedTable
|
||||||
|
prismaModel="penalty"
|
||||||
|
include={{
|
||||||
|
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[] }>[]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
import { ExclamationTriangleIcon } from "@radix-ui/react-icons";
|
import { ExclamationTriangleIcon } from "@radix-ui/react-icons";
|
||||||
import { prisma } from "@repo/db";
|
import { prisma } from "@repo/db";
|
||||||
import { Error } from "_components/Error";
|
import { Error } from "_components/Error";
|
||||||
import { ReportAdmin, ReportSenderInfo } from "(app)/admin/report/_components/form";
|
import {
|
||||||
|
ReportAdmin,
|
||||||
|
ReportPenalties,
|
||||||
|
ReportSenderInfo,
|
||||||
|
} from "(app)/admin/report/_components/form";
|
||||||
|
|
||||||
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
|
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
|
||||||
const { id } = await params;
|
const { id } = await params;
|
||||||
@@ -33,6 +37,9 @@ export default async function Page({ params }: { params: Promise<{ id: string }>
|
|||||||
<div className="card bg-base-200 shadow-xl mb-4 col-span-6 xl:col-span-3">
|
<div className="card bg-base-200 shadow-xl mb-4 col-span-6 xl:col-span-3">
|
||||||
<ReportAdmin report={report} />
|
<ReportAdmin report={report} />
|
||||||
</div>
|
</div>
|
||||||
|
<div className="card bg-base-200 shadow-xl mb-4 col-span-6">
|
||||||
|
<ReportPenalties report={report} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { editReport } from "(app)/admin/report/actions";
|
import { editReport } from "(app)/admin/report/actions";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { Report as IReport, User } from "@repo/db";
|
import { Report as IReport, Penalty, PenaltyType, Report, User } from "@repo/db";
|
||||||
import { ReportSchema, Report as IReportZod } from "@repo/db/zod";
|
import { ReportSchema, Report as IReportZod } from "@repo/db/zod";
|
||||||
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
import { PaginatedTable } from "_components/PaginatedTable";
|
||||||
import { Button } from "_components/ui/Button";
|
import { Button } from "_components/ui/Button";
|
||||||
import { Switch } from "_components/ui/Switch";
|
import { Switch } from "_components/ui/Switch";
|
||||||
import { Trash } from "lucide-react";
|
import { formatDistance } from "date-fns";
|
||||||
|
import { de } from "date-fns/locale";
|
||||||
|
import { Eye, LockKeyhole, RedoDot, Shield, Timer, Trash } from "lucide-react";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
@@ -24,10 +28,12 @@ export const ReportSenderInfo = ({
|
|||||||
const { Reported, Sender } = report;
|
const { Reported, Sender } = report;
|
||||||
return (
|
return (
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<Link href={`/admin/user/${Reported?.id}`} className="card-title link link-hover">
|
<h2 className="card-title">
|
||||||
{Reported?.firstname} {Reported?.lastname} ({Reported?.publicId}) als{" "}
|
<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>
|
<span className="text-primary">{report.reportedUserRole}</span>
|
||||||
</Link>
|
</h2>
|
||||||
<div className="textarea w-full text-left">{report.text}</div>
|
<div className="textarea w-full text-left">{report.text}</div>
|
||||||
<Link
|
<Link
|
||||||
href={`/admin/user/${Reported?.id}`}
|
href={`/admin/user/${Reported?.id}`}
|
||||||
@@ -85,7 +91,7 @@ export const ReportAdmin = ({
|
|||||||
{report.Reviewer &&
|
{report.Reviewer &&
|
||||||
`Kommentar von ${Reviewer?.firstname} ${Reviewer?.lastname} (${Reviewer?.publicId})`}
|
`Kommentar von ${Reviewer?.firstname} ${Reviewer?.lastname} (${Reviewer?.publicId})`}
|
||||||
</p>
|
</p>
|
||||||
<Switch form={form} name="reviewed" label="Report als geklärt markieren" />
|
<Switch form={form} name="reviewed" label="Report als erledigt markieren" />
|
||||||
<div className="card-actions flex justify-between">
|
<div className="card-actions flex justify-between">
|
||||||
<Button
|
<Button
|
||||||
role="submit"
|
role="submit"
|
||||||
@@ -126,3 +132,109 @@ export const ReportAdmin = ({
|
|||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ReportPenalties = ({
|
||||||
|
report,
|
||||||
|
}: {
|
||||||
|
report: IReport & {
|
||||||
|
Reported?: User;
|
||||||
|
Sender?: User;
|
||||||
|
Reviewer?: User | null;
|
||||||
|
};
|
||||||
|
}) => {
|
||||||
|
if (!report.penaltyId)
|
||||||
|
return (
|
||||||
|
<div className="card-body">
|
||||||
|
<h2 className="card-title">
|
||||||
|
<Shield className="w-5 h-5" /> Strafen zu diesem Report
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm text-gray-600">Es wurden keine Strafen zu diesem Report erfasst.</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="card-body">
|
||||||
|
<h2 className="card-title">
|
||||||
|
<Shield className="w-5 h-5" /> Strafen zu diesem Report
|
||||||
|
</h2>
|
||||||
|
<PaginatedTable
|
||||||
|
prismaModel="penalty"
|
||||||
|
include={{
|
||||||
|
CreatedUser: true,
|
||||||
|
Report: true,
|
||||||
|
}}
|
||||||
|
filter={{
|
||||||
|
id: report.penaltyId,
|
||||||
|
}}
|
||||||
|
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[] }>[]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import {
|
|||||||
ConnectedAircraft,
|
ConnectedAircraft,
|
||||||
ConnectedDispatcher,
|
ConnectedDispatcher,
|
||||||
DiscordAccount,
|
DiscordAccount,
|
||||||
|
Penalty,
|
||||||
|
PenaltyType,
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
Report,
|
Report,
|
||||||
Station,
|
Station,
|
||||||
@@ -31,12 +33,23 @@ import { UserOptionalDefaults, UserOptionalDefaultsSchema } from "@repo/db/zod";
|
|||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { PaginatedTable, PaginatedTableRef } from "_components/PaginatedTable";
|
import { PaginatedTable, PaginatedTableRef } from "_components/PaginatedTable";
|
||||||
import { cn } from "../../../../../../helper/cn";
|
import { cn } from "../../../../../../helper/cn";
|
||||||
import { ChartBarBigIcon, Check, Eye, PlaneIcon, Timer, X } from "lucide-react";
|
import {
|
||||||
|
ChartBarBigIcon,
|
||||||
|
Check,
|
||||||
|
Eye,
|
||||||
|
LockKeyhole,
|
||||||
|
PlaneIcon,
|
||||||
|
RedoDot,
|
||||||
|
Timer,
|
||||||
|
X,
|
||||||
|
} from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { ColumnDef } from "@tanstack/react-table";
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
import { Error } from "_components/Error";
|
import { Error } from "_components/Error";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import { setStandardName } from "../../../../../../helper/discord";
|
import { setStandardName } from "../../../../../../helper/discord";
|
||||||
|
import { de } from "date-fns/locale";
|
||||||
|
import { formatDistance } from "date-fns";
|
||||||
|
|
||||||
interface ProfileFormProps {
|
interface ProfileFormProps {
|
||||||
user: User;
|
user: User;
|
||||||
@@ -300,11 +313,99 @@ export const ConnectionHistory: React.FC<{ user: User }> = ({ user }: { user: Us
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const UserPenalties = ({ user }: { user: User }) => {
|
||||||
|
return (
|
||||||
|
<div className="card-body">
|
||||||
|
<h2 className="card-title">
|
||||||
|
<ExclamationTriangleIcon className="w-5 h-5" /> Nutzer Strafen
|
||||||
|
</h2>
|
||||||
|
<PaginatedTable
|
||||||
|
prismaModel="penalty"
|
||||||
|
include={{
|
||||||
|
CreatedUser: true,
|
||||||
|
Report: true,
|
||||||
|
}}
|
||||||
|
filter={{
|
||||||
|
userId: user.id,
|
||||||
|
}}
|
||||||
|
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[] }>[]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const UserReports = ({ user }: { user: User }) => {
|
export const UserReports = ({ user }: { user: User }) => {
|
||||||
return (
|
return (
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<h2 className="card-title">
|
<h2 className="card-title">
|
||||||
<ExclamationTriangleIcon className="w-5 h-5" /> User Reports
|
<ExclamationTriangleIcon className="w-5 h-5" /> Nutzer Reports
|
||||||
</h2>
|
</h2>
|
||||||
<PaginatedTable
|
<PaginatedTable
|
||||||
prismaModel="report"
|
prismaModel="report"
|
||||||
@@ -433,7 +534,7 @@ export const AdminForm = ({
|
|||||||
role="submit"
|
role="submit"
|
||||||
className="btn-sm btn-wide btn-outline btn-error"
|
className="btn-sm btn-wide btn-outline btn-error"
|
||||||
>
|
>
|
||||||
<HobbyKnifeIcon /> User Sperren
|
<HobbyKnifeIcon /> HUB zugang sperren
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{user.isBanned && (
|
{user.isBanned && (
|
||||||
@@ -451,7 +552,7 @@ export const AdminForm = ({
|
|||||||
role="submit"
|
role="submit"
|
||||||
className="btn-sm btn-wide btn-outline btn-warning"
|
className="btn-sm btn-wide btn-outline btn-warning"
|
||||||
>
|
>
|
||||||
<HobbyKnifeIcon /> User Entperren
|
<HobbyKnifeIcon /> HUB zugang entsperren
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{discordAccount && (
|
{discordAccount && (
|
||||||
@@ -509,8 +610,6 @@ export const AdminForm = ({
|
|||||||
<h2 className="card-title">
|
<h2 className="card-title">
|
||||||
<ExclamationTriangleIcon className="w-5 h-5" /> Reports
|
<ExclamationTriangleIcon className="w-5 h-5" /> Reports
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
{/* TODO: Report summary Here */}
|
|
||||||
<div className="stats flex">
|
<div className="stats flex">
|
||||||
<div className="stat">
|
<div className="stat">
|
||||||
<div className="stat-figure text-primary">
|
<div className="stat-figure text-primary">
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
import { PersonIcon } from "@radix-ui/react-icons";
|
import { PersonIcon } from "@radix-ui/react-icons";
|
||||||
import { prisma } from "@repo/db";
|
import { prisma } from "@repo/db";
|
||||||
import { AdminForm, ConnectionHistory, ProfileForm, UserReports } from "./_components/forms";
|
import {
|
||||||
|
AdminForm,
|
||||||
|
ConnectionHistory,
|
||||||
|
ProfileForm,
|
||||||
|
UserPenalties,
|
||||||
|
UserReports,
|
||||||
|
} from "./_components/forms";
|
||||||
import { Error } from "../../../../_components/Error";
|
import { Error } from "../../../../_components/Error";
|
||||||
|
|
||||||
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
|
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
|
||||||
@@ -115,6 +121,9 @@ export default async function Page({ params }: { params: Promise<{ id: string }>
|
|||||||
<div className="card bg-base-200 shadow-xl mb-4 col-span-6 xl:col-span-6">
|
<div className="card bg-base-200 shadow-xl mb-4 col-span-6 xl:col-span-6">
|
||||||
<UserReports user={user} />
|
<UserReports user={user} />
|
||||||
</div>
|
</div>
|
||||||
|
<div className="card bg-base-200 shadow-xl mb-4 col-span-6 xl:col-span-6">
|
||||||
|
<UserPenalties user={user} />
|
||||||
|
</div>
|
||||||
<div className="card bg-base-200 shadow-xl mb-4 col-span-6 xl:col-span-6">
|
<div className="card bg-base-200 shadow-xl mb-4 col-span-6 xl:col-span-6">
|
||||||
<ConnectionHistory user={user} />
|
<ConnectionHistory user={user} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ export const editUser = async (id: string, data: Prisma.UserUpdateInput) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const addPenalty = async (data: Prisma.PenaltyCreateInput) => {
|
||||||
|
return await prisma.penalty.create({
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const resetPassword = async (id: string) => {
|
export const resetPassword = async (id: string) => {
|
||||||
const array = new Uint8Array(8);
|
const array = new Uint8Array(8);
|
||||||
crypto.getRandomValues(array);
|
crypto.getRandomValues(array);
|
||||||
|
|||||||
@@ -80,6 +80,11 @@ export const VerticalNav = async () => {
|
|||||||
<Link href="/admin/report">Reports</Link>
|
<Link href="/admin/report">Reports</Link>
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
|
{session.user.permissions.includes("ADMIN_USER") && (
|
||||||
|
<li>
|
||||||
|
<Link href="/admin/penalty">Audit-Log</Link>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
"node": ">=18",
|
"node": ">=18",
|
||||||
"pnpm": ">=10"
|
"pnpm": ">=10"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.11.1",
|
"packageManager": "pnpm@10.12.1",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"apps/*",
|
"apps/*",
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ model Penalty {
|
|||||||
reason String
|
reason String
|
||||||
until DateTime?
|
until DateTime?
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
timestamp DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
|
|
||||||
// relations:
|
// relations:
|
||||||
User User @relation(fields: [userId], references: [id])
|
User User @relation(fields: [userId], references: [id])
|
||||||
|
|||||||
14
pnpm-lock.yaml
generated
14
pnpm-lock.yaml
generated
@@ -92,7 +92,7 @@ importers:
|
|||||||
version: 0.5.7(@types/dom-mediacapture-transform@0.1.11)(livekit-client@2.13.3(@types/dom-mediacapture-record@1.0.22))
|
version: 0.5.7(@types/dom-mediacapture-transform@0.1.11)(livekit-client@2.13.3(@types/dom-mediacapture-record@1.0.22))
|
||||||
'@next-auth/prisma-adapter':
|
'@next-auth/prisma-adapter':
|
||||||
specifier: ^1.0.7
|
specifier: ^1.0.7
|
||||||
version: 1.0.7(@prisma/client@6.8.2(prisma@6.8.2(typescript@5.8.3))(typescript@5.8.3))(next-auth@4.24.11(next@15.3.3(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))
|
version: 1.0.7(@prisma/client@6.8.2(prisma@6.8.2(typescript@5.8.3))(typescript@5.8.3))(next-auth@4.24.11(next@15.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))
|
||||||
'@radix-ui/react-icons':
|
'@radix-ui/react-icons':
|
||||||
specifier: ^1.3.2
|
specifier: ^1.3.2
|
||||||
version: 1.3.2(react@19.1.0)
|
version: 1.3.2(react@19.1.0)
|
||||||
@@ -167,7 +167,7 @@ importers:
|
|||||||
version: 15.3.3(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
version: 15.3.3(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
next-auth:
|
next-auth:
|
||||||
specifier: ^4.24.11
|
specifier: ^4.24.11
|
||||||
version: 4.24.11(next@15.3.3(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
version: 4.24.11(next@15.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
npm:
|
npm:
|
||||||
specifier: ^11.4.1
|
specifier: ^11.4.1
|
||||||
version: 11.4.1
|
version: 11.4.1
|
||||||
@@ -325,7 +325,7 @@ importers:
|
|||||||
version: 5.0.1(react-hook-form@7.57.0(react@19.1.0))
|
version: 5.0.1(react-hook-form@7.57.0(react@19.1.0))
|
||||||
'@next-auth/prisma-adapter':
|
'@next-auth/prisma-adapter':
|
||||||
specifier: ^1.0.7
|
specifier: ^1.0.7
|
||||||
version: 1.0.7(@prisma/client@6.8.2(prisma@6.8.2(typescript@5.8.3))(typescript@5.8.3))(next-auth@4.24.11(next@15.3.3(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))
|
version: 1.0.7(@prisma/client@6.8.2(prisma@6.8.2(typescript@5.8.3))(typescript@5.8.3))(next-auth@4.24.11(next@15.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))
|
||||||
'@radix-ui/react-icons':
|
'@radix-ui/react-icons':
|
||||||
specifier: ^1.3.2
|
specifier: ^1.3.2
|
||||||
version: 1.3.2(react@19.1.0)
|
version: 1.3.2(react@19.1.0)
|
||||||
@@ -403,7 +403,7 @@ importers:
|
|||||||
version: 15.3.3(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
version: 15.3.3(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
next-auth:
|
next-auth:
|
||||||
specifier: ^4.24.11
|
specifier: ^4.24.11
|
||||||
version: 4.24.11(next@15.3.3(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
version: 4.24.11(next@15.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
next-remove-imports:
|
next-remove-imports:
|
||||||
specifier: ^1.0.12
|
specifier: ^1.0.12
|
||||||
version: 1.0.12(webpack@5.99.9)
|
version: 1.0.12(webpack@5.99.9)
|
||||||
@@ -6596,10 +6596,10 @@ snapshots:
|
|||||||
'@tybys/wasm-util': 0.9.0
|
'@tybys/wasm-util': 0.9.0
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next-auth/prisma-adapter@1.0.7(@prisma/client@6.8.2(prisma@6.8.2(typescript@5.8.3))(typescript@5.8.3))(next-auth@4.24.11(next@15.3.3(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))':
|
'@next-auth/prisma-adapter@1.0.7(@prisma/client@6.8.2(prisma@6.8.2(typescript@5.8.3))(typescript@5.8.3))(next-auth@4.24.11(next@15.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@prisma/client': 6.8.2(prisma@6.8.2(typescript@5.8.3))(typescript@5.8.3)
|
'@prisma/client': 6.8.2(prisma@6.8.2(typescript@5.8.3))(typescript@5.8.3)
|
||||||
next-auth: 4.24.11(next@15.3.3(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
next-auth: 4.24.11(next@15.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
|
|
||||||
'@next/env@15.3.3': {}
|
'@next/env@15.3.3': {}
|
||||||
|
|
||||||
@@ -10945,7 +10945,7 @@ snapshots:
|
|||||||
|
|
||||||
neo-async@2.6.2: {}
|
neo-async@2.6.2: {}
|
||||||
|
|
||||||
next-auth@4.24.11(next@15.3.3(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
|
next-auth@4.24.11(next@15.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.27.4
|
'@babel/runtime': 7.27.4
|
||||||
'@panva/hkdf': 1.2.1
|
'@panva/hkdf': 1.2.1
|
||||||
|
|||||||
Reference in New Issue
Block a user