From 0f833efa76f2474cc61ddcb8206bdd8a243e84d8 Mon Sep 17 00:00:00 2001 From: nehalist Date: Sun, 28 Jan 2024 10:49:23 +0100 Subject: [PATCH] feat: add calculated ratings --- drizzle/0006_worthless_mandroid.sql | 3 + drizzle/0007_moaning_sunspot.sql | 1 + drizzle/0008_solid_bill_hollister.sql | 1 + drizzle/meta/0006_snapshot.json | 715 +++++++++++++++++ drizzle/meta/0007_snapshot.json | 722 ++++++++++++++++++ drizzle/meta/0008_snapshot.json | 722 ++++++++++++++++++ drizzle/meta/_journal.json | 21 + src/app/[locale]/(home)/home.tsx | 7 +- .../[locale]/(home)/match-creation-form.tsx | 4 +- src/app/[locale]/(home)/recent-matches.tsx | 29 +- src/components/rating-change.tsx | 8 + src/db/model/match.ts | 51 +- src/db/schema.ts | 14 +- src/lib/rating.ts | 11 + src/lib/storage.ts | 40 - 15 files changed, 2282 insertions(+), 67 deletions(-) create mode 100644 drizzle/0006_worthless_mandroid.sql create mode 100644 drizzle/0007_moaning_sunspot.sql create mode 100644 drizzle/0008_solid_bill_hollister.sql create mode 100644 drizzle/meta/0006_snapshot.json create mode 100644 drizzle/meta/0007_snapshot.json create mode 100644 drizzle/meta/0008_snapshot.json create mode 100644 src/components/rating-change.tsx delete mode 100644 src/lib/storage.ts diff --git a/drizzle/0006_worthless_mandroid.sql b/drizzle/0006_worthless_mandroid.sql new file mode 100644 index 0000000..a867fdb --- /dev/null +++ b/drizzle/0006_worthless_mandroid.sql @@ -0,0 +1,3 @@ +ALTER TABLE "matches" ADD COLUMN "team1RatingChange" real DEFAULT 0 NOT NULL;--> statement-breakpoint +ALTER TABLE "matches" ADD COLUMN "team1Rating" real DEFAULT 0 NOT NULL;--> statement-breakpoint +ALTER TABLE "matches" ADD COLUMN "team2Rating" real DEFAULT 0 NOT NULL; \ No newline at end of file diff --git a/drizzle/0007_moaning_sunspot.sql b/drizzle/0007_moaning_sunspot.sql new file mode 100644 index 0000000..1bf93e7 --- /dev/null +++ b/drizzle/0007_moaning_sunspot.sql @@ -0,0 +1 @@ +ALTER TABLE "matches" ADD COLUMN "team2RatingChange" real DEFAULT 0 NOT NULL; \ No newline at end of file diff --git a/drizzle/0008_solid_bill_hollister.sql b/drizzle/0008_solid_bill_hollister.sql new file mode 100644 index 0000000..5ada913 --- /dev/null +++ b/drizzle/0008_solid_bill_hollister.sql @@ -0,0 +1 @@ +ALTER TABLE "team" ALTER COLUMN "rating" SET DATA TYPE real; \ No newline at end of file diff --git a/drizzle/meta/0006_snapshot.json b/drizzle/meta/0006_snapshot.json new file mode 100644 index 0000000..b304207 --- /dev/null +++ b/drizzle/meta/0006_snapshot.json @@ -0,0 +1,715 @@ +{ + "id": "e1de5345-40af-45e8-b8ba-9f3cc35be126", + "prevId": "f732d5b2-862e-4b2b-9a42-33728d8ad91f", + "version": "5", + "dialect": "pg", + "tables": { + "account": { + "name": "account", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerAccountId": { + "name": "providerAccountId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "session_state": { + "name": "session_state", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_userId_user_id_fk": { + "name": "account_userId_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "account_provider_providerAccountId_pk": { + "name": "account_provider_providerAccountId_pk", + "columns": [ + "provider", + "providerAccountId" + ] + } + }, + "uniqueConstraints": {} + }, + "league": { + "name": "league", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "game": { + "name": "game", + "type": "game", + "primaryKey": false, + "notNull": true, + "default": "'custom'" + }, + "maxScorePerMatch": { + "name": "maxScorePerMatch", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "allowDraws": { + "name": "allowDraws", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "defaultRating": { + "name": "defaultRating", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1000 + }, + "ratingSystem": { + "name": "ratingSystem", + "type": "ratingSystem", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "ratingSystemParameters": { + "name": "ratingSystemParameters", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{}'::json" + }, + "leagueStatus": { + "name": "leagueStatus", + "type": "leagueStatus", + "primaryKey": false, + "notNull": false, + "default": "'active'" + }, + "inviteCode": { + "name": "inviteCode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "substr\n (md5(random()::text), 0, 25)" + }, + "ownerId": { + "name": "ownerId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "league_ownerId_user_id_fk": { + "name": "league_ownerId_user_id_fk", + "tableFrom": "league", + "tableTo": "user", + "columnsFrom": [ + "ownerId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "league_inviteCode_unique": { + "name": "league_inviteCode_unique", + "nullsNotDistinct": false, + "columns": [ + "inviteCode" + ] + } + } + }, + "matches": { + "name": "matches", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "leagueId": { + "name": "leagueId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team1Id": { + "name": "team1Id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "score1": { + "name": "score1", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "team2Id": { + "name": "team2Id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "score2": { + "name": "score2", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team1RatingChange": { + "name": "team1RatingChange", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "team1Rating": { + "name": "team1Rating", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "team2Rating": { + "name": "team2Rating", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "matches_leagueId_league_id_fk": { + "name": "matches_leagueId_league_id_fk", + "tableFrom": "matches", + "tableTo": "league", + "columnsFrom": [ + "leagueId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "matches_team1Id_team_id_fk": { + "name": "matches_team1Id_team_id_fk", + "tableFrom": "matches", + "tableTo": "team", + "columnsFrom": [ + "team1Id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "matches_team2Id_team_id_fk": { + "name": "matches_team2Id_team_id_fk", + "tableFrom": "matches", + "tableTo": "team", + "columnsFrom": [ + "team2Id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "matches_userId_user_id_fk": { + "name": "matches_userId_user_id_fk", + "tableFrom": "matches", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "memberships": { + "name": "memberships", + "schema": "", + "columns": { + "leagueId": { + "name": "leagueId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "membershipRole", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "memberships_leagueId_league_id_fk": { + "name": "memberships_leagueId_league_id_fk", + "tableFrom": "memberships", + "tableTo": "league", + "columnsFrom": [ + "leagueId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "memberships_userId_user_id_fk": { + "name": "memberships_userId_user_id_fk", + "tableFrom": "memberships", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "memberships_userId_leagueId_pk": { + "name": "memberships_userId_leagueId_pk", + "columns": [ + "userId", + "leagueId" + ] + } + }, + "uniqueConstraints": {} + }, + "session": { + "name": "session", + "schema": "", + "columns": { + "sessionToken": { + "name": "sessionToken", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires": { + "name": "expires", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "team": { + "name": "team", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "leagueId": { + "name": "leagueId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "teamsize": { + "name": "teamsize", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rating": { + "name": "rating", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1000 + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "team_leagueId_league_id_fk": { + "name": "team_leagueId_league_id_fk", + "tableFrom": "team", + "tableTo": "league", + "columnsFrom": [ + "leagueId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_userId_user_id_fk": { + "name": "team_userId_user_id_fk", + "tableFrom": "team", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "team_name_leagueId_unique": { + "name": "team_name_leagueId_unique", + "nullsNotDistinct": false, + "columns": [ + "name", + "leagueId" + ] + } + } + }, + "user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "role", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "emailVerified": { + "name": "emailVerified", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "selectedLeagueId": { + "name": "selectedLeagueId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "verificationToken": { + "name": "verificationToken", + "schema": "", + "columns": { + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires": { + "name": "expires", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "verificationToken_identifier_token_pk": { + "name": "verificationToken_identifier_token_pk", + "columns": [ + "identifier", + "token" + ] + } + }, + "uniqueConstraints": {} + } + }, + "enums": { + "game": { + "name": "game", + "values": { + "custom": "custom", + "foosball": "foosball", + "badminton": "badminton", + "chess": "chess", + "pool": "pool", + "table-tennis": "table-tennis", + "sixty-six": "sixty-six" + } + }, + "leagueStatus": { + "name": "leagueStatus", + "values": { + "active": "active", + "finished": "finished" + } + }, + "membershipRole": { + "name": "membershipRole", + "values": { + "member": "member", + "admin": "admin" + } + }, + "ratingSystem": { + "name": "ratingSystem", + "values": { + "unknown": "unknown", + "elo": "elo", + "glicko2": "glicko2" + } + }, + "role": { + "name": "role", + "values": { + "user": "user", + "admin": "admin" + } + } + }, + "schemas": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/0007_snapshot.json b/drizzle/meta/0007_snapshot.json new file mode 100644 index 0000000..3e1b721 --- /dev/null +++ b/drizzle/meta/0007_snapshot.json @@ -0,0 +1,722 @@ +{ + "id": "60cdb799-e044-49e6-97dc-acd4402cd2fd", + "prevId": "e1de5345-40af-45e8-b8ba-9f3cc35be126", + "version": "5", + "dialect": "pg", + "tables": { + "account": { + "name": "account", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerAccountId": { + "name": "providerAccountId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "session_state": { + "name": "session_state", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_userId_user_id_fk": { + "name": "account_userId_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "account_provider_providerAccountId_pk": { + "name": "account_provider_providerAccountId_pk", + "columns": [ + "provider", + "providerAccountId" + ] + } + }, + "uniqueConstraints": {} + }, + "league": { + "name": "league", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "game": { + "name": "game", + "type": "game", + "primaryKey": false, + "notNull": true, + "default": "'custom'" + }, + "maxScorePerMatch": { + "name": "maxScorePerMatch", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "allowDraws": { + "name": "allowDraws", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "defaultRating": { + "name": "defaultRating", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1000 + }, + "ratingSystem": { + "name": "ratingSystem", + "type": "ratingSystem", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "ratingSystemParameters": { + "name": "ratingSystemParameters", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{}'::json" + }, + "leagueStatus": { + "name": "leagueStatus", + "type": "leagueStatus", + "primaryKey": false, + "notNull": false, + "default": "'active'" + }, + "inviteCode": { + "name": "inviteCode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "substr\n (md5(random()::text), 0, 25)" + }, + "ownerId": { + "name": "ownerId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "league_ownerId_user_id_fk": { + "name": "league_ownerId_user_id_fk", + "tableFrom": "league", + "tableTo": "user", + "columnsFrom": [ + "ownerId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "league_inviteCode_unique": { + "name": "league_inviteCode_unique", + "nullsNotDistinct": false, + "columns": [ + "inviteCode" + ] + } + } + }, + "matches": { + "name": "matches", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "leagueId": { + "name": "leagueId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team1Id": { + "name": "team1Id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "score1": { + "name": "score1", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "team2Id": { + "name": "team2Id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "score2": { + "name": "score2", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team1RatingChange": { + "name": "team1RatingChange", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "team2RatingChange": { + "name": "team2RatingChange", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "team1Rating": { + "name": "team1Rating", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "team2Rating": { + "name": "team2Rating", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "matches_leagueId_league_id_fk": { + "name": "matches_leagueId_league_id_fk", + "tableFrom": "matches", + "tableTo": "league", + "columnsFrom": [ + "leagueId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "matches_team1Id_team_id_fk": { + "name": "matches_team1Id_team_id_fk", + "tableFrom": "matches", + "tableTo": "team", + "columnsFrom": [ + "team1Id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "matches_team2Id_team_id_fk": { + "name": "matches_team2Id_team_id_fk", + "tableFrom": "matches", + "tableTo": "team", + "columnsFrom": [ + "team2Id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "matches_userId_user_id_fk": { + "name": "matches_userId_user_id_fk", + "tableFrom": "matches", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "memberships": { + "name": "memberships", + "schema": "", + "columns": { + "leagueId": { + "name": "leagueId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "membershipRole", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "memberships_leagueId_league_id_fk": { + "name": "memberships_leagueId_league_id_fk", + "tableFrom": "memberships", + "tableTo": "league", + "columnsFrom": [ + "leagueId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "memberships_userId_user_id_fk": { + "name": "memberships_userId_user_id_fk", + "tableFrom": "memberships", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "memberships_userId_leagueId_pk": { + "name": "memberships_userId_leagueId_pk", + "columns": [ + "userId", + "leagueId" + ] + } + }, + "uniqueConstraints": {} + }, + "session": { + "name": "session", + "schema": "", + "columns": { + "sessionToken": { + "name": "sessionToken", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires": { + "name": "expires", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "team": { + "name": "team", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "leagueId": { + "name": "leagueId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "teamsize": { + "name": "teamsize", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rating": { + "name": "rating", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1000 + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "team_leagueId_league_id_fk": { + "name": "team_leagueId_league_id_fk", + "tableFrom": "team", + "tableTo": "league", + "columnsFrom": [ + "leagueId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_userId_user_id_fk": { + "name": "team_userId_user_id_fk", + "tableFrom": "team", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "team_name_leagueId_unique": { + "name": "team_name_leagueId_unique", + "nullsNotDistinct": false, + "columns": [ + "name", + "leagueId" + ] + } + } + }, + "user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "role", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "emailVerified": { + "name": "emailVerified", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "selectedLeagueId": { + "name": "selectedLeagueId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "verificationToken": { + "name": "verificationToken", + "schema": "", + "columns": { + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires": { + "name": "expires", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "verificationToken_identifier_token_pk": { + "name": "verificationToken_identifier_token_pk", + "columns": [ + "identifier", + "token" + ] + } + }, + "uniqueConstraints": {} + } + }, + "enums": { + "game": { + "name": "game", + "values": { + "custom": "custom", + "foosball": "foosball", + "badminton": "badminton", + "chess": "chess", + "pool": "pool", + "table-tennis": "table-tennis", + "sixty-six": "sixty-six" + } + }, + "leagueStatus": { + "name": "leagueStatus", + "values": { + "active": "active", + "finished": "finished" + } + }, + "membershipRole": { + "name": "membershipRole", + "values": { + "member": "member", + "admin": "admin" + } + }, + "ratingSystem": { + "name": "ratingSystem", + "values": { + "unknown": "unknown", + "elo": "elo", + "glicko2": "glicko2" + } + }, + "role": { + "name": "role", + "values": { + "user": "user", + "admin": "admin" + } + } + }, + "schemas": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/0008_snapshot.json b/drizzle/meta/0008_snapshot.json new file mode 100644 index 0000000..c20edb9 --- /dev/null +++ b/drizzle/meta/0008_snapshot.json @@ -0,0 +1,722 @@ +{ + "id": "0f8ab27b-e877-42d9-9c41-47e8528f4323", + "prevId": "60cdb799-e044-49e6-97dc-acd4402cd2fd", + "version": "5", + "dialect": "pg", + "tables": { + "account": { + "name": "account", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerAccountId": { + "name": "providerAccountId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "session_state": { + "name": "session_state", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_userId_user_id_fk": { + "name": "account_userId_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "account_provider_providerAccountId_pk": { + "name": "account_provider_providerAccountId_pk", + "columns": [ + "provider", + "providerAccountId" + ] + } + }, + "uniqueConstraints": {} + }, + "league": { + "name": "league", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "game": { + "name": "game", + "type": "game", + "primaryKey": false, + "notNull": true, + "default": "'custom'" + }, + "maxScorePerMatch": { + "name": "maxScorePerMatch", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "allowDraws": { + "name": "allowDraws", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "defaultRating": { + "name": "defaultRating", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1000 + }, + "ratingSystem": { + "name": "ratingSystem", + "type": "ratingSystem", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "ratingSystemParameters": { + "name": "ratingSystemParameters", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{}'::json" + }, + "leagueStatus": { + "name": "leagueStatus", + "type": "leagueStatus", + "primaryKey": false, + "notNull": false, + "default": "'active'" + }, + "inviteCode": { + "name": "inviteCode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "substr\n (md5(random()::text), 0, 25)" + }, + "ownerId": { + "name": "ownerId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "league_ownerId_user_id_fk": { + "name": "league_ownerId_user_id_fk", + "tableFrom": "league", + "tableTo": "user", + "columnsFrom": [ + "ownerId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "league_inviteCode_unique": { + "name": "league_inviteCode_unique", + "nullsNotDistinct": false, + "columns": [ + "inviteCode" + ] + } + } + }, + "matches": { + "name": "matches", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "leagueId": { + "name": "leagueId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team1Id": { + "name": "team1Id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "score1": { + "name": "score1", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "team2Id": { + "name": "team2Id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "score2": { + "name": "score2", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team1RatingChange": { + "name": "team1RatingChange", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "team2RatingChange": { + "name": "team2RatingChange", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "team1Rating": { + "name": "team1Rating", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "team2Rating": { + "name": "team2Rating", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "matches_leagueId_league_id_fk": { + "name": "matches_leagueId_league_id_fk", + "tableFrom": "matches", + "tableTo": "league", + "columnsFrom": [ + "leagueId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "matches_team1Id_team_id_fk": { + "name": "matches_team1Id_team_id_fk", + "tableFrom": "matches", + "tableTo": "team", + "columnsFrom": [ + "team1Id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "matches_team2Id_team_id_fk": { + "name": "matches_team2Id_team_id_fk", + "tableFrom": "matches", + "tableTo": "team", + "columnsFrom": [ + "team2Id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "matches_userId_user_id_fk": { + "name": "matches_userId_user_id_fk", + "tableFrom": "matches", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "memberships": { + "name": "memberships", + "schema": "", + "columns": { + "leagueId": { + "name": "leagueId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "membershipRole", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "memberships_leagueId_league_id_fk": { + "name": "memberships_leagueId_league_id_fk", + "tableFrom": "memberships", + "tableTo": "league", + "columnsFrom": [ + "leagueId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "memberships_userId_user_id_fk": { + "name": "memberships_userId_user_id_fk", + "tableFrom": "memberships", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "memberships_userId_leagueId_pk": { + "name": "memberships_userId_leagueId_pk", + "columns": [ + "userId", + "leagueId" + ] + } + }, + "uniqueConstraints": {} + }, + "session": { + "name": "session", + "schema": "", + "columns": { + "sessionToken": { + "name": "sessionToken", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires": { + "name": "expires", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "team": { + "name": "team", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "leagueId": { + "name": "leagueId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "teamsize": { + "name": "teamsize", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rating": { + "name": "rating", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 1000 + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "team_leagueId_league_id_fk": { + "name": "team_leagueId_league_id_fk", + "tableFrom": "team", + "tableTo": "league", + "columnsFrom": [ + "leagueId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_userId_user_id_fk": { + "name": "team_userId_user_id_fk", + "tableFrom": "team", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "team_name_leagueId_unique": { + "name": "team_name_leagueId_unique", + "nullsNotDistinct": false, + "columns": [ + "name", + "leagueId" + ] + } + } + }, + "user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "role", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "emailVerified": { + "name": "emailVerified", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "selectedLeagueId": { + "name": "selectedLeagueId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "verificationToken": { + "name": "verificationToken", + "schema": "", + "columns": { + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires": { + "name": "expires", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "verificationToken_identifier_token_pk": { + "name": "verificationToken_identifier_token_pk", + "columns": [ + "identifier", + "token" + ] + } + }, + "uniqueConstraints": {} + } + }, + "enums": { + "game": { + "name": "game", + "values": { + "custom": "custom", + "foosball": "foosball", + "badminton": "badminton", + "chess": "chess", + "pool": "pool", + "table-tennis": "table-tennis", + "sixty-six": "sixty-six" + } + }, + "leagueStatus": { + "name": "leagueStatus", + "values": { + "active": "active", + "finished": "finished" + } + }, + "membershipRole": { + "name": "membershipRole", + "values": { + "member": "member", + "admin": "admin" + } + }, + "ratingSystem": { + "name": "ratingSystem", + "values": { + "unknown": "unknown", + "elo": "elo", + "glicko2": "glicko2" + } + }, + "role": { + "name": "role", + "values": { + "user": "user", + "admin": "admin" + } + } + }, + "schemas": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 20ee130..c86b725 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -43,6 +43,27 @@ "when": 1706278884835, "tag": "0005_demonic_roland_deschain", "breakpoints": true + }, + { + "idx": 6, + "version": "5", + "when": 1706433106388, + "tag": "0006_worthless_mandroid", + "breakpoints": true + }, + { + "idx": 7, + "version": "5", + "when": 1706433332504, + "tag": "0007_moaning_sunspot", + "breakpoints": true + }, + { + "idx": 8, + "version": "5", + "when": 1706433650227, + "tag": "0008_solid_bill_hollister", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/app/[locale]/(home)/home.tsx b/src/app/[locale]/(home)/home.tsx index 6b8c63c..9dbf8af 100644 --- a/src/app/[locale]/(home)/home.tsx +++ b/src/app/[locale]/(home)/home.tsx @@ -1,11 +1,16 @@ import { MatchCreationForm } from "@/app/[locale]/(home)/match-creation-form"; -import { getLeagueTeamsForCurrentUser } from "@/db/model/league"; +import { getLeagueTeamsForCurrentUser, getSelectedUserLeague } from "@/db/model/league"; import { RecentMatches } from "@/app/[locale]/(home)/recent-matches"; import { getRecentLeagueMatches } from "@/db/model/match"; export async function Home() { const teams = await getLeagueTeamsForCurrentUser(); const recentMatches = await getRecentLeagueMatches(); + const league = await getSelectedUserLeague(); + + if (! league) { + return null; + } return ( <> diff --git a/src/app/[locale]/(home)/match-creation-form.tsx b/src/app/[locale]/(home)/match-creation-form.tsx index 7ae71fd..4dcec23 100644 --- a/src/app/[locale]/(home)/match-creation-form.tsx +++ b/src/app/[locale]/(home)/match-creation-form.tsx @@ -22,7 +22,7 @@ interface FormValues { } export function MatchCreationForm({ teams }: MatchCreationFormProps) { - const { register, control } = useForm({ + const { register, control, setValue } = useForm({ defaultValues: { team1: ["dubi"], score1: "", @@ -39,7 +39,6 @@ export function MatchCreationForm({ teams }: MatchCreationFormProps) {
-
@@ -112,7 +111,6 @@ export function MatchCreationForm({ teams }: MatchCreationFormProps) {
-
diff --git a/src/app/[locale]/(home)/recent-matches.tsx b/src/app/[locale]/(home)/recent-matches.tsx index 42feb66..a754654 100644 --- a/src/app/[locale]/(home)/recent-matches.tsx +++ b/src/app/[locale]/(home)/recent-matches.tsx @@ -10,27 +10,38 @@ import { } from "@nextui-org/react"; import { getRecentLeagueMatches } from "@/db/model/match"; import { TimeDistance } from "@/components/time-distance"; +import { RatingChange } from "@/components/rating-change"; interface RecentMatchesProps { - matches: Awaited> + matches: Awaited>; } export function RecentMatches({ matches }: RecentMatchesProps) { return ( - Team 1 - Result - Team 2 - Date + Team 1 + Result + Team 2 + Date {match => ( - {match.team1.name} - {match.score1} {match.score2} - {match.team2.name} - + + {match.team1.name}{" "} + + + + {match.score1} {match.score2} + + + {match.team2.name}{" "} + + + + + )} diff --git a/src/components/rating-change.tsx b/src/components/rating-change.tsx new file mode 100644 index 0000000..b8f0e16 --- /dev/null +++ b/src/components/rating-change.tsx @@ -0,0 +1,8 @@ +export function RatingChange({ change }: { change: number }) { + if (change > 0) { + return +{change.toFixed(2)}; + } else if (change < 0) { + return {change.toFixed(2)}; + } + return null; +} diff --git a/src/db/model/match.ts b/src/db/model/match.ts index 8a748af..80f6753 100644 --- a/src/db/model/match.ts +++ b/src/db/model/match.ts @@ -1,7 +1,8 @@ -import { League, matches, Team, User } from "@/db/schema"; +import { League, matches, Team, teams, User } from "@/db/schema"; import { db } from "@/db"; -import { desc } from "drizzle-orm"; +import { desc, eq } from "drizzle-orm"; import { getCurrentUser } from "@/lib/session"; +import { ratingSystems } from "@/lib/rating"; export async function createMatch( league: League, @@ -11,13 +12,41 @@ export async function createMatch( score1: number, score2: number, ) { - // const ratingSystem = ratingSystems.find(rs => rs.id === league.ratingSystem); - // if (!ratingSystem) { - // return; - // } - // - // const team1Rating = team1.rating; - // const team2Rating = team2.rating; + const ratingSystem = ratingSystems.find(rs => rs.id === league.ratingSystem); + if (!ratingSystem) { + return; + } + + const team1Rating = team1.rating; + const team2Rating = team2.rating; + const team1NewRating = ratingSystem.getNewRating( + league, + team1, + team2, + score1, + score2, + ); + const team2NewRating = ratingSystem.getNewRating( + league, + team2, + team1, + score2, + score1, + ); + + await db + .update(teams) + .set({ + rating: team1NewRating, + }) + .where(eq(teams.id, team1.id)); + + await db + .update(teams) + .set({ + rating: team2Rating, + }) + .where(eq(teams.id, team2.id)); return db.insert(matches).values({ leagueId: league.id, @@ -26,6 +55,10 @@ export async function createMatch( score1, score2, userId: user.id, + team1RatingChange: team1NewRating - team1Rating, + team2RatingChange: team2NewRating - team2Rating, + team1Rating: team1NewRating, + team2Rating: team2NewRating, }); } diff --git a/src/db/schema.ts b/src/db/schema.ts index c83d775..b87b155 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -1,10 +1,10 @@ import { boolean, integer, - json, + json, numeric, pgEnum, pgTable, - primaryKey, + primaryKey, real, text, timestamp, unique, @@ -12,7 +12,6 @@ import { import { AdapterAccount } from "@auth/core/adapters"; import { relations, sql } from "drizzle-orm"; import { games } from "@/lib/games"; -import { ratingSystems } from "@/lib/rating"; export const leagueStatusEnum = pgEnum("leagueStatus", ["active", "finished"]); export const gamesEnum = pgEnum("game", [ @@ -21,7 +20,8 @@ export const gamesEnum = pgEnum("game", [ ]); export const ratingSystemsEnum = pgEnum("ratingSystem", [ "unknown", - ...ratingSystems.map(rs => rs.id), + "elo", + "glicko2" ]); export const userRolesEnum = pgEnum("role", ["user", "admin"]); export const membershipRole = pgEnum("membershipRole", ["member", "admin"]); @@ -91,7 +91,7 @@ export const teams = pgTable( userId: text("userId") .notNull() .references(() => users.id, { onDelete: "cascade" }), - rating: integer("rating").notNull().default(1000), + rating: real("rating").notNull().default(1000), createdAt: timestamp("createdAt", { mode: "date" }).notNull().defaultNow(), }, t => ({ @@ -133,6 +133,10 @@ export const matches = pgTable("matches", { userId: text("userId") .notNull() .references(() => users.id, { onDelete: "cascade" }), + team1RatingChange: real("team1RatingChange").notNull().default(0), + team2RatingChange: real("team2RatingChange").notNull().default(0), + team1Rating: real("team1Rating").notNull().default(0), + team2Rating: real("team2Rating").notNull().default(0), createdAt: timestamp("createdAt", { mode: "date" }).notNull().defaultNow(), }); diff --git a/src/lib/rating.ts b/src/lib/rating.ts index d23ac70..290fe2f 100644 --- a/src/lib/rating.ts +++ b/src/lib/rating.ts @@ -1,3 +1,5 @@ +import { League, Team } from "@/db/schema"; + export enum RatingSystem { Elo = "elo", Glicko2 = "glicko2", @@ -16,6 +18,12 @@ export const ratingSystems = [ defaultValue: 32, }, ], + getNewRating: (league: League, team: Team, opponent: Team, score1: number, score2: number) => { + const kFactor = (league.ratingSystemParameters as Record).kFactor || 32; + const result = score1 > score2 ? 1 : score1 < score2 ? 0 : 0.5; + const expectedRating = 1 / (1 + 10 ** ((opponent.rating - team.rating) / 400)); + return team.rating + kFactor * (result - expectedRating); + } }, { id: RatingSystem.Glicko2, @@ -39,6 +47,9 @@ export const ratingSystems = [ defaultValue: 0.06, }, ], + getNewRating: (league: League, team: Team, opponent: Team, score1: number, score2: number) => { + return 0; + } }, ] as const; diff --git a/src/lib/storage.ts b/src/lib/storage.ts deleted file mode 100644 index 55b98b2..0000000 --- a/src/lib/storage.ts +++ /dev/null @@ -1,40 +0,0 @@ -import "server-only"; -import * as fs from "fs"; -import * as path from "path"; -import * as fg from "fast-glob"; - -export const uploadPath = "./public/uploads"; - -export async function uploadFile(file: File, identifier: string) { - const data = await file.arrayBuffer(); - const fileName = `${identifier}.${file.name.split(".").pop()}`; - const destination = getUploadedFilePath(fileName); - const dir = path.dirname(identifier); - - if (dir !== ".") { - fs.mkdirSync(path.join(process.cwd(), uploadPath, dir), { - recursive: true, - }); - } - - fs.writeFileSync(destination, Buffer.from(data)); - - return { - fileName: `/uploads/${fileName}`, - destination, - }; -} - -export async function removeUploadedFiles(pattern: string) { - try { - fg.globSync(getUploadedFilePath(pattern)).forEach(file => { - fs.unlinkSync(file); - }); - } catch (e) { - console.error(e); - } -} - -function getUploadedFilePath(name: string) { - return path.join(process.cwd(), uploadPath, name); -}