diff --git a/src/components/admin/components/FightEditPart.svelte b/src/components/admin/components/FightEditPart.svelte index 6565924..4d9d529 100644 --- a/src/components/admin/components/FightEditPart.svelte +++ b/src/components/admin/components/FightEditPart.svelte @@ -18,83 +18,103 @@ -->
@@ -107,32 +127,29 @@
- + {#snippet children({ props })} - - {/snippet} + + {/snippet}
- +
- +
- + {#snippet children({ props })} - - {/snippet} + + {/snippet}
- +
diff --git a/src/components/admin/pages/Perms.svelte b/src/components/admin/pages/Perms.svelte index 2f003d6..6ff4812 100644 --- a/src/components/admin/pages/Perms.svelte +++ b/src/components/admin/pages/Perms.svelte @@ -18,21 +18,37 @@ -->
- + {#snippet children({ hidden, toggle })} - - - - Permissions - - - {/snippet} + + + Permissions + + {/snippet}
@@ -124,14 +136,19 @@
- +
- {#if filteredPlayers.length < 100} + {#if playersList.length < 100}
    - {#each filteredPlayers as player (player.uuid)} -
  • selectedPlayer = player.uuid)}> + onclick={preventDefault(() => { + selectedPlayer = player.uuid; + selectedPlayerName = player.name; + })} + > {player.name}
  • {/each} @@ -140,7 +157,7 @@ {#if selectedPlayer} -

    {player.name}

    +

    {selectedPlayerName}

    {#await permsFuture} {:then perms} @@ -149,39 +166,27 @@ {:then player}

    Prefix

    {#each Object.entries(perms.prefixes) as [key, prefix]} - {capitalize(prefix.name.substring(7).toLowerCase())} + {capitalize(prefix.name.substring(7).toLowerCase())} {/each}

    Permissions

    {#each perms.perms as perm} - {capitalize(perm.toLowerCase())} + {capitalize(perm.toLowerCase())} {/each}
    - + {#if $me != null && $me.perms.includes("ADMINISTRATION")} - + - + - + {#snippet footer()} - - - - - {/snippet} + + + {/snippet} {/if}
    diff --git a/src/components/admin/pages/event/FightList.svelte b/src/components/admin/pages/event/FightList.svelte index d5d117f..964f4f8 100644 --- a/src/components/admin/pages/event/FightList.svelte +++ b/src/components/admin/pages/event/FightList.svelte @@ -36,8 +36,9 @@ } from "flowbite-svelte-icons"; import FightCard from "./FightCard.svelte"; import CreateFightModal from "./modals/CreateFightModal.svelte"; - import {groups, players} from "@stores/stores.ts"; + import {groups} from "@stores/stores.ts"; import TypeAheadSearch from "../../components/TypeAheadSearch.svelte"; + import PlayerSelector from "@components/ui/PlayerSelector.svelte"; import {fightRepo, type UpdateFight} from "@repo/fight.ts"; import dayjs from "dayjs"; import duration from "dayjs/plugin/duration"; @@ -97,12 +98,6 @@ } let spectatePortOpen = $state(false); - let selectPlayers = $derived($players.map(player => { - return { - name: player.name, - value: player.uuid - }; - }).sort((a, b) => a.name.localeCompare(b.name))); let spectatePort = $state(""); async function updateSpectatePort() { @@ -262,12 +257,12 @@
{#snippet footer()} - - - - - {/snippet} - + +
+ + +
+ {#snippet footer()}
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} + + + + +