Compare commits
11 Commits
94c139c7b8
...
8f844472c6
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f844472c6 | |||
| 51d44afb9d | |||
| bbcb04f882 | |||
| dff0d9441f | |||
| 8879c943a4 | |||
| 5f4436180e | |||
| 7a76412a3f | |||
| 6b3baad695 | |||
| 3624ce9c00 | |||
| b4611d262b | |||
| fdfbd4c7c7 |
@@ -1,605 +0,0 @@
|
||||
{
|
||||
"id": "00000000-0000-0000-0000-000000000000",
|
||||
"prevId": "",
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"tables": {
|
||||
"divisions": {
|
||||
"name": "divisions",
|
||||
"columns": {
|
||||
"id": {
|
||||
"autoincrement": true,
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"autoincrement": false,
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"eventAttributions": {
|
||||
"name": "eventAttributions",
|
||||
"columns": {
|
||||
"id": {
|
||||
"autoincrement": true,
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"eventID": {
|
||||
"autoincrement": false,
|
||||
"name": "eventID",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"playerID": {
|
||||
"autoincrement": false,
|
||||
"name": "playerID",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"placement": {
|
||||
"default": 0,
|
||||
"autoincrement": false,
|
||||
"name": "placement",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"eventAttributions_placement_scoringPresets_placement_fk": {
|
||||
"name": "eventAttributions_placement_scoringPresets_placement_fk",
|
||||
"tableFrom": "eventAttributions",
|
||||
"tableTo": "scoringPresets",
|
||||
"columnsFrom": [
|
||||
"placement"
|
||||
],
|
||||
"columnsTo": [
|
||||
"placement"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"eventAttributions_playerID_players_id_fk": {
|
||||
"name": "eventAttributions_playerID_players_id_fk",
|
||||
"tableFrom": "eventAttributions",
|
||||
"tableTo": "players",
|
||||
"columnsFrom": [
|
||||
"playerID"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"eventAttributions_eventID_events_id_fk": {
|
||||
"name": "eventAttributions_eventID_events_id_fk",
|
||||
"tableFrom": "eventAttributions",
|
||||
"tableTo": "events",
|
||||
"columnsFrom": [
|
||||
"eventID"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"events": {
|
||||
"name": "events",
|
||||
"columns": {
|
||||
"id": {
|
||||
"autoincrement": true,
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"autoincrement": false,
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"preset": {
|
||||
"autoincrement": false,
|
||||
"name": "preset",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"division": {
|
||||
"autoincrement": false,
|
||||
"name": "division",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"order": {
|
||||
"autoincrement": false,
|
||||
"name": "order",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"state": {
|
||||
"default": 0,
|
||||
"autoincrement": false,
|
||||
"name": "state",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"timeCompleted": {
|
||||
"autoincrement": false,
|
||||
"name": "timeCompleted",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"events_division_divisions_id_fk": {
|
||||
"name": "events_division_divisions_id_fk",
|
||||
"tableFrom": "events",
|
||||
"tableTo": "divisions",
|
||||
"columnsFrom": [
|
||||
"division"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"events_preset_scoringPresets_preset_fk": {
|
||||
"name": "events_preset_scoringPresets_preset_fk",
|
||||
"tableFrom": "events",
|
||||
"tableTo": "scoringPresets",
|
||||
"columnsFrom": [
|
||||
"preset"
|
||||
],
|
||||
"columnsTo": [
|
||||
"preset"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"ledger": {
|
||||
"name": "ledger",
|
||||
"columns": {
|
||||
"id": {
|
||||
"autoincrement": true,
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"timestamp": {
|
||||
"default": "(unixepoch())",
|
||||
"autoincrement": false,
|
||||
"name": "timestamp",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"type": {
|
||||
"default": "'event'",
|
||||
"autoincrement": false,
|
||||
"name": "type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"event": {
|
||||
"autoincrement": false,
|
||||
"name": "event",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"scorer": {
|
||||
"autoincrement": false,
|
||||
"name": "scorer",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"ledger_scorer_scorers_id_fk": {
|
||||
"name": "ledger_scorer_scorers_id_fk",
|
||||
"tableFrom": "ledger",
|
||||
"tableTo": "scorers",
|
||||
"columnsFrom": [
|
||||
"scorer"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"ledger_event_events_id_fk": {
|
||||
"name": "ledger_event_events_id_fk",
|
||||
"tableFrom": "ledger",
|
||||
"tableTo": "events",
|
||||
"columnsFrom": [
|
||||
"event"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"ledgerScores": {
|
||||
"name": "ledgerScores",
|
||||
"columns": {
|
||||
"id": {
|
||||
"autoincrement": true,
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"ledgerID": {
|
||||
"autoincrement": false,
|
||||
"name": "ledgerID",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"team": {
|
||||
"autoincrement": false,
|
||||
"name": "team",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"placement": {
|
||||
"autoincrement": false,
|
||||
"name": "placement",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"points": {
|
||||
"default": 0,
|
||||
"autoincrement": false,
|
||||
"name": "points",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"ledgerScores_placement_scoringPresets_placement_fk": {
|
||||
"name": "ledgerScores_placement_scoringPresets_placement_fk",
|
||||
"tableFrom": "ledgerScores",
|
||||
"tableTo": "scoringPresets",
|
||||
"columnsFrom": [
|
||||
"placement"
|
||||
],
|
||||
"columnsTo": [
|
||||
"placement"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"ledgerScores_team_teams_id_fk": {
|
||||
"name": "ledgerScores_team_teams_id_fk",
|
||||
"tableFrom": "ledgerScores",
|
||||
"tableTo": "teams",
|
||||
"columnsFrom": [
|
||||
"team"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"ledgerScores_ledgerID_ledger_id_fk": {
|
||||
"name": "ledgerScores_ledgerID_ledger_id_fk",
|
||||
"tableFrom": "ledgerScores",
|
||||
"tableTo": "ledger",
|
||||
"columnsFrom": [
|
||||
"ledgerID"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"players": {
|
||||
"name": "players",
|
||||
"columns": {
|
||||
"id": {
|
||||
"autoincrement": true,
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"firstName": {
|
||||
"autoincrement": false,
|
||||
"name": "firstName",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"lastName": {
|
||||
"autoincrement": false,
|
||||
"name": "lastName",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"team": {
|
||||
"autoincrement": false,
|
||||
"name": "team",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"division": {
|
||||
"autoincrement": false,
|
||||
"name": "division",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"players_division_divisions_id_fk": {
|
||||
"name": "players_division_divisions_id_fk",
|
||||
"tableFrom": "players",
|
||||
"tableTo": "divisions",
|
||||
"columnsFrom": [
|
||||
"division"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"players_team_teams_id_fk": {
|
||||
"name": "players_team_teams_id_fk",
|
||||
"tableFrom": "players",
|
||||
"tableTo": "teams",
|
||||
"columnsFrom": [
|
||||
"team"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"scorers": {
|
||||
"name": "scorers",
|
||||
"columns": {
|
||||
"id": {
|
||||
"autoincrement": true,
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"firstName": {
|
||||
"autoincrement": false,
|
||||
"name": "firstName",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"lastName": {
|
||||
"autoincrement": false,
|
||||
"name": "lastName",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"email": {
|
||||
"autoincrement": false,
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"password": {
|
||||
"autoincrement": false,
|
||||
"name": "password",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"displayName": {
|
||||
"autoincrement": false,
|
||||
"name": "displayName",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"role": {
|
||||
"default": "'scorer'",
|
||||
"autoincrement": false,
|
||||
"name": "role",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"scoringPresets": {
|
||||
"name": "scoringPresets",
|
||||
"columns": {
|
||||
"id": {
|
||||
"autoincrement": true,
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"preset": {
|
||||
"autoincrement": false,
|
||||
"name": "preset",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"placement": {
|
||||
"default": 0,
|
||||
"autoincrement": false,
|
||||
"name": "placement",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"points": {
|
||||
"default": 0,
|
||||
"autoincrement": false,
|
||||
"name": "points",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"teams": {
|
||||
"name": "teams",
|
||||
"columns": {
|
||||
"id": {
|
||||
"autoincrement": true,
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"autoincrement": false,
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"color": {
|
||||
"default": "'white'",
|
||||
"autoincrement": false,
|
||||
"name": "color",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
}
|
||||
},
|
||||
"views": {
|
||||
"teamScoresView": {
|
||||
"columns": {
|
||||
"id": {
|
||||
"autoincrement": false,
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"name": {
|
||||
"autoincrement": false,
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"color": {
|
||||
"autoincrement": false,
|
||||
"name": "color",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"totalPoints": {
|
||||
"autoincrement": false,
|
||||
"name": "totalPoints",
|
||||
"type": "numeric",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"isExisting": false,
|
||||
"name": "teamScoresView",
|
||||
"definition": "select \"teams\".\"id\", \"teams\".\"name\", \"teams\".\"color\", sum(\"ledgerScores\".\"points\") as \"totalPoints\" from \"teams\" left join \"ledgerScores\" on \"teams\".\"id\" = \"ledgerScores\".\"team\" group by \"teams\".\"id\""
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"version": "7",
|
||||
"dialect": "sqlite",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "6",
|
||||
"when": 1778078709643,
|
||||
"tag": "0000_broken_weapon_omega",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
229
scripts/seed.ts
229
scripts/seed.ts
@@ -1,4 +1,5 @@
|
||||
import { drizzle } from 'drizzle-orm/libsql';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { createClient } from '@libsql/client';
|
||||
import * as schema from '../src/lib/server/db/schema.js';
|
||||
import { parse } from 'csv-parse/sync';
|
||||
@@ -15,165 +16,159 @@ if (!process.env.DATABASE_URL) throw new Error('DATABASE_URL is not set');
|
||||
const client = createClient({ url: process.env.DATABASE_URL });
|
||||
const db = drizzle(client, { schema });
|
||||
|
||||
function readCSV(filename: string): Record<string, string>[] {
|
||||
function readCSV(filename: string): Record<string, any>[] {
|
||||
const content = readFileSync(join(dataDir, filename), 'utf-8');
|
||||
return parse(content, { columns: true, skip_empty_lines: true });
|
||||
return parse(content, {
|
||||
columns: true,
|
||||
skip_empty_lines: true,
|
||||
cast: true
|
||||
});
|
||||
}
|
||||
|
||||
async function seed() {
|
||||
console.log('Resetting database...');
|
||||
|
||||
// Disable foreign keys globally during setup to prevent structural mismatches
|
||||
await client.execute('PRAGMA foreign_keys = OFF');
|
||||
|
||||
await db.delete(schema.eventAttributions);
|
||||
await db.delete(schema.ledgerScores);
|
||||
await db.delete(schema.ledger);
|
||||
await db.delete(schema.events);
|
||||
await db.delete(schema.players);
|
||||
await db.delete(schema.scorers);
|
||||
await db.delete(schema.scoreLedger);
|
||||
await db.delete(schema.mainLedger);
|
||||
await db.delete(schema.registeredPlayers);
|
||||
await db.delete(schema.registeredEvents);
|
||||
await db.delete(schema.eventTypes);
|
||||
await db.delete(schema.scoringPresets);
|
||||
await db.delete(schema.players);
|
||||
await db.delete(schema.divisions);
|
||||
await db.delete(schema.teams);
|
||||
|
||||
await client.execute('DELETE FROM sqlite_sequence');
|
||||
|
||||
console.log('Database reset complete.');
|
||||
console.log('Database reset complete. Seeding...');
|
||||
|
||||
console.log('Seeding teams...');
|
||||
// --- 1. Teams ---
|
||||
const teamsCSV = readCSV('teams.csv');
|
||||
for (const row of teamsCSV) {
|
||||
const result = await db
|
||||
.insert(schema.teams)
|
||||
.values({ name: row.name, color: row.color })
|
||||
.returning();
|
||||
console.log(` Inserted team: ${result[0].name} (id: ${result[0].id})`);
|
||||
await db.insert(schema.teams).values({ name: row.team_name, color: row.color });
|
||||
console.log(` → Team: ${row.team_name} (${row.color})`);
|
||||
}
|
||||
|
||||
console.log('Seeding divisions...');
|
||||
// --- 2. Divisions ---
|
||||
const divisionsCSV = readCSV('divisions.csv');
|
||||
for (const row of divisionsCSV) {
|
||||
const result = await db.insert(schema.divisions).values({ name: row.name }).returning();
|
||||
console.log(` Inserted division: ${result[0].name} (id: ${result[0].id})`);
|
||||
await db.insert(schema.divisions).values({ name: row.div_name });
|
||||
console.log(` → Division: ${row.div_name}`);
|
||||
}
|
||||
|
||||
console.log('Seeding scoring presets...');
|
||||
// --- 3. Scoring Presets ---
|
||||
const scoringPresetsCSV = readCSV('scoringPresets.csv');
|
||||
for (const row of scoringPresetsCSV) {
|
||||
const result = await db
|
||||
.insert(schema.scoringPresets)
|
||||
.values({
|
||||
presetID: parseInt(row.presetID),
|
||||
placement: parseInt(row.placement),
|
||||
points: parseInt(row.points)
|
||||
})
|
||||
.returning();
|
||||
console.log(
|
||||
` Inserted preset: ${result[0].presetID} placement ${result[0].placement} = ${result[0].points}pts`
|
||||
);
|
||||
await db.insert(schema.scoringPresets).values({
|
||||
presetID: row.preset,
|
||||
placement: row.placement,
|
||||
points: row.points
|
||||
});
|
||||
console.log(` → Preset ${row.preset}: placement ${row.placement} = ${row.points}pts`);
|
||||
}
|
||||
|
||||
console.log('Seeding scorers...');
|
||||
const scorersCSV = readCSV('scorers.csv');
|
||||
for (const row of scorersCSV) {
|
||||
const result = await db
|
||||
.insert(schema.scorers)
|
||||
.values({
|
||||
firstName: row.firstName,
|
||||
lastName: row.lastName,
|
||||
email: row.email,
|
||||
password: row.password,
|
||||
displayName: row.displayName,
|
||||
role: row.role
|
||||
})
|
||||
.returning();
|
||||
console.log(` Inserted scorer: ${result[0].displayName}`);
|
||||
}
|
||||
|
||||
console.log('Seeding players...');
|
||||
const teams = await db.select().from(schema.teams);
|
||||
const divisions = await db.select().from(schema.divisions);
|
||||
const teamMap = new Map(teams.map((t) => [t.name, t.id]));
|
||||
const divisionMap = new Map(divisions.map((d) => [d.name, d.id]));
|
||||
// Maps for dynamic relational lookups
|
||||
const dbTeams = await db.select().from(schema.teams);
|
||||
const dbDivisions = await db.select().from(schema.divisions);
|
||||
const teamMap = new Map(dbTeams.map((t) => [t.name, t.id]));
|
||||
const divisionMap = new Map(dbDivisions.map((d) => [d.name, d.id]));
|
||||
const divisionNameMap = new Map([...divisionMap.entries()].map(([name, id]) => [id, name]));
|
||||
|
||||
// --- 4. Players ---
|
||||
const playersCSV = readCSV('players.csv');
|
||||
for (const row of playersCSV) {
|
||||
const teamId = teamMap.get(row.team);
|
||||
const divisionId = divisionMap.get(row.division);
|
||||
if (!teamId) throw new Error(`Team "${row.team}" not found`);
|
||||
if (!divisionId) throw new Error(`Division "${row.division}" not found`);
|
||||
|
||||
const result = await db
|
||||
.insert(schema.players)
|
||||
.values({
|
||||
firstName: row.firstName,
|
||||
lastName: row.lastName,
|
||||
team: teamId,
|
||||
division: divisionId
|
||||
})
|
||||
.returning();
|
||||
console.log(` Inserted player: ${result[0].firstName} ${result[0].lastName}`);
|
||||
}
|
||||
|
||||
console.log('Seeding events...');
|
||||
const scoringPresets = await db.select().from(schema.scoringPresets);
|
||||
const presetMap = new Map(scoringPresets.map((p) => [p.presetID, p.presetID]));
|
||||
const divisionMapForEvents = new Map(divisions.map((d) => [d.name, d.id]));
|
||||
|
||||
const eventsCSV = readCSV('events.csv');
|
||||
for (const row of eventsCSV) {
|
||||
const preset = parseInt(row.preset);
|
||||
const divisionId = divisionMapForEvents.get(row.division);
|
||||
if (!divisionId) throw new Error(`Division "${row.division}" not found`);
|
||||
|
||||
const result = await db
|
||||
.insert(schema.events)
|
||||
.values({
|
||||
name: row.name,
|
||||
preset,
|
||||
division: divisionId,
|
||||
order: parseInt(row.order),
|
||||
state: parseInt(row.state)
|
||||
})
|
||||
.returning();
|
||||
console.log(` Inserted event: ${result[0].name}`);
|
||||
}
|
||||
|
||||
console.log('Seeding event attributions...');
|
||||
const events = await db.select().from(schema.events);
|
||||
const players = await db.select().from(schema.players);
|
||||
const eventMap = new Map(events.map((e) => [e.name, e.id]));
|
||||
const playerMap = new Map(players.map((p) => [`${p.firstName} ${p.lastName}`, p.id]));
|
||||
|
||||
const eventAttributionsCSV = readCSV('eventAttributions.csv');
|
||||
for (const row of eventAttributionsCSV) {
|
||||
const eventId = eventMap.get(row.eventID);
|
||||
const playerId = playerMap.get(row.playerID);
|
||||
const placement = parseInt(row.placement);
|
||||
|
||||
if (!eventId) throw new Error(`Event "${row.eventID}" not found`);
|
||||
if (!playerId) throw new Error(`Player "${row.playerID}" not found`);
|
||||
if (placement <= 0) continue;
|
||||
|
||||
const result = await db
|
||||
.insert(schema.eventAttributions)
|
||||
.values({
|
||||
eventID: eventId,
|
||||
playerID: playerId,
|
||||
placement
|
||||
})
|
||||
.returning();
|
||||
await db.insert(schema.players).values({
|
||||
firstName: row.firstName,
|
||||
lastName: row.lastName,
|
||||
team: teamId,
|
||||
division: divisionId || null
|
||||
});
|
||||
console.log(
|
||||
` Inserted attribution: event=${row.eventID}, player=${row.playerID}, placement=${placement}`
|
||||
` → Player: ${row.firstName} ${row.lastName} | team ${teamId} | division ${divisionId}`
|
||||
);
|
||||
}
|
||||
|
||||
console.log('\nSeeding complete!');
|
||||
console.log('Ledger and ledgerScores tables are empty (reset).');
|
||||
// --- 5. Event Types ---
|
||||
const eventTypesCSV = readCSV('eventTypes.csv');
|
||||
for (const row of eventTypesCSV) {
|
||||
await db.insert(schema.eventTypes).values({
|
||||
name: row.event_name,
|
||||
preset: row.preset
|
||||
});
|
||||
console.log(` → Event Type: ${row.event_name} (preset ${row.preset})`);
|
||||
}
|
||||
|
||||
const dbEventTypes = await db.select().from(schema.eventTypes);
|
||||
const eventTypeMap = new Map(dbEventTypes.map((et) => [et.name, et.id]));
|
||||
|
||||
// --- 6. Registered Events ---
|
||||
// Change the Map key type to a string (the event name)
|
||||
const eventNameMap = new Map<string, number>();
|
||||
const registeredEventsCSV = readCSV('registeredEvents.csv');
|
||||
|
||||
for (const row of registeredEventsCSV) {
|
||||
const eventTypeId = eventTypeMap.get(row.event_type);
|
||||
const divisionId = divisionMap.get(row.division);
|
||||
|
||||
if (!eventTypeId) throw new Error(`Event Type "${row.event_type}" not found`);
|
||||
if (!divisionId) throw new Error(`Division "${row.division}" not found`);
|
||||
|
||||
const [inserted] = await db
|
||||
.insert(schema.registeredEvents)
|
||||
.values({
|
||||
eventType: eventTypeId,
|
||||
division: divisionId,
|
||||
state: row.event_state || 0,
|
||||
timeCompleted: row.time_completed || null
|
||||
})
|
||||
.returning();
|
||||
console.log(` → Registered Event [id:${inserted.id}]: ${row.event_type} | ${row.division}`);
|
||||
|
||||
// Map the textual event name (e.g., "100m Sprint") to the generated DB ID
|
||||
eventNameMap.set(`${row.event_type}|${row.division}`, inserted.id);
|
||||
}
|
||||
|
||||
// --- 7. Registered Players ---
|
||||
const dbPlayers = await db.select().from(schema.players);
|
||||
const playerMap = new Map(dbPlayers.map((p) => [`${p.firstName} ${p.lastName}`, p]));
|
||||
|
||||
const registeredPlayersCSV = readCSV('registeredPlayers.csv');
|
||||
for (const row of registeredPlayersCSV) {
|
||||
const player = playerMap.get(row.player_registered);
|
||||
const divisionName = divisionNameMap.get(player?.division ?? -1);
|
||||
const actualEventId = eventNameMap.get(`${row.event_registered}|${divisionName}`);
|
||||
|
||||
if (!player) throw new Error(`Player "${row.player_registered}" not found`);
|
||||
if (!actualEventId)
|
||||
throw new Error(
|
||||
`Registered Event "${row.event_registered}" for division "${divisionName}" not found`
|
||||
);
|
||||
|
||||
await db.insert(schema.registeredPlayers).values({
|
||||
playerID: player.id,
|
||||
registeredEventID: actualEventId,
|
||||
placement: row.player_placement || 0
|
||||
});
|
||||
console.log(
|
||||
` → Registered Player: ${row.player_registered} → ${row.event_registered} (division: ${divisionName}) [eventId:${actualEventId}]`
|
||||
);
|
||||
}
|
||||
|
||||
// Re-enable Foreign Key constraints now that the data is built cleanly
|
||||
await client.execute('PRAGMA foreign_keys = ON');
|
||||
|
||||
console.log('\n✅ Seeding complete!');
|
||||
await client.close();
|
||||
}
|
||||
|
||||
seed().catch((err) => {
|
||||
console.error('Seed failed:', err);
|
||||
console.error('❌ Seed failed:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@@ -1,94 +1,84 @@
|
||||
import { sql, eq } from 'drizzle-orm';
|
||||
import { integer, sqliteTable, text, sqliteView } from 'drizzle-orm/sqlite-core';
|
||||
|
||||
export const players = sqliteTable('players', {
|
||||
id: integer('players_id').primaryKey({ autoIncrement: true }),
|
||||
firstName: text('firstName').notNull(),
|
||||
lastName: text('lastName').notNull(),
|
||||
team: integer('team')
|
||||
.references(() => teams.id)
|
||||
.notNull(),
|
||||
division: integer('division').references(() => divisions.id)
|
||||
});
|
||||
|
||||
export const teams = sqliteTable('teams', {
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
name: text('name').notNull(),
|
||||
id: integer('teams_id').primaryKey({ autoIncrement: true }),
|
||||
name: text('team_name').notNull(),
|
||||
color: text('color').notNull().default('white')
|
||||
});
|
||||
|
||||
export const divisions = sqliteTable('divisions', {
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
name: text('name').notNull()
|
||||
});
|
||||
|
||||
export const eventAttributions = sqliteTable('eventAttributions', {
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
eventID: integer('eventID')
|
||||
.references(() => events.id)
|
||||
.notNull(),
|
||||
playerID: integer('playerID')
|
||||
.references(() => players.id)
|
||||
.notNull(),
|
||||
placement: integer('placement')
|
||||
.references(() => scoringPresets.placement)
|
||||
.default(0)
|
||||
.notNull()
|
||||
id: integer('divisions_id').primaryKey({ autoIncrement: true }),
|
||||
name: text('div_name').notNull()
|
||||
});
|
||||
|
||||
export const scoringPresets = sqliteTable('scoringPresets', {
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
id: integer('scoringPresets_id').primaryKey({ autoIncrement: true }),
|
||||
presetID: integer('preset').notNull(),
|
||||
placement: integer('placement').notNull().default(0),
|
||||
points: integer('points').notNull().default(0)
|
||||
});
|
||||
|
||||
export const events = sqliteTable('events', {
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
name: text('name').notNull(),
|
||||
export const eventTypes = sqliteTable('eventTypes', {
|
||||
id: integer('eventTypes_id').primaryKey({ autoIncrement: true }),
|
||||
name: text('event_name').notNull(),
|
||||
preset: integer('preset')
|
||||
.references(() => scoringPresets.presetID)
|
||||
.notNull(),
|
||||
division: integer('division')
|
||||
.references(() => divisions.id)
|
||||
.notNull(),
|
||||
order: integer('order').notNull(),
|
||||
state: integer('state').default(0).notNull(),
|
||||
timeCompleted: integer('timeCompleted')
|
||||
});
|
||||
|
||||
export const players = sqliteTable('players', {
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
firstName: text('firstName').notNull(),
|
||||
lastName: text('lastName').notNull(),
|
||||
team: integer('team')
|
||||
.references(() => teams.id)
|
||||
.notNull(),
|
||||
division: integer('division')
|
||||
.references(() => divisions.id)
|
||||
.notNull()
|
||||
});
|
||||
|
||||
export const scorers = sqliteTable('scorers', {
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
firstName: text('firstName').notNull(),
|
||||
lastName: text('lastName').notNull(),
|
||||
email: text('email').notNull(),
|
||||
password: text('password').notNull(),
|
||||
displayName: text('displayName').notNull(),
|
||||
role: text('role').notNull().default('scorer')
|
||||
export const registeredEvents = sqliteTable('registeredEvents', {
|
||||
id: integer('registeredEvents_id').primaryKey({ autoIncrement: true }),
|
||||
eventType: integer('event_type')
|
||||
.references(() => eventTypes.id)
|
||||
.notNull(),
|
||||
division: integer('division')
|
||||
.references(() => divisions.id)
|
||||
.notNull(),
|
||||
state: integer('event_state').notNull().default(0),
|
||||
timeCompleted: integer('time_completed')
|
||||
});
|
||||
|
||||
export const ledger = sqliteTable('ledger', {
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
timestamp: integer('timestamp', { mode: 'timestamp' })
|
||||
export const registeredPlayers = sqliteTable('registeredPlayers', {
|
||||
id: integer('registeredPlayers_id').primaryKey({ autoIncrement: true }),
|
||||
playerID: integer('player_registered')
|
||||
.references(() => players.id)
|
||||
.notNull(),
|
||||
registeredEventID: integer('event_registered')
|
||||
.references(() => registeredEvents.id)
|
||||
.notNull(),
|
||||
placement: integer('player_placement').notNull().default(0)
|
||||
});
|
||||
|
||||
export const mainLedger = sqliteTable('mainLedger', {
|
||||
id: integer('mainLedger_id').primaryKey({ autoIncrement: true }),
|
||||
timestamp: integer('ledger_timestamp', { mode: 'timestamp' })
|
||||
.notNull()
|
||||
.default(sql`(unixepoch())`),
|
||||
type: text('type').notNull().default('event'),
|
||||
event: integer('event').references(() => events.id),
|
||||
scorer: text('scorer').references(() => scorers.id)
|
||||
updateType: text('update_type').notNull().default('event'),
|
||||
registeredEvent: integer('registered_event_reference').references(() => registeredEvents.id),
|
||||
scorer: text('scorer_logged').default('some dude idk')
|
||||
});
|
||||
|
||||
export const ledgerScores = sqliteTable('ledgerScores', {
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
ledgerID: integer('ledgerID')
|
||||
.references(() => ledger.id)
|
||||
export const scoreLedger = sqliteTable('scoresLedger', {
|
||||
id: integer('scoreLedger_id').primaryKey({ autoIncrement: true }),
|
||||
ledgerID: integer('ledger_address')
|
||||
.references(() => mainLedger.id)
|
||||
.notNull(),
|
||||
team: integer('team')
|
||||
teamID: integer('team_being_scored')
|
||||
.references(() => teams.id)
|
||||
.notNull(),
|
||||
placement: integer('placement').references(() => scoringPresets.placement),
|
||||
points: integer('points').notNull().default(0)
|
||||
points: integer('pointsAwarded').default(0).notNull()
|
||||
});
|
||||
|
||||
export const teamScoresView = sqliteView('teamScoresView').as((qb) => {
|
||||
@@ -97,9 +87,43 @@ export const teamScoresView = sqliteView('teamScoresView').as((qb) => {
|
||||
teamId: teams.id,
|
||||
teamName: teams.name,
|
||||
teamColor: teams.color,
|
||||
totalPoints: sql<number>`sum(${ledgerScores.points})`.mapWith(Number).as('totalPoints')
|
||||
totalPoints: sql<number>`sum(${scoreLedger.points})`.mapWith(Number).as('totalPoints')
|
||||
})
|
||||
.from(teams)
|
||||
.leftJoin(ledgerScores, eq(teams.id, ledgerScores.team))
|
||||
.groupBy(teams.id);
|
||||
.leftJoin(scoreLedger, eq(teams.id, scoreLedger.teamID))
|
||||
.groupBy(teams.id, teams.name, teams.color);
|
||||
});
|
||||
|
||||
export const registeredEventsView = sqliteView('registeredEventsView').as((qb) => {
|
||||
return qb
|
||||
.select({
|
||||
eventId: registeredEvents.id,
|
||||
eventName: eventTypes.name,
|
||||
division: divisions.name,
|
||||
state: registeredEvents.state,
|
||||
timeCompleted: registeredEvents.timeCompleted
|
||||
})
|
||||
.from(registeredEvents)
|
||||
.innerJoin(eventTypes, eq(registeredEvents.eventType, eventTypes.id))
|
||||
.innerJoin(divisions, eq(registeredEvents.division, divisions.id));
|
||||
});
|
||||
|
||||
export const registeredEventPlayersView = sqliteView('registeredEventPlayersView').as((qb) => {
|
||||
return qb
|
||||
.select({
|
||||
playerId: players.id,
|
||||
firstName: players.firstName,
|
||||
lastName: players.lastName,
|
||||
placement: registeredPlayers.placement,
|
||||
eventId: registeredEvents.id,
|
||||
eventName: eventTypes.name,
|
||||
teamId: teams.id,
|
||||
teamName: teams.name,
|
||||
teamColor: teams.color
|
||||
})
|
||||
.from(registeredPlayers)
|
||||
.innerJoin(registeredEvents, eq(registeredPlayers.registeredEventID, registeredEvents.id))
|
||||
.innerJoin(eventTypes, eq(registeredEvents.eventType, eventTypes.id))
|
||||
.innerJoin(players, eq(registeredPlayers.playerID, players.id))
|
||||
.innerJoin(teams, eq(players.team, teams.id));
|
||||
});
|
||||
|
||||
44
src/lib/server/endpoint.ts
Normal file
44
src/lib/server/endpoint.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
export async function generateEndpoint(
|
||||
startFunction?: (enqueue: (data: any) => void) => void | Promise<void | (() => void)>
|
||||
) {
|
||||
let streamController: ReadableStreamDefaultController | null = null;
|
||||
let cleanupFunction: (() => void) | void = undefined;
|
||||
|
||||
const enqueue = (data: any) => {
|
||||
let transferdata = JSON.stringify(data);
|
||||
// stringify data and add to controller queue
|
||||
if (streamController) {
|
||||
streamController.enqueue(`data: ${transferdata}\n\n`);
|
||||
} else {
|
||||
console.log('no controller');
|
||||
}
|
||||
};
|
||||
|
||||
const stream = new ReadableStream({
|
||||
async start(controller) {
|
||||
streamController = controller;
|
||||
if (startFunction) {
|
||||
const result = await startFunction(enqueue);
|
||||
if (typeof result === 'function') {
|
||||
cleanupFunction = result;
|
||||
}
|
||||
}
|
||||
},
|
||||
async cancel() {
|
||||
if (cleanupFunction) cleanupFunction();
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
response: new Response(stream, {
|
||||
headers: {
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Cache-Control': 'no-cache',
|
||||
Connection: 'keep-alive',
|
||||
'X-Accel-Buffering': 'no'
|
||||
}
|
||||
}),
|
||||
enqueue: enqueue,
|
||||
controller: streamController
|
||||
};
|
||||
}
|
||||
74
src/lib/server/eventManager.ts
Normal file
74
src/lib/server/eventManager.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { EventEmitter } from 'node:events';
|
||||
import { db } from '$lib/server/db';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import * as schema from '$lib/server/db/schema';
|
||||
|
||||
let testScore = 0;
|
||||
|
||||
// Emitter that emits
|
||||
export const globalEmitter = new EventEmitter();
|
||||
|
||||
// Increment score for testing (remove ts)
|
||||
const increment = () => {
|
||||
testScore++;
|
||||
console.log('score incremented', testScore);
|
||||
globalEmitter.emit('scoreUpdate');
|
||||
};
|
||||
|
||||
// Increment scores when there is an emit
|
||||
globalEmitter.on('incrementScores', increment);
|
||||
|
||||
// For page.server.ts so that it doesnt look weird before loading
|
||||
export async function getAllInitialInfo() {
|
||||
return {
|
||||
teams: await getTeams(),
|
||||
events: await getRegisteredEvents()
|
||||
};
|
||||
}
|
||||
|
||||
// Get teams object from database
|
||||
export async function getTeams() {
|
||||
const allTeams = await db.select().from(schema.teamScoresView);
|
||||
return {
|
||||
teams: allTeams.map((team) => ({
|
||||
name: team.teamName,
|
||||
color: team.teamColor,
|
||||
points: team.totalPoints || testScore
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
// Get all registered events from database
|
||||
export async function getRegisteredEvents() {
|
||||
const allEvents = await db.select().from(schema.registeredEventsView);
|
||||
return {
|
||||
events: allEvents.map((events) => ({
|
||||
id: events.eventId,
|
||||
name: events.eventName,
|
||||
division: events.division,
|
||||
state: events.state,
|
||||
completed: events.timeCompleted || 0
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
// Get all players with an event id specified
|
||||
export async function getAllRegisteredEventPlayers(eventId: number) {
|
||||
const eventPlayers = await db
|
||||
.select()
|
||||
.from(schema.registeredEventPlayersView)
|
||||
.where(eq(schema.registeredEventPlayersView.eventId, eventId));
|
||||
return {
|
||||
eventPlayers: eventPlayers.map((players) => ({
|
||||
id: players.playerId,
|
||||
firstName: players.firstName,
|
||||
lastName: players.lastName,
|
||||
placement: players.placement,
|
||||
eventId: players.eventId,
|
||||
eventName: players.eventName,
|
||||
teamId: players.teamId,
|
||||
teamName: players.teamName,
|
||||
teamColor: players.teamColor
|
||||
}))
|
||||
};
|
||||
}
|
||||
@@ -1,21 +1,7 @@
|
||||
import { db } from '$lib/server/db';
|
||||
import * as schema from '$lib/server/db/schema';
|
||||
|
||||
export class House {
|
||||
name: string = '';
|
||||
color: string = 'white';
|
||||
points: number = $state(0);
|
||||
}
|
||||
import { getAllInitialInfo } from '$lib/server/eventManager';
|
||||
|
||||
// Literally only here so that the frontend has the right structure
|
||||
export const load = async () => {
|
||||
const allTeams = await db.select().from(schema.teamScoresView);
|
||||
console.log(allTeams);
|
||||
return {
|
||||
teams: allTeams.map((team) => ({
|
||||
...team,
|
||||
name: team.teamName,
|
||||
color: team.teamColor,
|
||||
points: team.totalPoints || 0
|
||||
}))
|
||||
};
|
||||
return await getAllInitialInfo();
|
||||
};
|
||||
|
||||
|
||||
@@ -1,24 +1,107 @@
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
// import { enhance } from '$app/forms';
|
||||
import Table from './Table.svelte';
|
||||
|
||||
// Get initial data from the load thing (innacurate lol)
|
||||
let { data }: { data: import('./$types').PageData } = $props();
|
||||
|
||||
let leaderboard = $derived([...data.teams].sort((a, b) => b.points - a.points));
|
||||
// Derived unordered
|
||||
let teams = $derived(data.teams.teams);
|
||||
|
||||
let eventTable = $derived(data.events.events);
|
||||
|
||||
//// Leaderboard Database logic
|
||||
// new event source for websocket
|
||||
let scoreEndpoint: EventSource;
|
||||
let eventEndpoint: EventSource;
|
||||
|
||||
onMount(() => {
|
||||
// get endpoint
|
||||
scoreEndpoint = new EventSource('/api/teams');
|
||||
|
||||
// when you get a message do something
|
||||
scoreEndpoint.onmessage = (e) => {
|
||||
const teamsData = JSON.parse(e.data);
|
||||
// If the message has a teams object update the score thing
|
||||
if (teamsData['teams']) {
|
||||
teams = teamsData['teams'];
|
||||
console.log('teams updated');
|
||||
}
|
||||
};
|
||||
// Player endpoint
|
||||
eventEndpoint = new EventSource('/api/registeredEvents');
|
||||
|
||||
eventEndpoint.onmessage = (e) => {
|
||||
const eventData = JSON.parse(e.data);
|
||||
console.log(eventData);
|
||||
eventTable = eventData;
|
||||
};
|
||||
});
|
||||
|
||||
// When window destroyed close the websocket connection
|
||||
onDestroy(() => scoreEndpoint?.close());
|
||||
|
||||
// Order leaderboard so that its displayed correctly
|
||||
let leaderboard = $derived([...teams].sort((a, b) => b.points - a.points));
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="grid-cols-1 justify-center p-10 sm:grid sm:grid-cols-2 md:grid-cols-2 lg:flex lg:flex-row"
|
||||
>
|
||||
{#each leaderboard as house (house.name)}
|
||||
{#snippet header()}
|
||||
<th>Event</th>
|
||||
<th>Division</th>
|
||||
<th>Players</th>
|
||||
{/snippet}
|
||||
|
||||
{#snippet row(d: any)}
|
||||
<td>{d.name}</td>
|
||||
<td>{d.division}</td>
|
||||
<td>
|
||||
<div class="flex justify-center">
|
||||
{#each d.registeredPlayers as player}
|
||||
<div
|
||||
style="--theme-color: {player.teamColor};"
|
||||
class="player-box w-min-0 m-1 flex aspect-square h-[100px] flex-col rounded-md border-2"
|
||||
>
|
||||
<div>{player.firstName} {player.lastName}</div>
|
||||
<div></div>
|
||||
<div>pic</div>
|
||||
{#if player.placement != 0}
|
||||
<div>{player.placement}</div>
|
||||
{/if}
|
||||
<div>{player.teamName}</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</td>
|
||||
{/snippet}
|
||||
|
||||
<svelte:window onbeforeunload={() => scoreEndpoint?.close()} />
|
||||
|
||||
<div class="p-[2vw]">
|
||||
{#each leaderboard as team (team.name)}
|
||||
<div
|
||||
style="--theme-color: {house.color};"
|
||||
class="--theme-color: m-5 border-solid {house.color} score-box aspect-1/1 rounded-2xl border-3 first:col-span-2 sm:first:aspect-2/1 lg:w-70"
|
||||
style="--theme-color: {team.color};"
|
||||
class="score-box mb-2 aspect-3/1 rounded-2xl border-5 first:aspect-2/1"
|
||||
>
|
||||
<div class="text-center">{house.name}</div>
|
||||
<div class="items-center justify-center text-center"><p>{house.points}</p></div>
|
||||
<div class="text-center">{team.name}</div>
|
||||
<div class="items-center justify-center text-center"><p>{team.points}</p></div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<button
|
||||
onclick={() =>
|
||||
// Onclick send a request to the post endpoint
|
||||
fetch('/api/teams', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
})}
|
||||
>
|
||||
Send update
|
||||
</button>
|
||||
|
||||
<Table data={eventTable} {header} {row} />
|
||||
|
||||
<style>
|
||||
@import url('https://cdn.jsdelivr.net/npm/@catppuccin/palette/css/catppuccin.css');
|
||||
.score-box {
|
||||
@@ -26,4 +109,9 @@
|
||||
border-color: var(--theme-color);
|
||||
background-color: color-mix(in srgb, var(--theme-color), transparent 90%);
|
||||
}
|
||||
.player-box {
|
||||
color: var(--theme-color);
|
||||
border-color: var(--theme-color);
|
||||
background-color: color-mix(in srgb, var(--theme-color), transparent 90%);
|
||||
}
|
||||
</style>
|
||||
|
||||
29
src/routes/Table.svelte
Normal file
29
src/routes/Table.svelte
Normal file
@@ -0,0 +1,29 @@
|
||||
<script>
|
||||
let { data, header, row } = $props();
|
||||
</script>
|
||||
|
||||
<table class="w-full table-auto">
|
||||
{#if header}
|
||||
<thead>
|
||||
<tr>{@render header()}</tr>
|
||||
</thead>
|
||||
{/if}
|
||||
|
||||
<tbody>
|
||||
{#each data as d}
|
||||
<tr>{@render row(d)}</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<style>
|
||||
table :global(th:not(.large)),
|
||||
table :global(td:not(.large)) {
|
||||
width: 1%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
tbody tr:nth-child(2n + 1) {
|
||||
background: var(--ctp-mocha-surface1);
|
||||
}
|
||||
</style>
|
||||
54
src/routes/api/registeredEvents/+server.ts
Normal file
54
src/routes/api/registeredEvents/+server.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import {
|
||||
globalEmitter,
|
||||
getRegisteredEvents,
|
||||
getAllRegisteredEventPlayers
|
||||
} from '$lib/server/eventManager';
|
||||
import { generateEndpoint } from '$lib/server/endpoint';
|
||||
|
||||
export async function GET() {
|
||||
// Generate stream endpoint
|
||||
const endpoint = generateEndpoint(async (enqueue) => {
|
||||
const eventList = async () => {
|
||||
// Get updated events from database
|
||||
let newEvents = await getRegisteredEvents();
|
||||
let registeredEventList = newEvents['events'];
|
||||
|
||||
let fullEventList: any[] = [];
|
||||
|
||||
// For every event
|
||||
for (let registeredEvent in registeredEventList) {
|
||||
let event = registeredEventList[registeredEvent];
|
||||
|
||||
// Get all players for the event
|
||||
let registeredPlayers = await getAllRegisteredEventPlayers(event.id);
|
||||
|
||||
// Some nonsense to extract the players into a better object
|
||||
let registeredPlayersVec: any[] = [];
|
||||
for (let player in registeredPlayers.eventPlayers) {
|
||||
let currentPlayer = registeredPlayers.eventPlayers[player];
|
||||
registeredPlayersVec.push(currentPlayer);
|
||||
}
|
||||
|
||||
// add registeredPlayers as part of the event object
|
||||
let eventWithPlayers = {
|
||||
...event,
|
||||
registeredPlayers: registeredPlayersVec
|
||||
};
|
||||
// combine all of the events into one array
|
||||
fullEventList.push(eventWithPlayers);
|
||||
}
|
||||
// Send to client
|
||||
enqueue(fullEventList);
|
||||
};
|
||||
|
||||
// Initial Sync
|
||||
eventList();
|
||||
globalEmitter.on('eventUpdate', eventList);
|
||||
|
||||
// Simply return the cleanup function here
|
||||
return () => {
|
||||
globalEmitter.off('eventUpdate', eventList);
|
||||
};
|
||||
});
|
||||
return (await endpoint).response;
|
||||
}
|
||||
9
src/routes/api/registeredPlayers/+server.ts
Normal file
9
src/routes/api/registeredPlayers/+server.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { globalEmitter, getAllRegisteredEventPlayers } from '$lib/server/eventManager';
|
||||
import { generateEndpoint } from '$lib/server/endpoint';
|
||||
|
||||
// Expose post request
|
||||
export async function POST({ request }: any) {
|
||||
// When post request recieved increment testscores by 1
|
||||
// Return ok so the frontend is happy
|
||||
return new Response('ok');
|
||||
}
|
||||
30
src/routes/api/teams/+server.ts
Normal file
30
src/routes/api/teams/+server.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { globalEmitter, getTeams } from '$lib/server/eventManager';
|
||||
import { generateEndpoint } from '$lib/server/endpoint';
|
||||
|
||||
// Expose post request
|
||||
export async function POST({ request }: any) {
|
||||
// When post request recieved increment testscores by 1
|
||||
globalEmitter.emit('incrementScores');
|
||||
// Return ok so the frontend is happy
|
||||
return new Response('ok');
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
const endpoint = generateEndpoint(async (enqueue) => {
|
||||
// Function to grab score from database and add it to message queue
|
||||
let newScore = async () => {
|
||||
let newScores = await getTeams();
|
||||
enqueue(newScores);
|
||||
};
|
||||
|
||||
// Initial Sync
|
||||
newScore();
|
||||
globalEmitter.on('scoreUpdate', newScore);
|
||||
|
||||
// Simply return the cleanup function here
|
||||
return () => {
|
||||
globalEmitter.off('scoreUpdate', newScore);
|
||||
};
|
||||
});
|
||||
return (await endpoint).response;
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import adapter from '@sveltejs/adapter-auto';
|
||||
const config = {
|
||||
compilerOptions: {
|
||||
// Force runes mode for the project, except for libraries. Can be removed in svelte 6.
|
||||
runes: ({ filename }) => filename.split(/[/\\]/).includes('node_modules') ? undefined : true
|
||||
runes: ({ filename }) => (filename.split(/[/\\]/).includes('node_modules') ? undefined : true)
|
||||
},
|
||||
kit: {
|
||||
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
|
||||
@@ -17,6 +17,14 @@ const config = {
|
||||
...config,
|
||||
include: [...config.include, '../drizzle.config.ts']
|
||||
})
|
||||
},
|
||||
experimental: {
|
||||
remoteFunctions: true
|
||||
}
|
||||
},
|
||||
compilerOptions: {
|
||||
experimental: {
|
||||
async: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user