lots of styling changes and some random fixes
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy, tick } from 'svelte';
|
||||
import { flip } from 'svelte/animate';
|
||||
|
||||
let {
|
||||
data
|
||||
@@ -127,11 +128,12 @@
|
||||
{/if}
|
||||
|
||||
<!-- Runners-up, responsive grid -->
|
||||
{#if leaderboard.length > 1}
|
||||
{#if leaderboard.length > 1}
|
||||
<div class="runners-grid" style="--runner-count:{Math.min(leaderboard.length - 1, 5)}">
|
||||
{#each leaderboard.slice(1) as team, i (team.name)}
|
||||
<a
|
||||
href="/"
|
||||
animate:flip={{ duration: 300 }}
|
||||
class="score-box runner"
|
||||
style="--c:{team.color}"
|
||||
aria-label="{team.name}, {ordinal(i + 2)} place, {team.points} points"
|
||||
@@ -169,6 +171,7 @@
|
||||
<div class="events">
|
||||
{#each eventTable as event (event.id)}
|
||||
<div
|
||||
animate:flip={{ duration: 300 }}
|
||||
class="event-card"
|
||||
class:ongoing-event={event.state == 1}
|
||||
class:completed-event={event.state == 2}
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Sort players: placed first (ascending), unplaced last
|
||||
if (data && data[0] && data[0].registeredPlayers) {
|
||||
data[0].registeredPlayers.forEach((bracket: any) => {
|
||||
bracket.items.sort((a: any, b: any) => {
|
||||
@@ -63,99 +62,83 @@
|
||||
|
||||
onMount(() => {
|
||||
eventEndpoint = new EventSource('/api/registeredEvents');
|
||||
|
||||
// eventEndpoint.onmessage = (e) => {
|
||||
// const eventData = JSON.parse(e.data);
|
||||
// console.log(eventData);
|
||||
// };
|
||||
});
|
||||
</script>
|
||||
|
||||
{#await eventDataPromise}
|
||||
<div>loading</div>
|
||||
<div class="page-container"><div>loading</div></div>
|
||||
{:then eventData}
|
||||
{@const event = eventData[0]}
|
||||
{console.log(event)}
|
||||
<div class="flex justify-center">
|
||||
<div class="w-full flex-col px-[5vw] text-center">
|
||||
<div
|
||||
style:background-color={event.state === 1
|
||||
? 'color-mix(in srgb, #fe640b 18%, transparent)'
|
||||
: event.state === 2
|
||||
? 'color-mix(in srgb, #a6e3a1 18%, transparent)'
|
||||
: ''}
|
||||
class="align-text-middle my-7 h-10 w-full rounded-2xl border-2 border-solid border-ctp-surface1"
|
||||
>
|
||||
{event.name} - {event.division}
|
||||
{#if event.state == 1}- ONGOING
|
||||
{:else if event.state == 2}- FINISHED
|
||||
<div class="page-container">
|
||||
<div class="event-card" class:ongoing-event={event.state == 1} class:completed-event={event.state == 2}
|
||||
style={event.state == 2 ? `--event-color: ${event.winner?.color ?? 'currentColor'}` : ''}>
|
||||
<div class="event-header">
|
||||
<span class="event-name goldman">{event.name}</span>
|
||||
<span class="event-division">{event.division}</span>
|
||||
{#if event.state == 1}
|
||||
<span class="event-status goldman">ONGOING</span>
|
||||
{:else if event.state == 2}
|
||||
<span class="event-status goldman">FINISHED</span>
|
||||
{/if}
|
||||
</div>
|
||||
{#each event.registeredPlayers as bracket, bi}
|
||||
{#if bi > 0}
|
||||
<div class="bracket-sep" aria-hidden="true"></div>
|
||||
{/if}
|
||||
<div class="bracket-row">
|
||||
<div class="brackets-name flex items-center">
|
||||
<span class="brackets-name-text align-text-middle">{bracket.name}</span>
|
||||
<div class="bracket-vertical-sep"></div>
|
||||
</div>
|
||||
{#each bracket.items as player}
|
||||
<div class="player-box" style="--c:{player.teamColor}">
|
||||
<div class="player-ghost" aria-hidden="true">
|
||||
<svg viewBox="0 0.1 100 0.6" preserveAspectRatio="none" class="ghost-svg">
|
||||
<text
|
||||
x="0"
|
||||
y="0.7"
|
||||
font-size="1"
|
||||
dominant-baseline="auto"
|
||||
textLength="100"
|
||||
lengthAdjust="spacingAndGlyphs"
|
||||
font-family="'Black Ops One',system-ui">{player.firstName}</text
|
||||
>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="player-name-wrap" use:marquee>
|
||||
<span class=" text-xl">
|
||||
{player.firstName}
|
||||
{player.lastName}
|
||||
</span>
|
||||
</div>
|
||||
{#if player.placement !== 0}
|
||||
<div class="player-placement goldman">{ordinal(player.placement)}</div>
|
||||
<div class="resultContainer flex justify-center">
|
||||
{#each player.playerScores as score}
|
||||
<div class="mx-5 my-1 rounded border-2" style="border-color:{player.teamColor}">
|
||||
Run {score.resultIndex + 1}: {score.result}{event.resultPresets[0].unit}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="player-placement-gap"></div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{#if data.user}
|
||||
<div class="mt-10 flex w-full justify-center">
|
||||
<a
|
||||
class="flex justify-center rounded border-2 border-solid border-white bg-ctp-surface2 p-4"
|
||||
href="/event/scoring/{eventId}">Score This Event</a
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
{/await}
|
||||
|
||||
<style>
|
||||
.resultContainer {
|
||||
flex-direction: column;
|
||||
}
|
||||
@media (max-width: 479px) {
|
||||
.resultContainer {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div class="brackets px-[5vw]">
|
||||
{#each event.registeredPlayers as bracket, bi}
|
||||
{#if bi > 0}
|
||||
<div class="bracket-sep" aria-hidden="true"></div>
|
||||
{/if}
|
||||
<div class="bracket-row">
|
||||
<div class="brackets-name flex items-center">
|
||||
<span class="brackets-name-text align-text-middle">{bracket.name}</span>
|
||||
<div class="bracket-vertical-sep"></div>
|
||||
</div>
|
||||
{#each bracket.items as player}
|
||||
<div class="player-box" style="--c:{player.teamColor}">
|
||||
<div class="player-ghost" aria-hidden="true">
|
||||
<svg viewBox="0 0.1 100 0.6" preserveAspectRatio="none" class="ghost-svg">
|
||||
<text
|
||||
x="0"
|
||||
y="0.7"
|
||||
font-size="1"
|
||||
dominant-baseline="auto"
|
||||
textLength="100"
|
||||
lengthAdjust="spacingAndGlyphs"
|
||||
font-family="'Black Ops One',system-ui">{player.firstName}</text
|
||||
>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="player-name-wrap" use:marquee>
|
||||
<a href="/stats/player/{player.id}" class="marquee-inner">
|
||||
{player.firstName}
|
||||
{player.lastName}
|
||||
</a>
|
||||
</div>
|
||||
{#if player.placement !== 0}
|
||||
<div class="player-placement goldman">{ordinal(player.placement)}</div>
|
||||
<div class="flex flex-col justify-center">
|
||||
{#each player.playerScores as score}
|
||||
<div class="mx-1 my-0.5 rounded border px-1 text-xs" style="border-color:{player.teamColor}">
|
||||
Run {score.resultIndex + 1}: {score.result}{event.resultPresets[0].unit}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="player-placement-gap"></div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if data.user}
|
||||
<div class="mt-10 flex w-full justify-center">
|
||||
<a
|
||||
class="flex justify-center rounded border-2 border-solid border-white bg-ctp-surface2 p-4"
|
||||
href="/event/scoring/{eventId}">Score This Event</a
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/await}
|
||||
@@ -17,6 +17,13 @@
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.page-container {
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -51,7 +58,7 @@
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
text-decoration: none;
|
||||
transition: filter 0.15s ease;
|
||||
transition: aspect-ratio 0.3s ease, border-width 0.3s ease, min-height 0.3s ease, filter 0.15s ease;
|
||||
}
|
||||
.score-box:hover {
|
||||
filter: brightness(1.15);
|
||||
@@ -65,10 +72,12 @@
|
||||
aspect-ratio: 2 / 1;
|
||||
border-width: 3px;
|
||||
min-height: 80px;
|
||||
transition: aspect-ratio 0.3s ease, border-width 0.3s ease, min-height 0.3s ease;
|
||||
}
|
||||
.runner {
|
||||
aspect-ratio: 4 / 1;
|
||||
min-height: 56px;
|
||||
transition: aspect-ratio 0.3s ease, min-height 0.3s ease;
|
||||
}
|
||||
|
||||
/* Ghost SVG fills cell, text sits at bottom */
|
||||
@@ -90,40 +99,83 @@
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 14px;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
.score-meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
.score-rank {
|
||||
font-size: 10px;
|
||||
letter-spacing: 1.5px;
|
||||
font-size: clamp(14px, 2.5vw, 17px);
|
||||
letter-spacing: 1.2px;
|
||||
text-transform: uppercase;
|
||||
opacity: 0.5;
|
||||
opacity: 0.85;
|
||||
line-height: 1;
|
||||
margin-bottom: 3px;
|
||||
margin-bottom: 2px;
|
||||
white-space: nowrap;
|
||||
text-shadow: 0 0 6px rgb(0 0 0 / 50%);
|
||||
}
|
||||
.score-name {
|
||||
font-size: clamp(11px, 3vw, 20px);
|
||||
font-size: clamp(20px, 5.5vw, 40px);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 100%;
|
||||
}
|
||||
.score-pts {
|
||||
font-size: clamp(20px, 5.5vw, 40px);
|
||||
letter-spacing: 2px;
|
||||
flex-shrink: 0;
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
z-index: 2;
|
||||
text-shadow: 0 0 8px rgb(0 0 0 / 80%), 0 0 4px rgb(0 0 0 / 60%);
|
||||
}
|
||||
.winner .score-meta {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.winner .score-rank {
|
||||
font-size: clamp(16px, 3vw, 20px);
|
||||
opacity: 0.85;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.winner .score-name {
|
||||
font-size: clamp(16px, 5vw, 32px);
|
||||
font-size: clamp(34px, 9vw, 64px);
|
||||
max-width: 70%;
|
||||
}
|
||||
.winner .score-pts {
|
||||
font-size: clamp(26px, 7.5vw, 52px);
|
||||
font-size: clamp(34px, 9vw, 64px);
|
||||
}
|
||||
|
||||
/* Smallest screens: one-line for runners, bigger text to fill height */
|
||||
@media (max-width: 479px) {
|
||||
.score-meta {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.score-rank {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.score-name {
|
||||
font-size: clamp(18px, 5vw, 28px);
|
||||
}
|
||||
.score-pts {
|
||||
font-size: clamp(22px, 6vw, 34px);
|
||||
}
|
||||
.winner .score-name {
|
||||
font-size: clamp(26px, 7vw, 42px);
|
||||
max-width: 60%;
|
||||
}
|
||||
.winner .score-pts {
|
||||
font-size: clamp(28px, 8vw, 48px);
|
||||
}
|
||||
}
|
||||
|
||||
/* Responsive runners-up: 1 col <480px, 2 cols 480-699px, 4 cols ≥700px */
|
||||
@@ -131,6 +183,7 @@
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
grid-template-columns: 1fr;
|
||||
transition: gap 0.3s ease;
|
||||
}
|
||||
@media (min-width: 480px) {
|
||||
.runners-grid {
|
||||
@@ -256,17 +309,20 @@
|
||||
.brackets {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: gap 0.3s ease, align-items 0.3s ease;
|
||||
}
|
||||
.bracket-sep {
|
||||
height: 1px;
|
||||
background: color-mix(in srgb, currentColor 10%, transparent);
|
||||
margin: 0 10px;
|
||||
transition: height 0.3s ease, width 0.3s ease, margin 0.3s ease;
|
||||
}
|
||||
.bracket-row {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
padding: 7px 9px;
|
||||
flex-wrap: wrap;
|
||||
transition: gap 0.3s ease, padding 0.3s ease, min-width 0.3s ease, flex 0.3s ease;
|
||||
}
|
||||
|
||||
/* Player boxes */
|
||||
@@ -283,6 +339,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 52px;
|
||||
transition: max-width 0.3s ease, flex 0.3s ease, min-height 0.3s ease;
|
||||
}
|
||||
|
||||
/* Mobile: single column layout */
|
||||
@@ -384,3 +441,76 @@
|
||||
.player-placement-gap {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
/* Shared card pattern for auth, settings, etc. */
|
||||
.card {
|
||||
border-radius: 12px;
|
||||
border: 1px solid color-mix(in srgb, currentColor 18%, transparent);
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
.card h1 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
}
|
||||
.card label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
font-size: 0.85rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.card input,
|
||||
.card select {
|
||||
padding: 0.5rem;
|
||||
border: 1px solid color-mix(in srgb, currentColor 18%, transparent);
|
||||
border-radius: 6px;
|
||||
background: color-mix(in srgb, currentColor 6%, transparent);
|
||||
color: inherit;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.card input:focus,
|
||||
.card select:focus {
|
||||
outline: none;
|
||||
border-color: currentColor;
|
||||
}
|
||||
.card button {
|
||||
padding: 0.5rem 1rem;
|
||||
border: 1px solid color-mix(in srgb, currentColor 18%, transparent);
|
||||
border-radius: 6px;
|
||||
background: color-mix(in srgb, currentColor 10%, transparent);
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
transition: filter 0.15s ease;
|
||||
}
|
||||
.card button:hover {
|
||||
filter: brightness(1.15);
|
||||
}
|
||||
.card button:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: default;
|
||||
}
|
||||
.card .error {
|
||||
color: var(--ctp-mocha-red, #f38ba8);
|
||||
font-size: 0.85rem;
|
||||
margin: 0;
|
||||
}
|
||||
.card .switch {
|
||||
font-size: 0.85rem;
|
||||
text-align: center;
|
||||
opacity: 0.7;
|
||||
}
|
||||
.card .switch a {
|
||||
color: inherit;
|
||||
}
|
||||
.card .switch a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.card small {
|
||||
opacity: 0.6;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
@@ -67,36 +67,45 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="p-4">
|
||||
<h2 class="mb-4 text-lg font-bold">Manual Ledger Entry</h2>
|
||||
<div class="mb-4 flex flex-wrap gap-4 items-end">
|
||||
<div>
|
||||
<label class="block text-sm mb-1">Event</label>
|
||||
<select bind:value={selectedEventId} class="border rounded px-2 py-1">
|
||||
<option value="">Select event...</option>
|
||||
{#each data.events as event (event.id)}
|
||||
<option value={event.id}>{event.name} — {event.division}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
{#each teamNames as team}
|
||||
<div class="page-container">
|
||||
<h2 class="section-label">Manual Ledger Entry</h2>
|
||||
<div class="card">
|
||||
<div class="flex flex-wrap gap-4 items-end">
|
||||
<div>
|
||||
<label class="block text-sm mb-1">{team}</label>
|
||||
<input
|
||||
type="number"
|
||||
value={inputPoints[team] ?? '0'}
|
||||
oninput={(e) => { inputPoints[team] = (e.target as HTMLInputElement).value; }}
|
||||
class="border rounded px-2 py-1 w-20"
|
||||
/>
|
||||
<label class="block text-sm mb-1">Event</label>
|
||||
<select bind:value={selectedEventId}>
|
||||
<option value="">Select event...</option>
|
||||
{#each data.events as event (event.id)}
|
||||
<option value={event.id}>{event.name} — {event.division}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
{/each}
|
||||
{#each teamNames as team}
|
||||
<div>
|
||||
<label class="block text-sm mb-1">{team}</label>
|
||||
<input
|
||||
type="number"
|
||||
value={inputPoints[team] ?? '0'}
|
||||
oninput={(e) => { inputPoints[team] = (e.target as HTMLInputElement).value; }}
|
||||
style="width: 5rem;"
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
<button onclick={submitEntry} disabled={!selectedEventId || submitting}>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-label" style="padding-bottom: 4px;">
|
||||
Ledger Entries
|
||||
<button
|
||||
onclick={submitEntry}
|
||||
disabled={!selectedEventId || submitting}
|
||||
class="border rounded px-4 py-1 bg-green-700 text-white disabled:opacity-50"
|
||||
onclick={() => loadEntries(!showAll)}
|
||||
style="margin-left: 0.5rem; padding: 0.15rem 0.5rem; font-size: 0.75rem; border: 1px solid color-mix(in srgb, currentColor 18%, transparent); border-radius: 4px; background: transparent; color: inherit; cursor: pointer;"
|
||||
>
|
||||
Submit
|
||||
{showAll ? 'Show 100 most recent' : 'Load all entries'}
|
||||
</button>
|
||||
<span class="text-sm opacity-60" style="margin-left: 0.5rem;">{entries.length} entries</span>
|
||||
</div>
|
||||
|
||||
<Table data={entries} maxHeight="none">
|
||||
@@ -121,14 +130,4 @@
|
||||
<td class="px-2">{formatTime(entry.timestamp)}</td>
|
||||
{/snippet}
|
||||
</Table>
|
||||
|
||||
<div class="mt-4">
|
||||
<button
|
||||
onclick={() => loadEntries(!showAll)}
|
||||
class="border rounded px-4 py-1"
|
||||
>
|
||||
{showAll ? 'Show 100 most recent' : 'Load all entries'}
|
||||
</button>
|
||||
<span class="ml-2 text-sm">{entries.length} entries shown</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -4,87 +4,39 @@
|
||||
export let form: ActionData;
|
||||
</script>
|
||||
|
||||
<div class="auth-card">
|
||||
{#if data.user}
|
||||
<div class="align-center flex h-full w-full flex-col text-center">
|
||||
<h1>Already logged in</h1>
|
||||
<p>You're signed in as <strong>{data.user.username}</strong></p>
|
||||
<a href="/">Go to home</a>
|
||||
<form method="POST" action="/api/logout">
|
||||
<button type="submit">Log out</button>
|
||||
</form>
|
||||
</div>
|
||||
{:else}
|
||||
<h1>Log in</h1>
|
||||
<div class="page-container">
|
||||
<div class="card" style="max-width: 360px; margin: 3rem auto;">
|
||||
{#if data.user}
|
||||
<div class="align-center flex h-full w-full flex-col text-center">
|
||||
<h1>Already logged in</h1>
|
||||
<p>You're signed in as <strong>{data.user.username}</strong></p>
|
||||
<a href="/">Go to home</a>
|
||||
<form method="POST" action="/api/logout">
|
||||
<button type="submit">Log out</button>
|
||||
</form>
|
||||
</div>
|
||||
{:else}
|
||||
<h1>Log in</h1>
|
||||
|
||||
<form method="POST">
|
||||
<label>
|
||||
Username
|
||||
<input class="text-black" name="username" type="text" autocomplete="username" required />
|
||||
</label>
|
||||
<form method="POST">
|
||||
<label>
|
||||
Username
|
||||
<input name="username" type="text" autocomplete="username" required />
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Password
|
||||
<input
|
||||
class="text-black"
|
||||
name="password"
|
||||
type="password"
|
||||
autocomplete="current-password"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Password
|
||||
<input name="password" type="password" autocomplete="current-password" required />
|
||||
</label>
|
||||
|
||||
{#if form?.message}
|
||||
<p class="error">{form.message}</p>
|
||||
{/if}
|
||||
{#if form?.message}
|
||||
<p class="error">{form.message}</p>
|
||||
{/if}
|
||||
|
||||
<button type="submit">Log in</button>
|
||||
</form>
|
||||
|
||||
<p class="switch">No account? <a href="/register">Sign up</a></p>
|
||||
{/if}
|
||||
|
||||
<!-- <p class="switch">No account? <a href="/signup">Sign up</a></p> -->
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.auth-card {
|
||||
max-width: 320px;
|
||||
margin: 4rem auto;
|
||||
padding: 2rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
}
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
input {
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
}
|
||||
button {
|
||||
padding: 0.6rem;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background: #333;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
.error {
|
||||
color: #c0392b;
|
||||
font-size: 0.85rem;
|
||||
margin: 0;
|
||||
}
|
||||
.switch {
|
||||
margin-top: 1rem;
|
||||
font-size: 0.85rem;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
@@ -4,85 +4,33 @@
|
||||
export let form: ActionData;
|
||||
</script>
|
||||
|
||||
<div class="auth-card">
|
||||
<h1>Sign up</h1>
|
||||
<div class="page-container">
|
||||
<div class="card" style="max-width: 360px; margin: 3rem auto;">
|
||||
<h1>Sign up</h1>
|
||||
|
||||
{#if data.disabled}
|
||||
<p class="error">Registration is currently disabled by an administrator.</p>
|
||||
{:else}
|
||||
<form method="POST">
|
||||
<label>
|
||||
Username
|
||||
<input name="username" class="text-black" type="text" autocomplete="username" required />
|
||||
</label>
|
||||
{#if data.disabled}
|
||||
<p class="error">Registration is currently disabled by an administrator.</p>
|
||||
{:else}
|
||||
<form method="POST">
|
||||
<label>
|
||||
Username
|
||||
<input name="username" type="text" autocomplete="username" required />
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Password
|
||||
<input
|
||||
class="text-black"
|
||||
name="password"
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
minlength="8"
|
||||
required
|
||||
/>
|
||||
<small>At least 8 characters</small>
|
||||
</label>
|
||||
<label>
|
||||
Password
|
||||
<input name="password" type="password" autocomplete="new-password" minlength="8" required />
|
||||
<small>At least 8 characters</small>
|
||||
</label>
|
||||
|
||||
{#if form?.message}
|
||||
<p class="error">{form.message}</p>
|
||||
{/if}
|
||||
{#if form?.message}
|
||||
<p class="error">{form.message}</p>
|
||||
{/if}
|
||||
|
||||
<button type="submit">Create account</button>
|
||||
</form>
|
||||
<button type="submit">Create account</button>
|
||||
</form>
|
||||
|
||||
<p class="switch">Already have an account? <a href="/login">Log in</a></p>
|
||||
{/if}
|
||||
<p class="switch">Already have an account? <a href="/login">Log in</a></p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.auth-card {
|
||||
max-width: 320px;
|
||||
margin: 4rem auto;
|
||||
padding: 2rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
}
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
input {
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
}
|
||||
small {
|
||||
color: #888;
|
||||
}
|
||||
button {
|
||||
padding: 0.6rem;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background: #333;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
.error {
|
||||
color: #c0392b;
|
||||
font-size: 0.85rem;
|
||||
margin: 0;
|
||||
}
|
||||
.switch {
|
||||
margin-top: 1rem;
|
||||
font-size: 0.85rem;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,21 +5,21 @@
|
||||
let { data }: { data: PageData } = $props();
|
||||
</script>
|
||||
|
||||
<div class="p-4">
|
||||
<h2 class="mb-4 text-lg font-bold">Settings</h2>
|
||||
<div class="page-container">
|
||||
<h2 class="section-label">Settings</h2>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
<form method="POST" action="?/toggleRegistration" use:enhance>
|
||||
<div class="flex items-center gap-4 border rounded p-4">
|
||||
<div class="card" style="flex-direction: row; align-items: center;">
|
||||
<div class="flex-1">
|
||||
<p class="font-semibold">Disable Registration</p>
|
||||
<p class="text-sm opacity-70">Prevents new users from signing up via /register</p>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
class="border rounded px-4 py-1 {data.disableRegistration
|
||||
? 'bg-red-700 text-white'
|
||||
: 'bg-green-700 text-white'}"
|
||||
style={data.disableRegistration
|
||||
? 'background: color-mix(in srgb, var(--ctp-mocha-red) 30%, transparent); border-color: var(--ctp-mocha-red);'
|
||||
: 'background: color-mix(in srgb, var(--ctp-mocha-green) 30%, transparent); border-color: var(--ctp-mocha-green);'}
|
||||
>
|
||||
{data.disableRegistration ? 'Disabled' : 'Enabled'}
|
||||
</button>
|
||||
@@ -27,16 +27,16 @@
|
||||
</form>
|
||||
|
||||
<form method="POST" action="?/toggleLeaderboardLock" use:enhance>
|
||||
<div class="flex items-center gap-4 border rounded p-4">
|
||||
<div class="card" style="flex-direction: row; align-items: center;">
|
||||
<div class="flex-1">
|
||||
<p class="font-semibold">Require Login for Leaderboard</p>
|
||||
<p class="text-sm opacity-70">Non-logged-in users will be redirected to /login</p>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
class="border rounded px-4 py-1 {data.requireLoginForLeaderboard
|
||||
? 'bg-red-700 text-white'
|
||||
: 'bg-green-700 text-white'}"
|
||||
style={data.requireLoginForLeaderboard
|
||||
? 'background: color-mix(in srgb, var(--ctp-mocha-red) 30%, transparent); border-color: var(--ctp-mocha-red);'
|
||||
: 'background: color-mix(in srgb, var(--ctp-mocha-green) 30%, transparent); border-color: var(--ctp-mocha-green);'}
|
||||
>
|
||||
{data.requireLoginForLeaderboard ? 'Locked' : 'Public'}
|
||||
</button>
|
||||
|
||||
@@ -23,53 +23,32 @@
|
||||
</script>
|
||||
|
||||
{#await data.playerInfo}
|
||||
<div>Loading...</div>
|
||||
<div class="page-container"><div>Loading...</div></div>
|
||||
{:then playerInfo}
|
||||
{console.log(playerInfo)}
|
||||
<div class="mt-5 flex justify-center">
|
||||
<div
|
||||
class="player-single-box aspect-square w-full max-w-[95vw] justify-center md:aspect-2/1 lg:aspect-2/1"
|
||||
style="--c:{playerInfo.teamInfo.color}"
|
||||
>
|
||||
<div class="player-ghost" aria-hidden="true">
|
||||
<svg viewBox="0 0.1 100 0.6" preserveAspectRatio="none" class="ghost-svg">
|
||||
<text
|
||||
x="0"
|
||||
y="0.7"
|
||||
font-size="1"
|
||||
dominant-baseline="auto"
|
||||
textLength="100"
|
||||
lengthAdjust="spacingAndGlyphs"
|
||||
font-family="'Black Ops One',system-ui">{playerInfo.firstName}</text
|
||||
>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" goldman player-name-wrap text-5xl" use:marquee>
|
||||
{playerInfo.firstName}
|
||||
{playerInfo.lastName}
|
||||
<div class="page-container">
|
||||
<div class="flex justify-center">
|
||||
<div
|
||||
class="player-box aspect-square w-full max-w-[95vw] md:aspect-2/1 lg:aspect-2/1"
|
||||
style="--c:{playerInfo.teamInfo.color}"
|
||||
>
|
||||
<div class="player-ghost" aria-hidden="true">
|
||||
<svg viewBox="0 0.1 100 0.6" preserveAspectRatio="none" class="ghost-svg">
|
||||
<text
|
||||
x="0"
|
||||
y="0.7"
|
||||
font-size="1"
|
||||
dominant-baseline="auto"
|
||||
textLength="100"
|
||||
lengthAdjust="spacingAndGlyphs"
|
||||
font-family="'Black Ops One',system-ui">{playerInfo.firstName}</text
|
||||
>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="goldman player-name-wrap text-5xl" use:marquee>
|
||||
{playerInfo.firstName}
|
||||
{playerInfo.lastName}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/await}
|
||||
|
||||
<style>
|
||||
.player-single-box {
|
||||
flex: 1 1 0;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
border: 1.5px solid var(--c);
|
||||
color: var(--c);
|
||||
background: color-mix(in srgb, var(--c) 10%, transparent);
|
||||
padding: 5px 7px 5px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.player-name-wrap {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
text-decoration: none;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user