Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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,77 @@
import { Kysely, sql } from 'kysely';

const TABLE = 'league';
const CHECK_NAME = 'ck_league_sport';

export async function up(db: Kysely<any>): Promise<void> {
const engine = process.env.DB_ENGINE ?? 'sqlite';

if (engine === 'sqlite') {
// 1) Create new table with sport + CHECK constraint
await sql`
CREATE TABLE league_new (
league_id TEXT PRIMARY KEY,
name TEXT NOT NULL,
sport TEXT NOT NULL DEFAULT 'NFL' CHECK (sport IN ('NFL','GOLF')),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
)
`.execute(db);

// 2) Copy existing data (sport will take DEFAULT 'NFL')
await sql`
INSERT INTO league_new (league_id, name, created_at)
SELECT league_id, name, created_at
FROM ${sql.table(TABLE)}
`.execute(db);

// 3) Swap tables
await sql`DROP TABLE ${sql.table(TABLE)}`.execute(db);
await sql`ALTER TABLE league_new RENAME TO ${sql.table(TABLE)}`.execute(db);
} else if (engine === 'postgres') {
await db.schema
.alterTable(TABLE)
.addColumn('sport', 'text', (col) => col.notNull().defaultTo('NFL'))
.execute();

await sql`
ALTER TABLE ${sql.table(TABLE)}
ADD CONSTRAINT ${sql.raw(CHECK_NAME)}
CHECK (sport IN ('NFL','GOLF'))
`.execute(db);
} else {
throw new Error(`Unsupported DB_ENGINE: ${engine}`);
}
}

export async function down(db: Kysely<any>): Promise<void> {
const engine = process.env.DB_ENGINE ?? 'sqlite';

if (engine === 'sqlite') {
// Recreate original schema without sport
await sql`
CREATE TABLE league_new (
league_id TEXT PRIMARY KEY,
name TEXT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
)
`.execute(db);

await sql`
INSERT INTO league_new (league_id, name, created_at)
SELECT league_id, name, created_at
FROM ${sql.table(TABLE)}
`.execute(db);

await sql`DROP TABLE ${sql.table(TABLE)}`.execute(db);
await sql`ALTER TABLE league_new RENAME TO ${sql.table(TABLE)}`.execute(db);
} else if (engine === 'postgres') {
await sql`
ALTER TABLE ${sql.table(TABLE)}
DROP CONSTRAINT IF EXISTS ${sql.raw(CHECK_NAME)}
`.execute(db);

await db.schema.alterTable(TABLE).dropColumn('sport').execute();
} else {
throw new Error(`Unsupported DB_ENGINE: ${engine}`);
}
}
1 change: 1 addition & 0 deletions backend/src/leagues/entities/league.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Selectable } from 'kysely';
export interface ILeague {
leagueId: string & tags.Format<'uuid'>;
name: string;
sport: string;
}

export type League = Selectable<ILeague>;
Expand Down
1 change: 1 addition & 0 deletions backend/src/leagues/leagues.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export class DatabaseLeaguesRepository extends LeaguesRepository {
.values({
leagueId: crypto.randomUUID(),
name: league.name,
sport: league.sport,
})
.returningAll()
.executeTakeFirstOrThrow();
Expand Down
44 changes: 27 additions & 17 deletions frontend/src/components/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const Dashboard = () => {
const [user, setUser] = useState(null);
const [leagues, setLeagues] = useState([]);
const [newLeague, setNewLeague] = useState("");
const [leagueSport, setLeagueSport] = useState("NFL");
const [joinCode, setJoinCode] = useState("");
const [showCreateForm, setShowCreateForm] = useState(false);
const [showJoinForm, setShowJoinForm] = useState(false);
Expand Down Expand Up @@ -90,6 +91,7 @@ const Dashboard = () => {
// Wire create league to backend leagues endpoints.
const addLeague = async () => {
const name = newLeague.trim();
const sport = leagueSport;
if (!name) return;
console.log(user);
const userId = user && (user.id || user.userId);
Expand All @@ -106,7 +108,7 @@ const Dashboard = () => {
method: "POST",
headers: {"Content-Type": "application/json"},
credentials: "include",
body: JSON.stringify({name}),
body: JSON.stringify({name: name, sport: sport}),
});

if (!createRes.ok) {
Expand Down Expand Up @@ -269,6 +271,7 @@ const Dashboard = () => {
<p className="font-semibold">
{league.name || league.leagueName || "Unnamed League"}
</p>
<p>{league.sport}</p>
<p>{role === "admin" ? "Admin" : role}</p>
<button
className="px-3 py-1 font-bold text-[#102131] bg-[#00ceb8] rounded hover:bg-[#00ceb8]/80"
Expand Down Expand Up @@ -297,22 +300,29 @@ const Dashboard = () => {
</button>
</div>

{showCreateForm && (
<div className="flex mt-4 space-x-2">
<input
className="flex-1 p-2 bg-transparent border rounded border-[#3a465b]"
placeholder="Enter League Name..."
value={newLeague}
onChange={(e) => setNewLeague(e.target.value)}
/>
<button
className="btn-primary"
onClick={addLeague}
>
Submit
</button>
</div>
)}
{showCreateForm && (
<div className="flex mt-4 space-x-2">
<input
className="flex-1 p-2 bg-transparent border rounded border-[#3a465b]"
placeholder="Enter League Name..."
value={newLeague}
onChange={(e) => setNewLeague(e.target.value)}
/>

<select
className="p-2 bg-transparent border rounded border-[#3a465b]"
value={leagueSport}
onChange={(e) => setLeagueSport(e.target.value)}
>
<option value="NFL">NFL</option>
<option value="GOLF">GOLF</option>
</select>

<button className="btn-primary" onClick={addLeague}>
Submit
</button>
</div>
)}

{showJoinForm && (
<div className="flex mt-4 space-x-2">
Expand Down