From 8d71480e9aa72db26c27f4bc933ef136462dcc96 Mon Sep 17 00:00:00 2001 From: Stan Runge Date: Fri, 27 Mar 2026 19:16:53 +0100 Subject: [PATCH] feat: add search and data tables --- src/hooks.server.ts | 4 + src/lib/components/DataTable.svelte | 222 ++++++++++ src/lib/components/ProfileComments.svelte | 114 +++++ src/lib/components/SearchModal.svelte | 253 +++++++++++ src/lib/components/Sidebar.svelte | 120 +++--- src/lib/server/db/schema.ts | 25 ++ src/lib/server/discord/client.ts | 9 + src/lib/server/permissions.test.ts | 5 +- src/lib/server/permissions/constants.ts | 74 ++++ .../{permissions.ts => permissions/index.ts} | 112 +++-- src/lib/server/storage/r2.ts | 25 ++ src/lib/server/table.ts | 61 +++ src/routes/(app)/+layout.svelte | 95 ++++- src/routes/(app)/+page.server.ts | 21 +- src/routes/(app)/+page.svelte | 255 +++--------- src/routes/(app)/admin/+page.server.ts | 103 +++-- src/routes/(app)/admin/+page.svelte | 207 ++++----- src/routes/(app)/leaderboard/+page.server.ts | 297 +++++++------ src/routes/(app)/leaderboard/+page.svelte | 394 +++++++++--------- src/routes/(app)/mappools/+page.server.ts | 54 ++- src/routes/(app)/mappools/+page.svelte | 62 +-- .../(app)/mappools/[id]/+page.server.ts | 45 +- src/routes/(app)/matches/+page.server.ts | 121 +++--- src/routes/(app)/matches/+page.svelte | 147 +++---- src/routes/(app)/matches/[id]/+page.server.ts | 14 +- src/routes/(app)/teams/+page.server.ts | 80 ++-- src/routes/(app)/teams/+page.svelte | 322 ++------------ src/routes/(app)/teams/[id]/+page.server.ts | 72 +++- src/routes/(app)/teams/[id]/+page.svelte | 88 +++- src/routes/(app)/tournaments/+page.server.ts | 67 ++- src/routes/(app)/tournaments/+page.svelte | 143 +++---- .../(app)/tournaments/[id]/+page.server.ts | 21 +- .../(app)/tournaments/new/+page.server.ts | 2 +- src/routes/(app)/users/[id]/+page.server.ts | 101 +++++ src/routes/(app)/users/[id]/+page.svelte | 154 +++++++ src/routes/api/mappools/+server.ts | 8 +- src/routes/api/matches/+server.ts | 8 +- src/routes/api/matches/[id]/lobby/+server.ts | 8 +- src/routes/api/matches/[id]/pick/+server.ts | 4 +- src/routes/api/matches/[id]/roll/+server.ts | 4 +- .../api/matches/[id]/rolling/+server.ts | 8 +- src/routes/api/matches/[id]/score/+server.ts | 8 +- src/routes/api/search/+server.ts | 56 +++ src/routes/api/tournaments/[id]/+server.ts | 20 +- .../api/tournaments/[id]/cancel/+server.ts | 12 +- .../[id]/close-registration/+server.ts | 12 +- .../[id]/finalize-seeding/+server.ts | 12 +- .../[id]/open-registration/+server.ts | 12 +- .../tournaments/[id]/qualifiers/+server.ts | 13 +- .../api/tournaments/[id]/rounds/+server.ts | 14 +- .../[id]/rounds/[roundId]/+server.ts | 20 +- .../api/tournaments/[id]/staff/+server.ts | 16 +- .../[id]/staff/[staffId]/+server.ts | 16 +- .../[id]/start-qualifiers/+server.ts | 14 +- 54 files changed, 2577 insertions(+), 1577 deletions(-) create mode 100644 src/lib/components/DataTable.svelte create mode 100644 src/lib/components/ProfileComments.svelte create mode 100644 src/lib/components/SearchModal.svelte create mode 100644 src/lib/server/permissions/constants.ts rename src/lib/server/{permissions.ts => permissions/index.ts} (53%) create mode 100644 src/lib/server/table.ts create mode 100644 src/routes/(app)/users/[id]/+page.server.ts create mode 100644 src/routes/(app)/users/[id]/+page.svelte create mode 100644 src/routes/api/search/+server.ts diff --git a/src/hooks.server.ts b/src/hooks.server.ts index e2b0e7f..5fd7e48 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -33,6 +33,10 @@ if (!building) { .then(({ startTournamentScheduler }) => startTournamentScheduler()) .catch((err) => log.tournament.warn({ err: err.message }, 'Tournament scheduler init skipped')); + import('$lib/server/discord/client') + .then(({ initDiscordClient }) => initDiscordClient()) + .catch((err) => log.discord.warn({ err: err.message }, 'Discord client init skipped')); + process.on('unhandledRejection', (reason) => { log.http.error({ err: reason }, 'Unhandled rejection'); }); diff --git a/src/lib/components/DataTable.svelte b/src/lib/components/DataTable.svelte new file mode 100644 index 0000000..1c961af --- /dev/null +++ b/src/lib/components/DataTable.svelte @@ -0,0 +1,222 @@ + + + +
+
+ + + + +
+ {meta.total} result{meta.total !== 1 ? 's' : ''} +
+ + +
+ + + + {#each columns as col} + + {/each} + + + + {#each data as item, i} + {@render row(item, i)} + {:else} + + + + {/each} + +
+ {#if col.sortable} + + {:else} + {col.label} + {/if} +
+ {meta.search ? `No results for "${meta.search}"` : 'No data'} +
+
+ + +{#if meta.totalPages > 1} +
+ + Showing {showingStart}–{showingEnd} of {meta.total} + +
+ + {#each pageButtons as p} + {#if p === '...'} + ... + {:else} + + {/if} + {/each} + +
+
+{/if} diff --git a/src/lib/components/ProfileComments.svelte b/src/lib/components/ProfileComments.svelte new file mode 100644 index 0000000..1af1201 --- /dev/null +++ b/src/lib/components/ProfileComments.svelte @@ -0,0 +1,114 @@ + + +
+

Comments

+ + {#if user} +
{ + posting = true; + return async ({ result, update }) => { + posting = false; + if (result.type === 'success') { + content = ''; + await update(); + } + }; + }} + class="mt-3" + > +
+ {#if user.image} + + {:else} +
+ {/if} +
+ +
+ +
+
+
+
+ {:else} +

Sign in to leave a comment.

+ {/if} + + {#if comments.length > 0} +
+ {#each comments as comment} +
+ {#if comment.author.image} + + + + {:else} + +
+ {comment.author.name.charAt(0).toUpperCase()} +
+
+ {/if} +
+
+ {comment.author.name} + {timeAgo(comment.createdAt)} +
+

+ {comment.content} +

+
+
+ {/each} +
+ {:else if !user} +

No comments yet.

+ {/if} +
diff --git a/src/lib/components/SearchModal.svelte b/src/lib/components/SearchModal.svelte new file mode 100644 index 0000000..8f09d49 --- /dev/null +++ b/src/lib/components/SearchModal.svelte @@ -0,0 +1,253 @@ + + + + +{#if open} + + + + + +{/if} diff --git a/src/lib/components/Sidebar.svelte b/src/lib/components/Sidebar.svelte index 7f94db5..b6916b6 100644 --- a/src/lib/components/Sidebar.svelte +++ b/src/lib/components/Sidebar.svelte @@ -6,7 +6,7 @@ const activeMatch = $derived(page.data?.activeMatch); const unreadCount = $derived((page.data as any)?.unreadNotificationCount ?? 0); - let { open = $bindable(false) } = $props(); + let { open = $bindable(false), guest = false } = $props(); let notifOpen = $state(false); let notifications = $state([]); let loadingNotifs = $state(false); @@ -232,7 +232,7 @@ Vash Esports - {#if user} + {#if user && !guest}
+ {:else if !activeMatch} - - {:else if !activeMatch} - - {/if} - + {/if} + + {/if}