fixed login and logout and started on player screen
This commit is contained in:
@@ -127,6 +127,11 @@ export async function getAllBrackets() {
|
||||
};
|
||||
}
|
||||
|
||||
export async function getPlayerInfo(playerId: number) {
|
||||
const playerInfo = await db.select().from(schema.players).where(eq(schema.players.id), playerId);
|
||||
return playerInfo;
|
||||
}
|
||||
|
||||
export async function getResultPreset(presetId?: number) {
|
||||
const resultPresets = await db
|
||||
.select()
|
||||
|
||||
15
src/routes/+layout.server.ts
Normal file
15
src/routes/+layout.server.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
// alternative, if role isn't on locals.user
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { db } from '$lib/server/db';
|
||||
import { scorers } from '$lib/server/db/schema';
|
||||
import type { LayoutServerLoad } from './$types';
|
||||
|
||||
export const load: LayoutServerLoad = async ({ locals }) => {
|
||||
if (!locals.user) return { user: null };
|
||||
|
||||
const [row] = await db
|
||||
.select({ role: scorers.role })
|
||||
.from(scorers)
|
||||
.where(eq(scorers.id, locals.user.id));
|
||||
return { user: { ...locals.user, role: row?.role ?? 'scorer' } };
|
||||
};
|
||||
@@ -1,22 +1,41 @@
|
||||
<script lang="ts">
|
||||
import './layout.css';
|
||||
import favicon from '$lib/assets/favicon.svg';
|
||||
import type { LayoutData } from './$types';
|
||||
|
||||
let { children } = $props();
|
||||
let { children, data }: { children: import('svelte').Snippet; data: LayoutData } = $props();
|
||||
</script>
|
||||
|
||||
<svelte:head><link rel="icon" href={favicon} /></svelte:head>
|
||||
|
||||
<div class="header goldman flex h-15 w-full">
|
||||
<a
|
||||
class="align-text-middle justify-left mx-3 my-1 h-auto content-center rounded-sm border-2 border-solid border-red-500 px-2"
|
||||
class="align-text-middle justify-left mx-3 my-1 h-auto content-center rounded-sm border-2
|
||||
border-solid border-red-500 px-2"
|
||||
href="/">home</a
|
||||
>
|
||||
<div class="w-full"></div>
|
||||
<a
|
||||
class="align-text-middle justify-right mx-3 my-1 h-auto content-center rounded-sm border-2 border-solid border-red-500 px-2"
|
||||
href="/login">login</a
|
||||
>
|
||||
|
||||
{#if data.user?.role === 'admin'}
|
||||
<a
|
||||
class="align-text-middle justify-right mx-3 my-1 h-auto content-center rounded-sm border-2
|
||||
border-solid border-red-500 px-2"
|
||||
href="/ledger">ledger</a
|
||||
>
|
||||
{/if}
|
||||
{#if data.user}
|
||||
<a
|
||||
class="align-text-middle justify-right mx-3 my-1 h-auto content-center rounded-sm border-2
|
||||
border-solid border-red-500 px-2"
|
||||
href="/login">logout</a
|
||||
>
|
||||
{:else}
|
||||
<a
|
||||
class="align-text-middle justify-right mx-3 my-1 h-auto content-center rounded-sm border-2
|
||||
border-solid border-red-500 px-2"
|
||||
href="/login">login</a
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{@render children()}
|
||||
|
||||
@@ -47,6 +47,17 @@
|
||||
return n + (s[(v - 20) % 10] ?? s[v] ?? s[0]);
|
||||
}
|
||||
|
||||
function sortPlayers(items: any) {
|
||||
return [...items].sort((a, b) => {
|
||||
// If a is unranked and b is ranked, move a down
|
||||
if (a.placement === 0 && b.placement !== 0) return 1;
|
||||
// If a is ranked and b is unranked, move a up
|
||||
if (a.placement !== 0 && b.placement === 0) return -1;
|
||||
// If both are ranked, sort numerically ascending (1st, 2nd, 3rd...)
|
||||
return a.placement - b.placement;
|
||||
});
|
||||
}
|
||||
|
||||
// Scroll to and highlight the currently ongoing event
|
||||
$effect(() => {
|
||||
if (focusEventId == null) return;
|
||||
@@ -160,7 +171,9 @@
|
||||
<div
|
||||
class="event-card"
|
||||
class:ongoing-event={event.state == 1}
|
||||
class:completed-event={event.state == 2}
|
||||
bind:this={eventRefs[event.id]}
|
||||
style={event.state == 2 ? `--event-color: ${event.winner.color}` : ''}
|
||||
>
|
||||
<div class="event-header">
|
||||
<a href="/event/{event.id}" class="event-name goldman">{event.name}</a>
|
||||
@@ -188,7 +201,7 @@
|
||||
<span class="brackets-name-text align-text-middle">{bracket.name}</span>
|
||||
<div class="bracket-vertical-sep"></div>
|
||||
</div>
|
||||
{#each bracket.items as player}
|
||||
{#each sortPlayers(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">
|
||||
@@ -204,10 +217,10 @@
|
||||
</svg>
|
||||
</div>
|
||||
<div class="player-name-wrap" use:marquee>
|
||||
<span class="marquee-inner">
|
||||
<a href="/stats/player/{player.id}" class="marquee-inner">
|
||||
{player.firstName}
|
||||
{player.lastName}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
{#if player.placement !== 0}
|
||||
<div class="player-placement goldman">{ordinal(player.placement)}</div>
|
||||
|
||||
@@ -10,7 +10,6 @@ export async function POST({ request }: any) {
|
||||
if (!responseBody) {
|
||||
return new Error('nuh uh');
|
||||
} else {
|
||||
console.log(JSON.stringify(responseBody));
|
||||
if (responseBody.eventId) {
|
||||
let eventData = await getRegisteredEvents(responseBody.eventId);
|
||||
|
||||
@@ -62,7 +61,6 @@ export async function POST({ request }: any) {
|
||||
.set({ placement: currentPlayerPosition })
|
||||
.where(eq(schema.registeredPlayers.id, currentPlayer.registeredPlayerId))
|
||||
.returning();
|
||||
console.log(newPlayerPlacement[0].placement, currentPlayer.firstName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,7 +75,6 @@ export async function POST({ request }: any) {
|
||||
}));
|
||||
|
||||
let newScores = await db.insert(schema.scoreLedger).values(ledgerEntries).returning();
|
||||
console.log(newScores);
|
||||
}
|
||||
|
||||
// Determine the winning team from accumulated scores
|
||||
@@ -97,11 +94,11 @@ export async function POST({ request }: any) {
|
||||
.set({ teamWinner: winningTeamId, state: 2, timeCompleted: Date.now() })
|
||||
.where(eq(schema.registeredEvents.id, responseBody.eventId))
|
||||
.returning();
|
||||
console.log(teamWinnerUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
globalEmitter.emit('scoreUpdate');
|
||||
globalEmitter.emit('eventUpdate');
|
||||
return new Response('coolsies');
|
||||
}
|
||||
}
|
||||
|
||||
12
src/routes/api/logout/+server.ts
Normal file
12
src/routes/api/logout/+server.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { invalidateSession, deleteSessionTokenCookie } from '$lib/server/auth';
|
||||
import type { RequestHandler } from './$types';
|
||||
|
||||
export const POST: RequestHandler = async (event) => {
|
||||
if (event.locals.session) {
|
||||
await invalidateSession(event.locals.session.id);
|
||||
}
|
||||
deleteSessionTokenCookie(event);
|
||||
|
||||
throw redirect(303, '/login');
|
||||
};
|
||||
@@ -251,10 +251,13 @@
|
||||
<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} - scoring {#if event.state == 1}- ONGOING
|
||||
{:else if event.state == 2}- FINISHED
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -210,6 +210,12 @@
|
||||
background-color: color-mix(in srgb, currentColor 18%, transparent);
|
||||
color: var(--ctp-latte-peach);
|
||||
}
|
||||
|
||||
.completed-event {
|
||||
border-color: var(--event-color);
|
||||
/* If you want a subtle background tint like ongoing events usually have: */
|
||||
background-color: color-mix(in srgb, var(--event-color) 10%, transparent);
|
||||
}
|
||||
/* Pulse animation for focused event card */
|
||||
.event-card.highlight-pulse {
|
||||
animation: card-pulse 1.2s ease-out forwards;
|
||||
@@ -337,9 +343,14 @@
|
||||
.player-name-wrap {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
text-decoration: none;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.player-name-wrap:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.marquee-inner {
|
||||
display: inline-block;
|
||||
font-size: 11px;
|
||||
|
||||
23
src/routes/ledger/+page.server.ts
Normal file
23
src/routes/ledger/+page.server.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
// src/routes/admin/+page.server.ts
|
||||
import { error, redirect } from '@sveltejs/kit';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { db } from '$lib/server/db';
|
||||
import { scorers } from '$lib/server/db/schema';
|
||||
import type { PageServerLoad } from './$types';
|
||||
|
||||
export const load: PageServerLoad = async ({ locals }) => {
|
||||
if (!locals.user) {
|
||||
throw redirect(303, '/login');
|
||||
}
|
||||
|
||||
const [row] = await db
|
||||
.select({ role: scorers.role })
|
||||
.from(scorers)
|
||||
.where(eq(scorers.id, locals.user.id));
|
||||
|
||||
if (row?.role !== 'admin') {
|
||||
throw error(403, 'Forbidden');
|
||||
}
|
||||
|
||||
return { user: locals.user };
|
||||
};
|
||||
1
src/routes/ledger/+page.svelte
Normal file
1
src/routes/ledger/+page.svelte
Normal file
@@ -0,0 +1 @@
|
||||
<div>some</div>
|
||||
@@ -4,7 +4,13 @@ import { eq } from 'drizzle-orm';
|
||||
import { db } from '$lib/server/db';
|
||||
import { scorers } from '$lib/server/db/schema';
|
||||
import { generateSessionToken, createSession, setSessionTokenCookie } from '$lib/server/auth';
|
||||
import type { Actions } from './$types';
|
||||
import type { Actions } from '../login/$types';
|
||||
|
||||
import type { PageServerLoad } from '../login/$types';
|
||||
|
||||
export const load: PageServerLoad = async ({ locals }) => {
|
||||
return { user: locals.user };
|
||||
};
|
||||
|
||||
export const actions: Actions = {
|
||||
default: async (event) => {
|
||||
|
||||
@@ -1,34 +1,46 @@
|
||||
<script lang="ts">
|
||||
import type { ActionData } from './$types';
|
||||
import type { ActionData, PageData } from './$types';
|
||||
export let data: PageData;
|
||||
export let form: ActionData;
|
||||
</script>
|
||||
|
||||
<div class="auth-card">
|
||||
<h1>Log in</h1>
|
||||
{#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 class="text-black" 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
|
||||
class="text-black"
|
||||
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>
|
||||
<button type="submit">Log in</button>
|
||||
</form>
|
||||
{/if}
|
||||
|
||||
<!-- <p class="switch">No account? <a href="/signup">Sign up</a></p> -->
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { eq } from 'drizzle-orm';
|
||||
import { db } from '$lib/server/db';
|
||||
import { scorers } from '$lib/server/db/schema';
|
||||
import { generateSessionToken, createSession, setSessionTokenCookie } from '$lib/server/auth';
|
||||
import type { Actions } from './$types';
|
||||
import type { Actions } from '../signup/$types';
|
||||
|
||||
export const actions: Actions = {
|
||||
default: async (event) => {
|
||||
@@ -9,12 +9,19 @@
|
||||
<form method="POST">
|
||||
<label>
|
||||
Username
|
||||
<input name="username" type="text" autocomplete="username" required />
|
||||
<input name="username" class="text-black" type="text" autocomplete="username" required />
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Password
|
||||
<input name="password" type="password" autocomplete="new-password" minlength="8" required />
|
||||
<input
|
||||
class="text-black"
|
||||
name="password"
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
minlength="8"
|
||||
required
|
||||
/>
|
||||
<small>At least 8 characters</small>
|
||||
</label>
|
||||
|
||||
7
src/routes/stats/player/[playerId]/+page.server.ts
Normal file
7
src/routes/stats/player/[playerId]/+page.server.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { getAllInitialInfo } from '$lib/server/databaseManager';
|
||||
|
||||
// Provide initial data for the home page
|
||||
export const load = async () => {
|
||||
return await getAllInitialInfo();
|
||||
};
|
||||
|
||||
20
src/routes/stats/player/[playerId]/+page.svelte
Normal file
20
src/routes/stats/player/[playerId]/+page.svelte
Normal file
@@ -0,0 +1,20 @@
|
||||
<script lang="ts">
|
||||
import type { PageProps } from './$types';
|
||||
import { getPlayerData } from './data.remote';
|
||||
|
||||
let { data, params }: PageProps = $props();
|
||||
|
||||
console.log(data);
|
||||
|
||||
let some = await getPlayerData(params.playerId);
|
||||
console.log(some)
|
||||
|
||||
let playerId = params.playerId;
|
||||
console.log(parseInt(playerId));
|
||||
</script>
|
||||
|
||||
<div class="my-3 flex flex-col justify-center">
|
||||
<div class="player-box w-full" style="--c:red">
|
||||
<!-- <span>{console.log(getPlayerInfo(playerId))}</span> -->
|
||||
</div>
|
||||
</div>
|
||||
8
src/routes/stats/player/[playerId]/data.remote.ts
Normal file
8
src/routes/stats/player/[playerId]/data.remote.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { query } from '$app/server';
|
||||
import { getPlayerInfo } from '$lib/server/databaseManager';
|
||||
|
||||
export const getPlayerData = query(async (playerId: number) => {
|
||||
const playerInfo = await getPlayerInfo(playerId);
|
||||
|
||||
return playerInfo;
|
||||
});
|
||||
Reference in New Issue
Block a user