completed Account Log

This commit is contained in:
PxlLoewe
2026-01-30 16:19:00 +01:00
parent ea8d63ce0b
commit 2154684223
8 changed files with 215 additions and 64 deletions

View File

@@ -12,9 +12,9 @@ export const reportColumns: ColumnDef<Report & { Sender?: User; Reported: User }
return (
<div className="text-center">
{row.getValue("reviewed") ? (
<Check className="text-green-500 w-5 h-5" />
<Check className="h-5 w-5 text-green-500" />
) : (
<X className="text-red-500 w-5 h-5" />
<X className="h-5 w-5 text-red-500" />
)}
</div>
);
@@ -31,13 +31,13 @@ export const reportColumns: ColumnDef<Report & { Sender?: User; Reported: User }
},
{
accessorKey: "reportedUserRole",
header: "Rolle des gemeldeten Nutzers",
header: "Rolle",
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" />
<Icon className="h-4 w-4" />
{role || "Unbekannt"}
</span>
);
@@ -62,7 +62,7 @@ export const reportColumns: ColumnDef<Report & { Sender?: User; Reported: User }
cell: ({ row }) => (
<Link href={`/admin/report/${row.original.id}`}>
<button className="btn btn-sm btn-outline btn-info flex items-center gap-2">
<Eye className="w-4 h-4" /> Anzeigen
<Eye className="h-4 w-4" /> Anzeigen
</button>
</Link>
),

View File

@@ -0,0 +1,137 @@
"use client";
import { Log, Prisma, User } from "@repo/db";
import { cn } from "@repo/shared-components";
import { ColumnDef } from "@tanstack/react-table";
import { PaginatedTable, PaginatedTableRef } from "_components/PaginatedTable";
import { Printer } from "lucide-react";
import Link from "next/link";
import { useRef, useState } from "react";
export const AccountLog = ({ sameIPLogs, userId }: { sameIPLogs: Log[]; userId: string }) => {
const [onlyImportant, setOnlyImportant] = useState(true);
const tableRef = useRef<PaginatedTableRef>(null);
return (
<div className="card-body">
<div className="card-title flex justify-between">
<h2 className="flex items-center gap-2">
<Printer className="h-5 w-5" /> Account Log
</h2>
<p className="text-end text-sm text-gray-500">
Hier werden Logs angezeigt, die dem Nutzer zugeordnet sind oder von der selben IP stammen.
</p>
</div>
<PaginatedTable
ref={tableRef}
rightOfPagination={
<div className="ml-4 flex items-center gap-2">
<input
type="checkbox"
id="ImportantOnly"
checked={onlyImportant}
onChange={() => {
setOnlyImportant(!onlyImportant);
tableRef.current?.refresh();
}}
className="checkbox checkbox-sm"
/>
<label
htmlFor="ImportantOnly"
className="min-w-[210px] cursor-pointer select-none text-sm"
>
Unauffällige Einträge ausblenden
</label>
</div>
}
getFilter={(searchTerm) => {
return {
AND: [
{
OR: [
{ ip: { contains: searchTerm } },
{ browser: { contains: searchTerm } },
{
id: {
in: sameIPLogs
.filter((log) => log.id.toString().includes(searchTerm))
.map((log) => log.id),
},
},
],
},
onlyImportant
? {
OR: [
{
id: {
in: sameIPLogs
.filter((log) => log.id.toString().includes(searchTerm))
.map((log) => log.id),
},
},
{
type: {
in: ["REGISTER", "PROFILE_CHANGE"],
},
},
],
}
: {},
],
} as Prisma.LogWhereInput;
}}
include={{
User: true,
}}
prismaModel={"log"}
columns={
[
{
header: "Zeitstempel",
accessorKey: "timestamp",
cell: (info) => new Date(info.getValue<string>()).toLocaleString("de-DE"),
},
{
header: "Aktion",
accessorKey: "action",
cell: ({ row }) => {
const action = row.original.type;
if (action !== "PROFILE_CHANGE") {
return <span className="text-blue-500">{action}</span>;
} else {
return (
<span className="text-yellow-500">{`${row.original.field} von "${row.original.oldValue}" zu "${row.original.newValue}"`}</span>
);
}
},
},
{
header: "IP-Adresse",
accessorKey: "ip",
},
{
header: "Browser",
accessorKey: "browser",
},
{
header: "Benutzer",
accessorKey: "userId",
cell: ({ row }) => {
return (
<Link
href={`/admin/user/${row.original.userId}`}
className={cn("link", userId !== row.original.userId && "text-red-400")}
>
{row.original.User
? `${row.original.User.firstname} ${row.original.User.lastname} - ${row.original.User.publicId}`
: "Unbekannt"}
</Link>
);
},
},
] as ColumnDef<Log & { User: User }>[]
}
/>
</div>
);
};

View File

@@ -11,6 +11,7 @@ import { Error } from "../../../../_components/Error";
import { getUserPenaltys } from "@repo/shared-components";
import { PaginatedTable } from "_components/PaginatedTable";
import { ColumnDef } from "@tanstack/react-table";
import { AccountLog } from "./_components/AccountLog";
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
@@ -175,44 +176,12 @@ export default async function Page({ params }: { params: Promise<{ id: string }>
/>
</div>
<div className="card bg-base-200 col-span-6 mb-4 shadow-xl xl:col-span-6">
<PaginatedTable
prismaModel={"log"}
columns={
[
{
header: "Zeitstempel",
accessorKey: "timestamp",
cell: (info) => new Date(info.getValue<string>()).toLocaleString("de-DE"),
},
{
header: "Aktion",
accessorKey: "action",
cell: ({ row }) => {
const action = row.original.type;
if (action !== "PROFILE_CHANGE") {
return action;
} else {
return `${row.original.field} von "${row.original.oldValue}" zu "${row.original.newValue}"`;
}
},
},
{
header: "IP-Adresse",
accessorKey: "ip",
},
{
header: "Gerät",
accessorKey: "browser",
},
] as ColumnDef<Log>[]
}
/>
<AccountLog sameIPLogs={sameIpLogs} userId={user.id} />
</div>
<div className="card bg-base-200 col-span-6 mb-4 shadow-xl xl:col-span-6">
<div className="card bg-base-200 col-span-6 mb-4 shadow-xl xl:col-span-3">
<UserReports user={user} />
</div>
<div className="card bg-base-200 col-span-6 mb-4 shadow-xl xl:col-span-6">
<div className="card bg-base-200 col-span-6 mb-4 shadow-xl xl:col-span-3">
<UserPenalties user={user} />
</div>
<div className="card bg-base-200 col-span-6 mb-4 shadow-xl xl:col-span-6">