completed Account Log
This commit is contained in:
@@ -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>
|
||||
),
|
||||
|
||||
137
apps/hub/app/(app)/admin/user/[id]/_components/AccountLog.tsx
Normal file
137
apps/hub/app/(app)/admin/user/[id]/_components/AccountLog.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user