From c036a8516beaea9c0e6ef50c9775069a7b0fd196 Mon Sep 17 00:00:00 2001 From: Telesphoreo Date: Tue, 19 May 2026 23:51:54 -0400 Subject: [PATCH] Update --- .../src/lib/pages/CommandsPage.svelte | 4 +- .../src/lib/pages/IndefBansPage.svelte | 116 ++++++++++++++---- 2 files changed, 97 insertions(+), 23 deletions(-) diff --git a/src/main/frontend/src/lib/pages/CommandsPage.svelte b/src/main/frontend/src/lib/pages/CommandsPage.svelte index 69800f1..f89e9db 100644 --- a/src/main/frontend/src/lib/pages/CommandsPage.svelte +++ b/src/main/frontend/src/lib/pages/CommandsPage.svelte @@ -59,7 +59,7 @@

No commands match that filter.

{:else}
- {#each visibleGroups as group (group.plugin)} + {#each visibleGroups as group, groupIndex (`${group.plugin}:${groupIndex}`)}
- {#each group.commands as command (command.name)} + {#each group.commands as command, commandIndex (`${command.name}:${commandIndex}`)}

diff --git a/src/main/frontend/src/lib/pages/IndefBansPage.svelte b/src/main/frontend/src/lib/pages/IndefBansPage.svelte index 2f19775..e00053d 100644 --- a/src/main/frontend/src/lib/pages/IndefBansPage.svelte +++ b/src/main/frontend/src/lib/pages/IndefBansPage.svelte @@ -5,29 +5,69 @@ import {api} from '$lib/api'; import {Card} from '$lib/components/ui/card'; import {Input} from '$lib/components/ui/input'; - import {lowerSearch, titleCase} from '$lib/utils'; + import {lowerSearch} from '$lib/utils'; + + interface BanGroup { + usernames: string[]; + uuids: string[]; + ips: string[]; + reason: string; + } let bans: Array> = $state([]); let loading = $state(true); let error = $state(null); let filter = $state(''); - const visible = $derived(bans.filter((ban) => !filter.trim() || lowerSearch(ban).includes(filter.toLowerCase().trim()))); + const groups = $derived(bans.map(toGroup)); + const visible = $derived.by(() => { + const q = filter.toLowerCase().trim(); + return groups.filter((group) => !q || lowerSearch(group).includes(q)); + }); const totals = $derived.by(() => { - const text = JSON.stringify(bans); return { - groups: bans.length, - users: (text.match(/user(name)?/gi) ?? []).length, - uuids: (text.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi) ?? []).length, - ips: (text.match(/\b(?:\d{1,3}\.){3}\d{1,3}\b/g) ?? []).length + groups: groups.length, + users: groups.reduce((total, group) => total + group.usernames.length, 0), + uuids: groups.reduce((total, group) => total + group.uuids.length, 0), + ips: groups.reduce((total, group) => total + group.ips.length, 0) }; }); - function display(value: unknown): string { - if (value == null || value === '') return '-'; - if (Array.isArray(value)) return value.map(display).join(', '); - if (typeof value === 'object') return JSON.stringify(value); - return String(value); + function isRecord(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !Array.isArray(value); + } + + function listValues(value: unknown): string[] { + if (value == null) return []; + if (Array.isArray(value)) return value.flatMap(listValues); + if (typeof value === 'string') { + const trimmed = value.trim(); + return trimmed ? [trimmed] : []; + } + if (typeof value === 'number' || typeof value === 'boolean') return [String(value)]; + if (isRecord(value)) return Object.values(value).flatMap(listValues); + return []; + } + + function toGroup(ban: Record): BanGroup { + const entries = Object.entries(ban); + const nested = entries.length === 1 && isRecord(entries[0][1]) ? entries[0][1] : ban; + const reason = listValues(nested.reason).join(', '); + + return { + usernames: listValues(nested.usernames ?? nested.users ?? nested.names), + uuids: listValues(nested.uuids ?? nested.uuid), + ips: listValues(nested.ips ?? nested.ip), + reason: reason || listValues(ban.reason).join(', ') + }; + } + + function entryCount(group: BanGroup): number { + return group.usernames.length + group.uuids.length + group.ips.length; + } + + function groupKey(group: BanGroup, index: number): string { + return `${index}:${group.usernames[0] ?? ''}:${group.uuids[0] ?? ''}:${group.ips[0] ?? ''}`; } onMount(async () => { @@ -45,7 +85,7 @@

Indefinite bans

{totals.groups} groups - {totals.users} user keys + {totals.users} users {totals.uuids} uuids {totals.ips} ips
@@ -63,18 +103,52 @@

Loading bans...

{:else if error}

{error}

+{:else if groups.length === 0} + +

No indefinite bans configured.

+
{:else if visible.length === 0}

No indefinite bans match that filter.

{:else}
- {#each visible as ban, index (index)} - -

Group {index + 1}

-
- {#each Object.entries(ban) as [key, value] (key)} -
{titleCase(key)}
-
{display(value)}
- {/each} + {#each visible as group, index (groupKey(group, index))} + {@const total = entryCount(group)} + +
+

+ {#if group.reason} + {group.reason} + {:else} + No reason provided + {/if} +

+ {total} {total === 1 ? 'entry' : 'entries'} +
+
+ {#if group.usernames.length} +
Users
+
+ {#each group.usernames as username, usernameIndex (`${username}:${usernameIndex}`)} + {username} + {/each} +
+ {/if} + {#if group.uuids.length} +
UUIDs
+
+ {#each group.uuids as uuid, uuidIndex (`${uuid}:${uuidIndex}`)} + {uuid} + {/each} +
+ {/if} + {#if group.ips.length} +
IPs
+
+ {#each group.ips as ip, ipIndex (`${ip}:${ipIndex}`)} + {ip} + {/each} +
+ {/if}
{/each}