From 9b82afb4a98ad2abdc586b40a261c9aad7183bab Mon Sep 17 00:00:00 2001
From: Logan Ford <110533855+Logannford@users.noreply.github.com>
Date: Sun, 9 Mar 2025 10:15:17 +0000
Subject: [PATCH 01/18] feat: init league schema commit
---
prisma/schema/leagues.prisma | 59 ++++++++++++++++++++++++++++++++++++
1 file changed, 59 insertions(+)
create mode 100644 prisma/schema/leagues.prisma
diff --git a/prisma/schema/leagues.prisma b/prisma/schema/leagues.prisma
new file mode 100644
index 000000000..3a3255838
--- /dev/null
+++ b/prisma/schema/leagues.prisma
@@ -0,0 +1,59 @@
+enum LeagueName {
+ BRONZE
+ SILVER
+ GOLD
+ PLATINUM
+ DIAMOND
+}
+
+enum LeagueColor {
+ CD7F32
+ C0C0C0
+ FFD700
+ E5E4E2
+ b9f2ff
+}
+
+// schema for a league, no relation to a users as
+// we have multiple instances of the same league
+model IndividualLeagueData {
+ // standard prisma fields
+ uid String @id @default(uuid())
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+
+ // the name of the league
+ name LeagueName
+
+ // the league's color
+ color LeagueColor
+
+ // a brief description of the league (may or may not be used)
+ description String?
+
+ // the amount of xp required to be in the league
+ xpRequirement Int
+
+ // when the league resets
+ resetDate DateTime
+
+ // leagues such as the bronze league cannot be relegated
+ // as it's the lowest league
+ canBeRelegated Boolean @default(false)
+
+ // the league's icon
+ icon String?
+
+ Leagues Leagues[]
+}
+
+model Leagues {
+ // standard prisma fields
+ uid String @id @default(uuid())
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+
+ // connect to the league data
+ leagueData IndividualLeagueData @relation(fields: [leagueDataUid], references: [uid])
+ leagueDataUid String
+}
\ No newline at end of file
From 8c7bbf944e0d0ff7c4b9d8800528a22bb7fd020c Mon Sep 17 00:00:00 2001
From: Logan Ford <110533855+Logannford@users.noreply.github.com>
Date: Sun, 9 Mar 2025 10:25:53 +0000
Subject: [PATCH 02/18] feat: adding more fields to league data
---
prisma/schema/leagues.prisma | 190 ++++++++++++++++++++++++++++++++++-
1 file changed, 189 insertions(+), 1 deletion(-)
diff --git a/prisma/schema/leagues.prisma b/prisma/schema/leagues.prisma
index 3a3255838..794d58ab9 100644
--- a/prisma/schema/leagues.prisma
+++ b/prisma/schema/leagues.prisma
@@ -12,6 +12,26 @@ enum LeagueColor {
FFD700
E5E4E2
b9f2ff
+ FF4D4D
+}
+
+enum LeagueAchievementType {
+ LEAGUE_WINNER
+ TOP_THREE
+ PROMOTION
+ PERFECT_WEEK
+ SURVIVAL
+ COMEBACK_KING
+ CONSISTENCY
+ SPEED_DEMON
+}
+
+enum LeaguePowerUp {
+ DOUBLE_XP
+ SHIELD
+ STREAK_SAVER
+ TIME_FREEZE
+ BONUS_POINTS
}
// schema for a league, no relation to a users as
@@ -44,7 +64,26 @@ model IndividualLeagueData {
// the league's icon
icon String?
- Leagues Leagues[]
+ // Minimum XP required to avoid relegation
+ minXpToAvoidRelegation Int @default(100)
+
+ // XP threshold for promotion
+ xpThresholdForPromotion Int?
+
+ // League rules
+ inactivityThresholdDays Int @default(7)
+ minWeeksBeforePromotion Int @default(1)
+
+ // Maximum number of power-ups allowed per week
+ maxPowerUpsPerWeek Int @default(3)
+
+ // Bonus XP multiplier for this league
+ xpMultiplier Float @default(1.0)
+
+ // Relations
+ leagues Leagues[]
+ achievements LeagueAchievement[]
+ history LeagueHistory[]
}
model Leagues {
@@ -56,4 +95,153 @@ model Leagues {
// connect to the league data
leagueData IndividualLeagueData @relation(fields: [leagueDataUid], references: [uid])
leagueDataUid String
+
+ // Maximum number of users in a league group
+ maxUsers Int @default(50)
+
+ // Current number of users
+ currentUsers Int @default(0)
+
+ // Whether the league is accepting new users
+ isOpen Boolean @default(true)
+
+ // When this league group started
+ startDate DateTime @default(now())
+
+ // When this league group ends
+ endDate DateTime
+
+ // Top users who will be promoted
+ promotionCount Int @default(3)
+
+ // Bottom users who will be relegated
+ relegationCount Int @default(5)
+
+ // Weekly challenges for bonus XP
+ weeklyChallenge String?
+ weeklyChallengeXP Int?
+
+ // Relations
+ users UserLeague[]
+}
+
+model UserLeague {
+ uid String @id @default(uuid())
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+
+ // The user in this league
+ user Users @relation(fields: [userUid], references: [uid])
+ userUid String
+
+ // The league instance they're in
+ league Leagues @relation(fields: [leagueUid], references: [uid])
+ leagueUid String
+
+ // Their position in the league
+ position Int?
+
+ // Their weekly XP in this league
+ weeklyXp Int @default(0)
+
+ // Whether they've been promoted/relegated
+ promoted Boolean @default(false)
+ relegated Boolean @default(false)
+
+ // When they joined this league
+ joinedAt DateTime @default(now())
+
+ // Gamification features
+ currentStreak Int @default(0)
+ bestPosition Int?
+ powerUpsRemaining Int @default(3)
+ activePowerUps LeaguePowerUp[]
+ powerUpExpiryTime DateTime?
+
+ // Weekly challenge progress
+ challengeProgress Int @default(0)
+ challengeCompleted Boolean @default(false)
+
+ // Unique constraint
+ @@unique([userUid, leagueUid])
+}
+
+model LeagueAchievement {
+ uid String @id @default(uuid())
+ createdAt DateTime @default(now())
+
+ // The user who earned it
+ user Users @relation(fields: [userUid], references: [uid])
+ userUid String
+
+ // The league it was earned in
+ league IndividualLeagueData @relation(fields: [leagueDataUid], references: [uid])
+ leagueDataUid String
+
+ // Type of achievement
+ type LeagueAchievementType
+
+ // When it was earned
+ earnedAt DateTime @default(now())
+
+ // Additional data (e.g., position, score)
+ metadata Json?
+
+ // XP bonus awarded
+ xpBonus Int @default(0)
+}
+
+model LeagueHistory {
+ uid String @id @default(uuid())
+ createdAt DateTime @default(now())
+
+ // The user
+ user Users @relation(fields: [userUid], references: [uid])
+ userUid String
+
+ // The league
+ league IndividualLeagueData @relation(fields: [leagueDataUid], references: [uid])
+ leagueDataUid String
+
+ // Their final position
+ finalPosition Int
+
+ // Their final XP
+ finalXp Int
+
+ // Whether they were promoted/relegated
+ wasPromoted Boolean
+ wasRelegated Boolean
+
+ // The week this history is for
+ weekStartDate DateTime
+ weekEndDate DateTime
+
+ // Performance stats
+ averageXpPerDay Float?
+ powerUpsUsed Int @default(0)
+ challengesCompleted Int @default(0)
+}
+
+model Users {
+ // ... your existing fields ...
+
+ // Current league membership
+ currentLeague UserLeague?
+
+ // All league memberships
+ leagueMemberships UserLeague[]
+
+ // League achievements
+ leagueAchievements LeagueAchievement[]
+
+ // League history
+ leagueHistory LeagueHistory[]
+
+ // Last league activity
+ lastLeagueActivity DateTime?
+
+ // League related fields
+ totalLeagueWins Int @default(0)
+ highestLeagueReached LeagueName @default(BRONZE)
}
\ No newline at end of file
From 677480c913487601979d2e55c91a9a60cbe107b6 Mon Sep 17 00:00:00 2001
From: Logan Ford <110533855+Logannford@users.noreply.github.com>
Date: Sun, 9 Mar 2025 10:26:25 +0000
Subject: [PATCH 03/18] hotfix: user league relations
---
prisma/schema/leagues.prisma | 23 -----------------------
prisma/schema/users.prisma | 6 ++++++
2 files changed, 6 insertions(+), 23 deletions(-)
diff --git a/prisma/schema/leagues.prisma b/prisma/schema/leagues.prisma
index 794d58ab9..ef1b9694e 100644
--- a/prisma/schema/leagues.prisma
+++ b/prisma/schema/leagues.prisma
@@ -221,27 +221,4 @@ model LeagueHistory {
averageXpPerDay Float?
powerUpsUsed Int @default(0)
challengesCompleted Int @default(0)
-}
-
-model Users {
- // ... your existing fields ...
-
- // Current league membership
- currentLeague UserLeague?
-
- // All league memberships
- leagueMemberships UserLeague[]
-
- // League achievements
- leagueAchievements LeagueAchievement[]
-
- // League history
- leagueHistory LeagueHistory[]
-
- // Last league activity
- lastLeagueActivity DateTime?
-
- // League related fields
- totalLeagueWins Int @default(0)
- highestLeagueReached LeagueName @default(BRONZE)
}
\ No newline at end of file
diff --git a/prisma/schema/users.prisma b/prisma/schema/users.prisma
index 4e6771d38..72becd6de 100644
--- a/prisma/schema/users.prisma
+++ b/prisma/schema/users.prisma
@@ -73,6 +73,12 @@ model Users {
roadmaps UserRoadmaps[]
studyPathEnrollments UserStudyPath[]
userMissions UserMission[]
+
+ LeagueHistory LeagueHistory[]
+
+ LeagueAchievement LeagueAchievement[]
+
+ UserLeague UserLeague[]
}
model Streaks {
From b6441ddce0a14b8011c529ec41bedec8b8cd7b4e Mon Sep 17 00:00:00 2001
From: Logan Ford <110533855+Logannford@users.noreply.github.com>
Date: Sun, 9 Mar 2025 15:28:37 +0000
Subject: [PATCH 04/18] feat: creates league schema and applying to db
---
.../migration.sql | 127 ++++++++++++++++++
prisma/schema/leagues.prisma | 76 +++++------
2 files changed, 162 insertions(+), 41 deletions(-)
create mode 100644 prisma/migrations/20250309152758_init_league_schema_commit/migration.sql
diff --git a/prisma/migrations/20250309152758_init_league_schema_commit/migration.sql b/prisma/migrations/20250309152758_init_league_schema_commit/migration.sql
new file mode 100644
index 000000000..26c8e8f79
--- /dev/null
+++ b/prisma/migrations/20250309152758_init_league_schema_commit/migration.sql
@@ -0,0 +1,127 @@
+-- CreateEnum
+CREATE TYPE "LeagueName" AS ENUM ('BRONZE', 'SILVER', 'GOLD', 'PLATINUM', 'DIAMOND');
+
+-- CreateEnum
+CREATE TYPE "LeagueColor" AS ENUM ('CD7F32', 'C0C0C0', 'FFD700', 'E5E4E2', 'b9f2ff', 'FF4D4D');
+
+-- CreateEnum
+CREATE TYPE "LeagueAchievementType" AS ENUM ('LEAGUE_WINNER', 'TOP_THREE', 'PROMOTION', 'PERFECT_WEEK', 'SURVIVAL', 'COMEBACK_KING', 'CONSISTENCY', 'SPEED_DEMON');
+
+-- CreateEnum
+CREATE TYPE "LeaguePowerUp" AS ENUM ('DOUBLE_XP', 'SHIELD', 'STREAK_SAVER', 'TIME_FREEZE', 'BONUS_POINTS');
+
+-- CreateTable
+CREATE TABLE "IndividualLeagueData" (
+ "uid" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL,
+ "name" "LeagueName" NOT NULL,
+ "color" "LeagueColor" NOT NULL,
+ "description" TEXT,
+ "xpRequirement" INTEGER NOT NULL,
+ "resetDate" TIMESTAMP(3) NOT NULL,
+ "canBeRelegated" BOOLEAN NOT NULL DEFAULT false,
+ "icon" TEXT,
+ "inactivityThresholdDays" INTEGER DEFAULT 7,
+ "maxPowerUpsPerWeek" INTEGER NOT NULL DEFAULT 3,
+ "xpMultiplier" DOUBLE PRECISION NOT NULL DEFAULT 1.0,
+
+ CONSTRAINT "IndividualLeagueData_pkey" PRIMARY KEY ("uid")
+);
+
+-- CreateTable
+CREATE TABLE "Leagues" (
+ "uid" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL,
+ "leagueDataUid" TEXT NOT NULL,
+ "maxUsers" INTEGER NOT NULL DEFAULT 30,
+ "currentUsers" INTEGER NOT NULL DEFAULT 0,
+ "startDate" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "endDate" TIMESTAMP(3) NOT NULL,
+ "promotionCount" INTEGER NOT NULL DEFAULT 3,
+ "relegationCount" INTEGER NOT NULL DEFAULT 5,
+ "weeklyChallenge" TEXT,
+ "weeklyChallengeXP" INTEGER,
+
+ CONSTRAINT "Leagues_pkey" PRIMARY KEY ("uid")
+);
+
+-- CreateTable
+CREATE TABLE "UserLeague" (
+ "uid" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL,
+ "userUid" TEXT NOT NULL,
+ "leagueUid" TEXT NOT NULL,
+ "position" INTEGER,
+ "weeklyXp" INTEGER NOT NULL DEFAULT 0,
+ "promoted" BOOLEAN NOT NULL DEFAULT false,
+ "relegated" BOOLEAN NOT NULL DEFAULT false,
+ "joinedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "currentStreak" INTEGER NOT NULL DEFAULT 0,
+ "bestPosition" INTEGER,
+ "activePowerUps" "LeaguePowerUp"[],
+ "powerUpExpiryTime" TIMESTAMP(3),
+ "challengeProgress" INTEGER NOT NULL DEFAULT 0,
+ "challengeCompleted" BOOLEAN NOT NULL DEFAULT false,
+
+ CONSTRAINT "UserLeague_pkey" PRIMARY KEY ("uid")
+);
+
+-- CreateTable
+CREATE TABLE "LeagueAchievement" (
+ "uid" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "userUid" TEXT NOT NULL,
+ "leagueDataUid" TEXT NOT NULL,
+ "type" "LeagueAchievementType" NOT NULL,
+ "earnedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "metadata" JSONB,
+ "xpBonus" INTEGER NOT NULL DEFAULT 0,
+
+ CONSTRAINT "LeagueAchievement_pkey" PRIMARY KEY ("uid")
+);
+
+-- CreateTable
+CREATE TABLE "LeagueHistory" (
+ "uid" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "userUid" TEXT NOT NULL,
+ "leagueDataUid" TEXT NOT NULL,
+ "finalPosition" INTEGER NOT NULL,
+ "finalXp" INTEGER NOT NULL,
+ "wasPromoted" BOOLEAN NOT NULL,
+ "wasRelegated" BOOLEAN NOT NULL,
+ "weekStartDate" TIMESTAMP(3) NOT NULL,
+ "weekEndDate" TIMESTAMP(3) NOT NULL,
+ "averageXpPerDay" DOUBLE PRECISION,
+ "powerUpsUsed" INTEGER NOT NULL DEFAULT 0,
+ "challengesCompleted" INTEGER NOT NULL DEFAULT 0,
+
+ CONSTRAINT "LeagueHistory_pkey" PRIMARY KEY ("uid")
+);
+
+-- CreateIndex
+CREATE UNIQUE INDEX "UserLeague_userUid_leagueUid_key" ON "UserLeague"("userUid", "leagueUid");
+
+-- AddForeignKey
+ALTER TABLE "Leagues" ADD CONSTRAINT "Leagues_leagueDataUid_fkey" FOREIGN KEY ("leagueDataUid") REFERENCES "IndividualLeagueData"("uid") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "UserLeague" ADD CONSTRAINT "UserLeague_userUid_fkey" FOREIGN KEY ("userUid") REFERENCES "Users"("uid") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "UserLeague" ADD CONSTRAINT "UserLeague_leagueUid_fkey" FOREIGN KEY ("leagueUid") REFERENCES "Leagues"("uid") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "LeagueAchievement" ADD CONSTRAINT "LeagueAchievement_userUid_fkey" FOREIGN KEY ("userUid") REFERENCES "Users"("uid") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "LeagueAchievement" ADD CONSTRAINT "LeagueAchievement_leagueDataUid_fkey" FOREIGN KEY ("leagueDataUid") REFERENCES "IndividualLeagueData"("uid") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "LeagueHistory" ADD CONSTRAINT "LeagueHistory_userUid_fkey" FOREIGN KEY ("userUid") REFERENCES "Users"("uid") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "LeagueHistory" ADD CONSTRAINT "LeagueHistory_leagueDataUid_fkey" FOREIGN KEY ("leagueDataUid") REFERENCES "IndividualLeagueData"("uid") ON DELETE RESTRICT ON UPDATE CASCADE;
diff --git a/prisma/schema/leagues.prisma b/prisma/schema/leagues.prisma
index ef1b9694e..b929e098e 100644
--- a/prisma/schema/leagues.prisma
+++ b/prisma/schema/leagues.prisma
@@ -16,22 +16,22 @@ enum LeagueColor {
}
enum LeagueAchievementType {
- LEAGUE_WINNER
- TOP_THREE
- PROMOTION
- PERFECT_WEEK
- SURVIVAL
- COMEBACK_KING
- CONSISTENCY
- SPEED_DEMON
+ LEAGUE_WINNER // did the user win their league last week
+ TOP_THREE // did the user finish in the top 3 of their league last week
+ PROMOTION // did the user get promoted last week
+ PERFECT_WEEK // did the user get 100% of the bonus xp last week
+ SURVIVAL // did the user avoid relegation last week
+ COMEBACK_KING // did the user come back from the bottom of the league last week
+ CONSISTENCY // did the user finish in the top 3 of their league for 3 weeks in a row
+ SPEED_DEMON // did the user get to the top of the league in the shortest time possible
}
enum LeaguePowerUp {
- DOUBLE_XP
- SHIELD
- STREAK_SAVER
- TIME_FREEZE
- BONUS_POINTS
+ DOUBLE_XP // double the users xp for the week
+ SHIELD // prevent the user= from being relegated for the week
+ STREAK_SAVER // prevent the user from being relegated for the next 3 weeks
+ TIME_FREEZE // freeze the users league for the week
+ BONUS_POINTS // give the user bonus xp for the week
}
// schema for a league, no relation to a users as
@@ -45,7 +45,7 @@ model IndividualLeagueData {
// the name of the league
name LeagueName
- // the league's color
+ // the league's color (used for styling)
color LeagueColor
// a brief description of the league (may or may not be used)
@@ -54,7 +54,7 @@ model IndividualLeagueData {
// the amount of xp required to be in the league
xpRequirement Int
- // when the league resets
+ // when the league resets (usually weekly)
resetDate DateTime
// leagues such as the bronze league cannot be relegated
@@ -64,28 +64,24 @@ model IndividualLeagueData {
// the league's icon
icon String?
- // Minimum XP required to avoid relegation
- minXpToAvoidRelegation Int @default(100)
-
- // XP threshold for promotion
- xpThresholdForPromotion Int?
-
- // League rules
- inactivityThresholdDays Int @default(7)
- minWeeksBeforePromotion Int @default(1)
+ // league rules (may or may not be used)
+ inactivityThresholdDays Int? @default(7)
// Maximum number of power-ups allowed per week
maxPowerUpsPerWeek Int @default(3)
- // Bonus XP multiplier for this league
+ // bonus XP multiplier for this league
xpMultiplier Float @default(1.0)
- // Relations
+ // relations
leagues Leagues[]
achievements LeagueAchievement[]
history LeagueHistory[]
}
+// we can have multiple instances of the same league
+// e.g. bronze league, silver league, gold league, etc.
+// this is so the same league can be used for different groups of users
model Leagues {
// standard prisma fields
uid String @id @default(uuid())
@@ -96,35 +92,33 @@ model Leagues {
leagueData IndividualLeagueData @relation(fields: [leagueDataUid], references: [uid])
leagueDataUid String
- // Maximum number of users in a league group
- maxUsers Int @default(50)
+ // maximum number of users in the league
+ maxUsers Int @default(30)
- // Current number of users
+ // current number of users in the league
currentUsers Int @default(0)
- // Whether the league is accepting new users
- isOpen Boolean @default(true)
-
- // When this league group started
+ // when this league group started
startDate DateTime @default(now())
// When this league group ends
endDate DateTime
- // Top users who will be promoted
+ // how many users will be promoted
promotionCount Int @default(3)
- // Bottom users who will be relegated
+ // how many users will be relegated
relegationCount Int @default(5)
- // Weekly challenges for bonus XP
+ // weekly challenges for bonus xp
weeklyChallenge String?
weeklyChallengeXP Int?
- // Relations
+ // relations
users UserLeague[]
}
+// the data tied to the user in the league
model UserLeague {
uid String @id @default(uuid())
createdAt DateTime @default(now())
@@ -138,10 +132,10 @@ model UserLeague {
league Leagues @relation(fields: [leagueUid], references: [uid])
leagueUid String
- // Their position in the league
+ // users position in the league
position Int?
- // Their weekly XP in this league
+ // users weekly xp in this league
weeklyXp Int @default(0)
// Whether they've been promoted/relegated
@@ -151,10 +145,8 @@ model UserLeague {
// When they joined this league
joinedAt DateTime @default(now())
- // Gamification features
currentStreak Int @default(0)
bestPosition Int?
- powerUpsRemaining Int @default(3)
activePowerUps LeaguePowerUp[]
powerUpExpiryTime DateTime?
@@ -166,6 +158,7 @@ model UserLeague {
@@unique([userUid, leagueUid])
}
+// FUTURE PROOFING - may not be used initially
model LeagueAchievement {
uid String @id @default(uuid())
createdAt DateTime @default(now())
@@ -191,6 +184,7 @@ model LeagueAchievement {
xpBonus Int @default(0)
}
+// FUTURE PROOFING - may not be used initially
model LeagueHistory {
uid String @id @default(uuid())
createdAt DateTime @default(now())
From 1e48ebcaa5cacd19754d3654d4599ad98db9e69f Mon Sep 17 00:00:00 2001
From: Logan Ford <110533855+Logannford@users.noreply.github.com>
Date: Sun, 9 Mar 2025 15:50:03 +0000
Subject: [PATCH 05/18] adds admin area w/ dummy data
---
src/app/(app)/admin/layout.tsx | 30 ++++
.../(app)/admin/leagues/achievements/page.tsx | 145 ++++++++++++++++
src/app/(app)/admin/leagues/list/page.tsx | 160 ++++++++++++++++++
src/app/(app)/admin/leagues/page.tsx | 132 +++++++++++++++
src/components/ui/icons/icons/map.tsx | 32 ++++
5 files changed, 499 insertions(+)
create mode 100644 src/app/(app)/admin/leagues/achievements/page.tsx
create mode 100644 src/app/(app)/admin/leagues/list/page.tsx
create mode 100644 src/app/(app)/admin/leagues/page.tsx
create mode 100644 src/components/ui/icons/icons/map.tsx
diff --git a/src/app/(app)/admin/layout.tsx b/src/app/(app)/admin/layout.tsx
index 972bfae9d..bfdac25b0 100644
--- a/src/app/(app)/admin/layout.tsx
+++ b/src/app/(app)/admin/layout.tsx
@@ -85,6 +85,36 @@ export default async function AdminLayout({ children }: { children: React.ReactN
+