Files
esports/src/lib/components/Header.svelte
Stan Runge d5448d9a94
Some checks failed
Test and deploy staging / staging (push) Failing after 14s
feat!: upgrade to SvelteKit 3 and adapter-bun
- run sv migrate sveltekit-3: config moves into vite.config.ts, $lib becomes
  #lib, $env/dynamic becomes $app/env/private via src/env.ts, $app/environment
  becomes $app/env, hook types move to @sveltejs/kit/hooks
- env vars stay optional (the migrator defaulted them to ''), and BANCHO_MODE,
  which it missed, is declared and passed to simulatorEnabled
- handleError only logs and masks unknown errors; Kit 3 now routes 404s and
  error(...) through it too
- json()/text() become Response.json()/new Response(); goto keepFocus/noScroll
  becomes reset: false; invalidateAll becomes refreshAll; page.url is copied
  before mutating
- replace adapter-node with @sveltejs/adapter-bun; prod-server.ts wraps
  Bun.serve to add native WebSockets (bunSockets) for /ws paths, since the
  generated server has no websocket option
- drop the Kit readdir patch: Kit 3 sorts route entries itself
- match view and broadcast pages use the live score modules

BREAKING CHANGE: requires SvelteKit 3, TypeScript 6 and Bun 1.4; the build
output is build/index.js from adapter-bun, started through prod-server.ts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 22:02:53 +00:00

129 lines
3.9 KiB
Svelte

<script lang="ts">
import Avatar from '#lib/components/Avatar.svelte';
import { page } from '$app/state';
import { enhance } from '$app/forms';
const user = $derived(page.data?.user);
const roleBadge: Record<string, { label: string; color: string }> = {
referee: { label: 'REF', color: 'bg-yellow-500/20 text-yellow-400' },
admin: { label: 'ADMIN', color: 'bg-red-500/20 text-red-400' }
};
const badge = $derived(user?.role ? roleBadge[user.role] : null);
let menuOpen = $state(false);
function toggleMenu() {
menuOpen = !menuOpen;
}
function closeMenu() {
menuOpen = false;
}
</script>
<svelte:window
onclick={(e) => {
const target = e.target as HTMLElement;
if (!target.closest('[data-profile-menu]')) closeMenu();
}}
/>
<div class="flex items-center gap-4">
{#if user}
<div class="relative" data-profile-menu>
<button
onclick={toggleMenu}
class="flex items-center gap-3 rounded-md px-2 py-1 transition-colors hover:bg-surface-700"
aria-expanded={menuOpen}
aria-haspopup="true"
>
{#if user.isRootAdmin}
<span class="font-700 rounded bg-red-500/20 px-1.5 py-0.5 text-[10px] text-red-400">
ROOT
</span>
{:else if badge}
<span class="font-700 rounded px-1.5 py-0.5 text-[10px] {badge.color}">
{badge.label}
</span>
{/if}
<Avatar src={user.image} name={user.name} size={28} />
<span class="font-500 hidden text-sm text-text-primary sm:inline">{user.name}</span>
<svg
class="h-3.5 w-3.5 text-text-secondary transition-transform {menuOpen
? 'rotate-180'
: ''}"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2.5"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7" />
</svg>
</button>
{#if menuOpen}
<div
class="absolute top-full right-0 z-50 mt-1.5 w-44 rounded-md border border-border bg-surface-800 py-1 shadow-lg"
>
<a
href="/settings"
onclick={closeMenu}
class="flex items-center gap-2.5 px-3 py-2 text-sm text-text-secondary transition-colors hover:bg-surface-700 hover:text-text-primary"
>
<svg
class="h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
Settings
</a>
<div class="my-1 border-t border-border"></div>
<form method="post" action="/settings?/logout" use:enhance>
<button
type="submit"
class="flex w-full items-center gap-2.5 px-3 py-2 text-sm text-text-secondary transition-colors hover:bg-surface-700 hover:text-red-400"
>
<svg
class="h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"
/>
</svg>
Log out
</button>
</form>
</div>
{/if}
</div>
{:else}
<a
href="/login"
class="font-600 rounded-md bg-accent px-3.5 py-1.5 text-sm text-surface-900 transition-colors hover:bg-accent-hover"
>
Sign in with osu!
</a>
{/if}
</div>