Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/leaderboard leagues #526

Merged
merged 29 commits into from
Mar 21, 2025
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9b82afb
feat: init league schema commit
Logannford Mar 9, 2025
8c7bbf9
feat: adding more fields to league data
Logannford Mar 9, 2025
677480c
hotfix: user league relations
Logannford Mar 9, 2025
b6441dd
feat: creates league schema and applying to db
Logannford Mar 9, 2025
1e48ebc
adds admin area w/ dummy data
Logannford Mar 9, 2025
1cb0f46
Merge branch 'main' into feat/create-leaderboard-leagues
Logannford Mar 9, 2025
752e7bc
lint
Logannford Mar 9, 2025
75d7e2d
feat: init league schema commit (#525)
Logannford Mar 9, 2025
4fc18f5
feat: ability to create new leagues (needs refining)
Logannford Mar 9, 2025
ec3b988
feat: adds individual league update page, form & action
Logannford Mar 9, 2025
7e34317
Merge branch 'main' into feat/league-admin-area
Logannford Mar 9, 2025
b104a43
Merge branch 'main' into feat/league-admin-area
Logannford Mar 9, 2025
ae346a2
Merge branch 'main' into feat/league-admin-area
Logannford Mar 9, 2025
6ce8846
fix: minor hotfixes to admin edit league
Logannford Mar 9, 2025
87e8b84
lint
Logannford Mar 9, 2025
b02253c
feat: ability to create new leagues (needs refining) (#527)
Logannford Mar 9, 2025
ee6a7b0
feat: init league carousel commit
Logannford Mar 9, 2025
9821730
chore: remove separators & minor league carousel progress
Logannford Mar 9, 2025
ef87e26
feat(leaderboard): progress with new leaderboard league hero
Logannford Mar 10, 2025
0f9c5ab
feat(leaderboard): more work with league hero creation + layout chang…
Logannford Mar 10, 2025
c5adefd
feat(leaderboard): more progress with league hero styling :)
Logannford Mar 10, 2025
e96f3e8
feat(leagues): clicking league card in hero will make it the active l…
Logannford Mar 10, 2025
1b499c3
Merge branch 'deployment/staging' into feature/leaderboard-leagues
Logannford Mar 15, 2025
eac2388
Merge branch 'feature/leaderboard-leagues' into feat/league-page-hero…
Logannford Mar 15, 2025
93ff7e6
lint & hotfixes
Logannford Mar 16, 2025
795a49c
feat: init league carousel commit (#528)
Logannford Mar 16, 2025
24b4335
Merge branch 'deployment/staging' into feature/leaderboard-leagues
Logannford Mar 20, 2025
5180233
bump
Logannford Mar 21, 2025
084ff6b
Merge branch 'deployment/staging' into feature/leaderboard-leagues
Logannford Mar 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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;
218 changes: 218 additions & 0 deletions prisma/schema/leagues.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
enum LeagueName {
BRONZE
SILVER
GOLD
PLATINUM
DIAMOND
}

enum LeagueColor {
CD7F32
C0C0C0
FFD700
E5E4E2
b9f2ff
FF4D4D
}

enum LeagueAchievementType {
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 // 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
// 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 (used for styling)
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 (usually weekly)
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?

// 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
xpMultiplier Float @default(1.0)

// 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())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

// connect to the league data
leagueData IndividualLeagueData @relation(fields: [leagueDataUid], references: [uid])
leagueDataUid String

// maximum number of users in the league
maxUsers Int @default(30)

// current number of users in the league
currentUsers Int @default(0)

// when this league group started
startDate DateTime @default(now())

// When this league group ends
endDate DateTime

// how many users will be promoted
promotionCount Int @default(3)

// how many users will be relegated
relegationCount Int @default(5)

// weekly challenges for bonus xp
weeklyChallenge String?
weeklyChallengeXP Int?

// relations
users UserLeague[]
}

// the data tied to the user in the league
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

// users position in the league
position Int?

// users 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())

currentStreak Int @default(0)
bestPosition Int?
activePowerUps LeaguePowerUp[]
powerUpExpiryTime DateTime?

// Weekly challenge progress
challengeProgress Int @default(0)
challengeCompleted Boolean @default(false)

// Unique constraint
@@unique([userUid, leagueUid])
}

// FUTURE PROOFING - may not be used initially
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)
}

// FUTURE PROOFING - may not be used initially
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)
}
6 changes: 6 additions & 0 deletions prisma/schema/users.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ model Users {
roadmaps UserRoadmaps[]
studyPathEnrollments UserStudyPath[]
userMissions UserMission[]

LeagueHistory LeagueHistory[]

LeagueAchievement LeagueAchievement[]

UserLeague UserLeague[]
}

model Streaks {
Expand Down
Loading