diff --git a/app/models/league.ts b/app/models/league.ts index 195da9c..33254d0 100644 --- a/app/models/league.ts +++ b/app/models/league.ts @@ -1,4 +1,4 @@ -import { eq } from "drizzle-orm"; +import { eq, desc } from "drizzle-orm"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; @@ -47,6 +47,13 @@ export async function deleteLeague(id: string): Promise { await db.delete(schema.leagues).where(eq(schema.leagues.id, id)); } +export async function setCurrentSeason( + leagueId: string, + seasonId: string | null +): Promise { + return await updateLeague(leagueId, { currentSeasonId: seasonId }); +} + export async function listLeagues(options?: { limit?: number; offset?: number; @@ -58,3 +65,26 @@ export async function listLeagues(options?: { orderBy: (leagues, { desc }) => [desc(leagues.createdAt)], }); } + +export async function findLeaguesWithActiveSeasonsByUserId( + userId: string +): Promise { + const db = database(); + + // Query to find all leagues where user has a team in the current season + const leagues = await db + .selectDistinct({ + id: schema.leagues.id, + name: schema.leagues.name, + createdBy: schema.leagues.createdBy, + currentSeasonId: schema.leagues.currentSeasonId, + createdAt: schema.leagues.createdAt, + updatedAt: schema.leagues.updatedAt, + }) + .from(schema.leagues) + .innerJoin(schema.teams, eq(schema.teams.seasonId, schema.leagues.currentSeasonId)) + .where(eq(schema.teams.ownerId, userId)) + .orderBy(desc(schema.leagues.createdAt)); + + return leagues; +} diff --git a/app/routes/home.tsx b/app/routes/home.tsx index d0622ac..ba6fcaa 100644 --- a/app/routes/home.tsx +++ b/app/routes/home.tsx @@ -1,10 +1,19 @@ import { useEffect } from "react"; -import { useSearchParams } from "react-router"; +import { Link, useSearchParams } from "react-router"; import { toast } from "sonner"; +import { getAuth } from "@clerk/react-router/server"; import type { Route } from "./+types/home"; -import { Welcome } from "../welcome/welcome"; -import { expressValueContext } from "~/contexts/express"; +import { findLeaguesWithActiveSeasonsByUserId } from "~/models/league"; +import { findSeasonById } from "~/models/season"; +import { Button } from "~/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "~/components/ui/card"; export function meta() { return [ @@ -16,22 +25,118 @@ export function meta() { ]; } -export async function loader({ context }: Route.LoaderArgs) { +export async function loader(args: Route.LoaderArgs) { + const { userId } = await getAuth(args); + + if (!userId) { + return { leagues: [], isLoggedIn: false }; + } + + // Fetch leagues where user has a team in the current season + const leagues = await findLeaguesWithActiveSeasonsByUserId(userId); + + // Fetch season details for each league's current season + const leaguesWithSeasons = await Promise.all( + leagues.map(async (league) => { + const season = league.currentSeasonId + ? await findSeasonById(league.currentSeasonId) + : null; + return { + ...league, + currentSeason: season, + }; + }) + ); + return { - message: context.get(expressValueContext) || "No message from Express", + leagues: leaguesWithSeasons, + isLoggedIn: true, }; } export default function Home({ loaderData }: Route.ComponentProps) { + const { leagues, isLoggedIn } = loaderData; const [searchParams, setSearchParams] = useSearchParams(); useEffect(() => { if (searchParams.get("deleted") === "true") { toast.success("League deleted successfully"); - // Remove the query parameter setSearchParams({}); } }, [searchParams, setSearchParams]); - return ; + if (!isLoggedIn) { + return ( +
+

Welcome to Brackt

+

+ Please sign in to view your leagues +

+
+ ); + } + + return ( +
+
+

My Leagues

+ +
+ + {leagues.length === 0 ? ( + + + No Active Leagues + + You don't have any teams in leagues with active seasons yet + + + + + + + ) : ( +
+ {leagues.map((league) => ( + + + + {league.name} + + Created {new Date(league.createdAt).toLocaleDateString()} + + + + {league.currentSeason ? ( +
+
+

+ Current Season +

+

{league.currentSeason.year}

+
+
+

Status

+

+ {league.currentSeason.status.replace("_", " ")} +

+
+
+ ) : ( +

+ No active season +

+ )} +
+
+ + ))} +
+ )} +
+ ); } diff --git a/app/routes/leagues/new.tsx b/app/routes/leagues/new.tsx index 2db15e4..a1cd833 100644 --- a/app/routes/leagues/new.tsx +++ b/app/routes/leagues/new.tsx @@ -1,6 +1,6 @@ import { Form, Link, redirect } from "react-router"; import { getAuth } from "@clerk/react-router/server"; -import { createLeague } from "~/models/league"; +import { createLeague, setCurrentSeason } from "~/models/league"; import { createSeason } from "~/models/season"; import { createManyTeams } from "~/models/team"; import { createCommissioner } from "~/models/commissioner"; @@ -70,6 +70,9 @@ export async function action(args: Route.ActionArgs) { status: "pre_draft", }); + // Set this as the current season for the league + await setCurrentSeason(league.id, season.id); + // Create teams with placeholder names const teams = Array.from({ length: teamCountNum }, (_, i) => ({ seasonId: season.id, diff --git a/database/schema.ts b/database/schema.ts index 50e1235..810808c 100644 --- a/database/schema.ts +++ b/database/schema.ts @@ -25,6 +25,7 @@ export const leagues = pgTable("leagues", { id: uuid("id").primaryKey().defaultRandom(), name: varchar("name", { length: 255 }).notNull(), createdBy: varchar("created_by", { length: 255 }).notNull(), // Clerk user ID + currentSeasonId: uuid("current_season_id"), // References the active season createdAt: timestamp("created_at").defaultNow().notNull(), updatedAt: timestamp("updated_at").defaultNow().notNull(), }); diff --git a/drizzle/0006_needy_junta.sql b/drizzle/0006_needy_junta.sql new file mode 100644 index 0000000..8af1ac5 --- /dev/null +++ b/drizzle/0006_needy_junta.sql @@ -0,0 +1 @@ +ALTER TABLE "leagues" ADD COLUMN "current_season_id" uuid; \ 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..feca228 --- /dev/null +++ b/drizzle/meta/0006_snapshot.json @@ -0,0 +1,347 @@ +{ + "id": "afd92b21-6a89-4b8c-80ac-414a320db897", + "prevId": "6839ce43-d04c-4246-9b00-7820935e1166", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.commissioners": { + "name": "commissioners", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "league_id": { + "name": "league_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "commissioners_league_id_leagues_id_fk": { + "name": "commissioners_league_id_leagues_id_fk", + "tableFrom": "commissioners", + "tableTo": "leagues", + "columnsFrom": [ + "league_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.leagues": { + "name": "leagues", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "current_season_id": { + "name": "current_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.seasons": { + "name": "seasons", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "league_id": { + "name": "league_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "year": { + "name": "year", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "season_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pre_draft'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "seasons_league_id_leagues_id_fk": { + "name": "seasons_league_id_leagues_id_fk", + "tableFrom": "seasons", + "tableTo": "leagues", + "columnsFrom": [ + "league_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.teams": { + "name": "teams", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "teams_season_id_seasons_id_fk": { + "name": "teams_season_id_seasons_id_fk", + "tableFrom": "teams", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "clerk_id": { + "name": "clerk_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "first_name": { + "name": "first_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "last_name": { + "name": "last_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "image_url": { + "name": "image_url", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_clerk_id_unique": { + "name": "users_clerk_id_unique", + "nullsNotDistinct": false, + "columns": [ + "clerk_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.season_status": { + "name": "season_status", + "schema": "public", + "values": [ + "pre_draft", + "draft", + "active", + "completed" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 5063c4a..b45ceda 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -43,6 +43,13 @@ "when": 1760169537260, "tag": "0005_abnormal_thunderbird", "breakpoints": true + }, + { + "idx": 6, + "version": "7", + "when": 1760241924414, + "tag": "0006_needy_junta", + "breakpoints": true } ] } \ No newline at end of file