diff --git a/src/components/admin/pages/event/RefereesList.svelte b/src/components/admin/pages/event/RefereesList.svelte
index 77a0e39..0efa88a 100644
--- a/src/components/admin/pages/event/RefereesList.svelte
+++ b/src/components/admin/pages/event/RefereesList.svelte
@@ -18,20 +18,19 @@
-->
diff --git a/src/components/moderator/layout/NavLinks.svelte b/src/components/moderator/layout/NavLinks.svelte
index 1b540e7..f058c3d 100644
--- a/src/components/moderator/layout/NavLinks.svelte
+++ b/src/components/moderator/layout/NavLinks.svelte
@@ -27,4 +27,5 @@
Players
Pages
Schematics
+
Logs
diff --git a/src/components/moderator/pages/event/RefereesList.svelte b/src/components/moderator/pages/event/RefereesList.svelte
index 904ee5c..6fe3fc8 100644
--- a/src/components/moderator/pages/event/RefereesList.svelte
+++ b/src/components/moderator/pages/event/RefereesList.svelte
@@ -19,12 +19,10 @@
@@ -60,27 +56,7 @@
{/each}
-
-
-
-
-
-
-
-
-
-
- No Players found :(
-
- {#each $players
- .filter((v) => v.name.toLowerCase().includes(playerSearch.toLowerCase()))
- .filter((v, i) => i < 50)
- .filter((v) => !referees.some((k) => k.uuid === v.uuid)) as player (player.uuid)}
- addReferee(player.uuid)} keywords={[player.uuid]}>{player.name}
- {/each}
-
-
-
-
-
+
+ addReferee(player.uuid)} />
+
diff --git a/src/components/moderator/pages/logs/AuditLog.svelte b/src/components/moderator/pages/logs/AuditLog.svelte
new file mode 100644
index 0000000..73b1975
--- /dev/null
+++ b/src/components/moderator/pages/logs/AuditLog.svelte
@@ -0,0 +1,191 @@
+
+
+
+
+
+ debounce(e.currentTarget.value, (v) => {
+ fullText = v;
+ })}
+ oninput={(e) =>
+ debounce(e.currentTarget.value, (v) => {
+ fullText = v;
+ })}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {#each table.getHeaderGroups() as headerGroup (headerGroup.id)}
+
+ {#each headerGroup.headers as header (header.id)}
+
+ {#if !header.isPlaceholder}
+
+ {/if}
+
+ {/each}
+
+ {/each}
+
+
+ {#each table.getRowModel().rows as row (row.id)}
+
+ {#each row.getVisibleCells() as cell (cell.id)}
+
+
+
+ {/each}
+
+ {:else}
+
+ Keine Einträge gefunden.
+
+ {/each}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/moderator/pages/logs/columns.ts b/src/components/moderator/pages/logs/columns.ts
new file mode 100644
index 0000000..805cb9e
--- /dev/null
+++ b/src/components/moderator/pages/logs/columns.ts
@@ -0,0 +1,35 @@
+import type { AuditLogEntry } from "@components/types/auditlog";
+import type { ColumnDef } from "@tanstack/table-core";
+
+export const columns: ColumnDef
[] = [
+ {
+ accessorKey: "id",
+ header: "ID",
+ },
+ {
+ accessorKey: "time",
+ header: "Time",
+ cell: (info) => new Date(info.getValue()).toLocaleString(),
+ },
+ {
+ accessorKey: "server",
+ header: "Server",
+ },
+ {
+ accessorKey: "serverOwner",
+ header: "Server Owner",
+ cell: (info) => info.getValue() || "N/A",
+ },
+ {
+ accessorKey: "actor",
+ header: "Spieler",
+ },
+ {
+ accessorKey: "actionType",
+ header: "Action Type",
+ },
+ {
+ accessorKey: "actionText",
+ header: "Action Text",
+ },
+];
diff --git a/src/components/moderator/pages/players/PermissionsDropdown.svelte b/src/components/moderator/pages/players/PermissionsDropdown.svelte
index a58141d..360b4ac 100644
--- a/src/components/moderator/pages/players/PermissionsDropdown.svelte
+++ b/src/components/moderator/pages/players/PermissionsDropdown.svelte
@@ -18,24 +18,27 @@
-->
-{#await playersFuture}
- Loading...
-{:then players}
-
-{/await}
\ No newline at end of file
+
+
+
+ debounce(e.currentTarget.value, (v) => {
+ search = v;
+ })}
+ oninput={(e) =>
+ debounce(e.currentTarget.value, (v) => {
+ search = v;
+ })}
+ />
+
+
+
+
+ {#each table.getHeaderGroups() as headerGroup (headerGroup.id)}
+
+ {#each headerGroup.headers as header (header.id)}
+
+ {#if !header.isPlaceholder}
+
+ {/if}
+
+ {/each}
+
+ {/each}
+
+
+ {#each table.getRowModel().rows as row (row.id)}
+
+ {#each row.getVisibleCells() as cell (cell.id)}
+
+
+
+ {/each}
+
+ {:else}
+
+ No players found.
+
+ {/each}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/moderator/pages/players/PrefixDropdown.svelte b/src/components/moderator/pages/players/PrefixDropdown.svelte
index 3093a80..db2b23f 100644
--- a/src/components/moderator/pages/players/PrefixDropdown.svelte
+++ b/src/components/moderator/pages/players/PrefixDropdown.svelte
@@ -18,16 +18,18 @@
-->
-
-
-
-
{
- table.getColumn("name")?.setFilterValue(e.currentTarget.value);
- }}
- oninput={(e) => {
- table.getColumn("name")?.setFilterValue(e.currentTarget.value);
- }}
- class="max-w-sm"
- />
-
-
-
-
-
-
- {#each table.getHeaderGroups() as headerGroup (headerGroup.id)}
-
- {#each headerGroup.headers as header (header.id)}
-
- {#if !header.isPlaceholder}
-
- {/if}
-
- {/each}
-
- {/each}
-
-
- {#each table.getRowModel().rows as row (row.id)}
-
- {#each row.getVisibleCells() as cell (cell.id)}
-
-
-
- {/each}
-
- {:else}
-
-
- No results.
-
-
- {/each}
-
-
-
-
- {pagination.pageIndex + 1}/{table.getPageCount()}
-
-
-
\ No newline at end of file
diff --git a/src/components/moderator/pages/players/columns.ts b/src/components/moderator/pages/players/columns.ts
index 2e8cb85..bbac55e 100644
--- a/src/components/moderator/pages/players/columns.ts
+++ b/src/components/moderator/pages/players/columns.ts
@@ -17,8 +17,8 @@
* along with this program. If not, see .
*/
-import type {ColumnDef} from "@tanstack/table-core";
-import type {Player} from "@type/data.ts";
+import type { ColumnDef } from "@tanstack/table-core";
+import type { Player } from "@type/data.ts";
import { renderComponent } from "@components/ui/data-table";
import PermissionsDropdown from "@components/moderator/pages/players/PermissionsDropdown.svelte";
import PrefixDropdown from "@components/moderator/pages/players/PrefixDropdown.svelte";
@@ -36,25 +36,20 @@ export const columns: ColumnDef = [
accessorKey: "prefix",
header: "Prefix",
cell: ({ row }) => {
- return renderComponent(
- PrefixDropdown, {
- prefix: row.getValue("prefix"),
- uuid: row.getValue("uuid"),
- },
- );
+ return renderComponent(PrefixDropdown, {
+ prefix: row.getValue("prefix"),
+ uuid: row.getValue("uuid"),
+ });
},
},
{
accessorKey: "perms",
header: "Permissions",
cell: ({ row }) => {
- return renderComponent(
- PermissionsDropdown,
- {
- perms: row.getValue("perms"),
- uuid: row.getValue("uuid"),
- },
- );
+ return renderComponent(PermissionsDropdown, {
+ perms: row.getValue("perms"),
+ uuid: row.getValue("uuid"),
+ });
},
},
-];
\ No newline at end of file
+];
diff --git a/src/components/repo/auditlog.ts b/src/components/repo/auditlog.ts
new file mode 100644
index 0000000..2499585
--- /dev/null
+++ b/src/components/repo/auditlog.ts
@@ -0,0 +1,40 @@
+import { derived } from "svelte/store";
+import { fetchWithToken, tokenStore } from "./repo";
+import { PagedAutidLogSchema } from "@components/types/auditlog";
+
+export class AuditLogRepo {
+ async get(
+ actionText: string | undefined,
+ serverText: string | undefined,
+ fullText: string | undefined,
+ actor: number[] | undefined,
+ actionType: string[] | undefined,
+ timeFrom: number | undefined,
+ timeTo: number | undefined,
+ serverOwner: number[] | undefined,
+ velocity: boolean | undefined,
+ page: number,
+ pageSize: number,
+ sorting: string | undefined
+ ) {
+ const params = new URLSearchParams();
+ if (actionText) params.append("actionText", actionText);
+ if (serverText) params.append("serverText", serverText);
+ if (fullText) params.append("fullText", fullText);
+ if (actor) actor.forEach((a) => params.append("actor", a.toString()));
+ if (actionType) actionType.forEach((a) => params.append("actionType", a));
+ if (timeFrom) params.append("timeGreater", timeFrom.toString());
+ if (timeTo) params.append("timeLess", timeTo.toString());
+ if (serverOwner) serverOwner.forEach((s) => params.append("serverOwner", s.toString()));
+ if (velocity !== undefined) params.append("velocity", velocity.toString());
+ params.append("page", page.toString());
+ params.append("limit", pageSize.toString());
+ if (sorting) params.append("sorting", sorting);
+
+ return await fetchWithToken("", `/auditlog?${params.toString()}`)
+ .then((value) => value.json())
+ .then((data) => PagedAutidLogSchema.parse(data));
+ }
+}
+
+export const auditLog = derived(tokenStore, ($token) => new AuditLogRepo());
diff --git a/src/components/repo/data.ts b/src/components/repo/data.ts
index b6a87db..44cf672 100644
--- a/src/components/repo/data.ts
+++ b/src/components/repo/data.ts
@@ -17,8 +17,8 @@
* along with this program. If not, see .
*/
-import type { Player, Server } from "@type/data.ts";
-import { PlayerSchema, ServerSchema } from "@type/data.ts";
+import type { Player, PlayerList, Server } from "@type/data.ts";
+import { PlayerListSchema, PlayerSchema, ServerSchema } from "@type/data.ts";
import { fetchWithToken, tokenStore } from "./repo.ts";
import { derived, get } from "svelte/store";
import { TeamSchema, type Team } from "@components/types/team.ts";
@@ -38,10 +38,28 @@ export class DataRepo {
.then(PlayerSchema.parse);
}
- public async getPlayers(): Promise {
- return await fetchWithToken(get(tokenStore), "/data/admin/users")
+ public async queryPlayers(
+ name: string | undefined,
+ uuid: string | undefined,
+ team: number[] | undefined,
+ limit: number | undefined,
+ page: number | undefined,
+ includePerms: boolean | undefined,
+ includeId: boolean | undefined
+ ): Promise {
+ let query = new URLSearchParams();
+
+ if (name) query.append("name", name);
+ if (uuid) query.append("uuid", uuid);
+ if (team) team.forEach((t) => query.append("team", t.toString()));
+ if (limit) query.append("limit", limit.toString());
+ if (page) query.append("page", page.toString());
+ if (includePerms !== undefined) query.append("includePerms", includePerms.toString());
+ if (includeId !== undefined) query.append("includeId", includeId.toString());
+
+ return await fetchWithToken(this.token, "/data/admin/users?" + query.toString())
.then((value) => value.json())
- .then(PlayerSchema.array().parse);
+ .then(PlayerListSchema.parse);
}
public async getTeams(): Promise {
diff --git a/src/components/stores/stores.ts b/src/components/stores/stores.ts
index 0afaf52..0ca1faf 100644
--- a/src/components/stores/stores.ts
+++ b/src/components/stores/stores.ts
@@ -31,10 +31,6 @@ import { permsRepo } from "@repo/perms.ts";
export const schemTypes = cached([], () => fetchWithToken(get(tokenStore), "/data/admin/schematicTypes").then((res) => res.json()));
-export const players = cached([], async () => {
- return get(dataRepo).getPlayers();
-});
-
export const teams = cached([], async () => {
return get(dataRepo).getTeams();
});
diff --git a/src/components/types/auditlog.ts b/src/components/types/auditlog.ts
new file mode 100644
index 0000000..d956980
--- /dev/null
+++ b/src/components/types/auditlog.ts
@@ -0,0 +1,19 @@
+import { z } from "zod";
+
+export const AuditLogEntrySchema = z.object({
+ id: z.number(),
+ time: z.number(),
+ server: z.string(),
+ serverOwner: z.string().nullable(),
+ actor: z.string(),
+ actionType: z.enum(["JOIN", "LEAVE", "COMMAND", "SENSITIVE_COMMAND", "CHAT", "GUI_OPEN", "GUI_CLOSE", "GUI_CLICK"]),
+ actionText: z.string(),
+});
+
+export const PagedAutidLogSchema = z.object({
+ entries: z.array(AuditLogEntrySchema),
+ rows: z.number(),
+});
+
+export type AuditLogEntry = z.infer;
+export type PagedAuditLog = z.infer;
diff --git a/src/components/types/data.ts b/src/components/types/data.ts
index 2098b68..3eee428 100644
--- a/src/components/types/data.ts
+++ b/src/components/types/data.ts
@@ -29,12 +29,20 @@ export type SchematicType = z.infer;
export const PlayerSchema = z.object({
name: z.string(),
uuid: z.string(),
- prefix: z.string(),
- perms: z.array(z.string()),
+ prefix: z.string().nullable(),
+ perms: z.array(z.string()).nullable(),
+ id: z.number().nullable(),
});
export type Player = z.infer;
+export const PlayerListSchema = z.object({
+ entries: z.array(PlayerSchema),
+ rows: z.number(),
+});
+
+export type PlayerList = z.infer;
+
export const ServerSchema = z.object({
description: z.any(),
players: z.object({
diff --git a/src/components/ui/PlayerSelector.svelte b/src/components/ui/PlayerSelector.svelte
new file mode 100644
index 0000000..e47db2b
--- /dev/null
+++ b/src/components/ui/PlayerSelector.svelte
@@ -0,0 +1,122 @@
+
+
+
+
+ {#snippet child({ props })}
+
+ {/snippet}
+
+
+
+
+
+ No players found.
+
+ {#each players as player (player.uuid)}
+ handleSelect(player)}>
+
+ {player.name}
+
+ {/each}
+
+
+
+
+