feat: add username field to users and display in league views

This commit is contained in:
Chris Parsons 2025-10-14 21:20:58 -07:00
parent 78dc3f7d96
commit 61929536a1
7 changed files with 1057 additions and 49 deletions

View file

@ -56,6 +56,7 @@ export async function updateUserByClerkId(
export async function findOrCreateUser(clerkUser: { export async function findOrCreateUser(clerkUser: {
id: string; id: string;
emailAddresses?: Array<{ emailAddress: string }>; emailAddresses?: Array<{ emailAddress: string }>;
username?: string | null;
firstName?: string | null; firstName?: string | null;
lastName?: string | null; lastName?: string | null;
imageUrl?: string; imageUrl?: string;
@ -84,6 +85,7 @@ export async function findOrCreateUser(clerkUser: {
// Update user info in case it changed in Clerk // Update user info in case it changed in Clerk
return await updateUserByClerkId(clerkUser.id, { return await updateUserByClerkId(clerkUser.id, {
email, email,
username: clerkUser.username || undefined,
displayName, displayName,
firstName: clerkUser.firstName || undefined, firstName: clerkUser.firstName || undefined,
lastName: clerkUser.lastName || undefined, lastName: clerkUser.lastName || undefined,
@ -95,6 +97,7 @@ export async function findOrCreateUser(clerkUser: {
return await createUser({ return await createUser({
clerkId: clerkUser.id, clerkId: clerkUser.id,
email, email,
username: clerkUser.username || undefined,
displayName, displayName,
firstName: clerkUser.firstName || undefined, firstName: clerkUser.firstName || undefined,
lastName: clerkUser.lastName || undefined, lastName: clerkUser.lastName || undefined,

View file

@ -45,7 +45,7 @@ export async function action({ request }: Route.ActionArgs) {
console.log(`Webhook received: ${eventType}`); console.log(`Webhook received: ${eventType}`);
if (eventType === "user.created" || eventType === "user.updated") { if (eventType === "user.created" || eventType === "user.updated") {
const { id, email_addresses, first_name, last_name, image_url } = evt.data; const { id, email_addresses, username, first_name, last_name, image_url } = evt.data;
try { try {
if (eventType === "user.created") { if (eventType === "user.created") {
@ -56,12 +56,13 @@ export async function action({ request }: Route.ActionArgs) {
email_addresses?.map((e: any) => ({ email_addresses?.map((e: any) => ({
emailAddress: e.email_address, emailAddress: e.email_address,
})) || [], })) || [],
username,
firstName: first_name, firstName: first_name,
lastName: last_name, lastName: last_name,
imageUrl: image_url, imageUrl: image_url,
}); });
console.log( console.log(
`User created in database: ${user.id} (${user.displayName})` `User created in database: ${user.id} (${user.username || user.displayName})`
); );
} else { } else {
// Update existing user // Update existing user
@ -83,13 +84,14 @@ export async function action({ request }: Route.ActionArgs) {
const user = await updateUserByClerkId(id, { const user = await updateUserByClerkId(id, {
email, email,
username,
displayName, displayName,
firstName: first_name || undefined, firstName: first_name || undefined,
lastName: last_name || undefined, lastName: last_name || undefined,
imageUrl: image_url, imageUrl: image_url,
}); });
console.log( console.log(
`User updated in database: ${user.id} (${user.displayName})` `User updated in database: ${user.id} (${user.username || user.displayName})`
); );
} }
} catch (error) { } catch (error) {

View file

@ -9,6 +9,7 @@ import {
findCommissionersByLeagueId, findCommissionersByLeagueId,
isUserLeagueMember, isUserLeagueMember,
isCommissioner, isCommissioner,
findUserByClerkId,
} from "~/models"; } from "~/models";
import type { Route } from "./+types/$leagueId"; import type { Route } from "./+types/$leagueId";
import { Button } from "~/components/ui/button"; import { Button } from "~/components/ui/button";
@ -57,6 +58,34 @@ export async function loader(args: Route.LoaderArgs) {
// Fetch teams for current season // Fetch teams for current season
const teams = season ? await findTeamsBySeasonId(season.id) : []; const teams = season ? await findTeamsBySeasonId(season.id) : [];
// Fetch user data for team owners
const ownerIds = teams.map(t => t.ownerId).filter((id): id is string => id !== null);
const uniqueOwnerIds = [...new Set(ownerIds)];
const owners = await Promise.all(
uniqueOwnerIds.map(async (ownerId) => {
const user = await findUserByClerkId(ownerId);
return user ? { clerkId: ownerId, name: user.username || user.displayName } : null;
})
);
const ownerMap = new Map(
owners.filter((o): o is NonNullable<typeof o> => o !== null).map(o => [o.clerkId, o.name])
);
// Fetch user data for commissioners
const commissionerIds = commissioners.map(c => c.userId);
const commissionerUsers = await Promise.all(
commissionerIds.map(async (commissionerId) => {
const user = await findUserByClerkId(commissionerId);
return user ? { clerkId: commissionerId, name: user.username || user.displayName } : null;
})
);
const commissionerMap = new Map(
commissionerUsers.filter((c): c is NonNullable<typeof c> => c !== null).map(c => [c.clerkId, c.name])
);
// Count available teams
const availableTeamCount = teams.filter(t => !t.ownerId).length;
return { return {
league, league,
season, season,
@ -64,11 +93,24 @@ export async function loader(args: Route.LoaderArgs) {
commissioners, commissioners,
currentUserId: userId, currentUserId: userId,
isUserCommissioner, isUserCommissioner,
ownerMap: Object.fromEntries(ownerMap),
commissionerMap: Object.fromEntries(commissionerMap),
availableTeamCount,
}; };
} }
export default function LeagueHome({ loaderData }: Route.ComponentProps) { export default function LeagueHome({ loaderData }: Route.ComponentProps) {
const { league, season, teams, commissioners, currentUserId, isUserCommissioner } = loaderData; const {
league,
season,
teams,
commissioners,
currentUserId,
isUserCommissioner,
ownerMap,
commissionerMap,
availableTeamCount,
} = loaderData;
const [searchParams, setSearchParams] = useSearchParams(); const [searchParams, setSearchParams] = useSearchParams();
const [copied, setCopied] = useState(false); const [copied, setCopied] = useState(false);
@ -124,6 +166,38 @@ export default function LeagueHome({ loaderData }: Route.ComponentProps) {
</div> </div>
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3"> <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
{isUserCommissioner && season && availableTeamCount > 0 && (
<Card className="md:col-span-2 lg:col-span-3">
<CardHeader>
<CardTitle>Invite Link</CardTitle>
<CardDescription>
Share this link to invite people to join your league ({availableTeamCount} spot{availableTeamCount !== 1 ? "s" : ""} available)
</CardDescription>
</CardHeader>
<CardContent className="space-y-3">
<div className="flex items-center gap-2">
<input
type="text"
readOnly
value={`${typeof window !== "undefined" ? window.location.origin : ""}/i/${season.inviteCode}`}
className="flex-1 px-3 py-2 text-sm border rounded-md bg-muted"
onClick={(e) => e.currentTarget.select()}
/>
<Button
onClick={handleCopyInviteLink}
variant={copied ? "default" : "outline"}
size="sm"
>
{copied ? "Copied!" : "Copy"}
</Button>
</div>
<p className="text-xs text-muted-foreground">
Anyone with this link can join your league
</p>
</CardContent>
</Card>
)}
<Card className="md:col-span-2 lg:col-span-3"> <Card className="md:col-span-2 lg:col-span-3">
<CardHeader> <CardHeader>
<CardTitle>Teams</CardTitle> <CardTitle>Teams</CardTitle>
@ -135,6 +209,7 @@ export default function LeagueHome({ loaderData }: Route.ComponentProps) {
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3"> <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{teams.map((team) => { {teams.map((team) => {
const isOwned = team.ownerId === currentUserId; const isOwned = team.ownerId === currentUserId;
const ownerName = team.ownerId ? ownerMap[team.ownerId] : null;
return ( return (
<Card <Card
key={team.id} key={team.id}
@ -149,7 +224,7 @@ export default function LeagueHome({ loaderData }: Route.ComponentProps) {
Your Team Your Team
</span> </span>
) : ( ) : (
"Owned" <span>{ownerName || "Unknown Owner"}</span>
) )
) : ( ) : (
<span className="text-muted-foreground"> <span className="text-muted-foreground">
@ -187,38 +262,6 @@ export default function LeagueHome({ loaderData }: Route.ComponentProps) {
</CardContent> </CardContent>
</Card> </Card>
{isUserCommissioner && season && (
<Card>
<CardHeader>
<CardTitle>Invite Link</CardTitle>
<CardDescription>
Share this link to invite people to join your league
</CardDescription>
</CardHeader>
<CardContent className="space-y-3">
<div className="flex items-center gap-2">
<input
type="text"
readOnly
value={`${typeof window !== "undefined" ? window.location.origin : ""}/i/${season.inviteCode}`}
className="flex-1 px-3 py-2 text-sm border rounded-md bg-muted"
onClick={(e) => e.currentTarget.select()}
/>
<Button
onClick={handleCopyInviteLink}
variant={copied ? "default" : "outline"}
size="sm"
>
{copied ? "Copied!" : "Copy"}
</Button>
</div>
<p className="text-xs text-muted-foreground">
Anyone with this link can join your league
</p>
</CardContent>
</Card>
)}
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle>Commissioners</CardTitle> <CardTitle>Commissioners</CardTitle>
@ -228,24 +271,22 @@ export default function LeagueHome({ loaderData }: Route.ComponentProps) {
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="space-y-2"> <div className="space-y-2">
{commissioners.map((commissioner, index) => ( {commissioners.map((commissioner) => {
const commissionerName = commissionerMap[commissioner.userId];
return (
<div <div
key={commissioner.id} key={commissioner.id}
className="flex items-center justify-between py-2 border-b last:border-0" className="py-2 border-b last:border-0"
> >
<div>
<p className="font-medium"> <p className="font-medium">
Commissioner {index + 1} {commissionerName || "Unknown User"}
{commissioner.userId === currentUserId && ( {commissioner.userId === currentUserId && (
<span className="ml-2 text-xs text-primary">(You)</span> <span className="ml-2 text-xs text-primary">(You)</span>
)} )}
</p> </p>
<p className="text-xs text-muted-foreground">
Since {new Date(commissioner.createdAt).toLocaleDateString()}
</p>
</div> </div>
</div> );
))} })}
</div> </div>
</CardContent> </CardContent>
</Card> </Card>

View file

@ -6,6 +6,7 @@ export const users = pgTable("users", {
id: uuid("id").primaryKey().defaultRandom(), id: uuid("id").primaryKey().defaultRandom(),
clerkId: varchar("clerk_id", { length: 255 }).notNull().unique(), clerkId: varchar("clerk_id", { length: 255 }).notNull().unique(),
email: varchar("email", { length: 255 }).notNull(), email: varchar("email", { length: 255 }).notNull(),
username: varchar("username", { length: 255 }),
displayName: varchar("display_name", { length: 255 }), displayName: varchar("display_name", { length: 255 }),
firstName: varchar("first_name", { length: 255 }), firstName: varchar("first_name", { length: 255 }),
lastName: varchar("last_name", { length: 255 }), lastName: varchar("last_name", { length: 255 }),

View file

@ -0,0 +1 @@
ALTER TABLE "users" ADD COLUMN "username" varchar(255);

View file

@ -0,0 +1,953 @@
{
"id": "93dd6587-4fbf-4269-8777-db6a88e40d38",
"prevId": "682234f1-8997-43d8-a434-46aead4ef32f",
"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.participant_results": {
"name": "participant_results",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"participant_id": {
"name": "participant_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"sports_season_id": {
"name": "sports_season_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"final_position": {
"name": "final_position",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"points_awarded": {
"name": "points_awarded",
"type": "integer",
"primaryKey": false,
"notNull": true,
"default": 0
},
"qualifying_points": {
"name": "qualifying_points",
"type": "numeric(10, 2)",
"primaryKey": false,
"notNull": false
},
"notes": {
"name": "notes",
"type": "text",
"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": {
"participant_results_participant_id_participants_id_fk": {
"name": "participant_results_participant_id_participants_id_fk",
"tableFrom": "participant_results",
"tableTo": "participants",
"columnsFrom": [
"participant_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
},
"participant_results_sports_season_id_sports_seasons_id_fk": {
"name": "participant_results_sports_season_id_sports_seasons_id_fk",
"tableFrom": "participant_results",
"tableTo": "sports_seasons",
"columnsFrom": [
"sports_season_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.participants": {
"name": "participants",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"sports_season_id": {
"name": "sports_season_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"short_name": {
"name": "short_name",
"type": "varchar(100)",
"primaryKey": false,
"notNull": false
},
"external_id": {
"name": "external_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": {
"participants_sports_season_id_sports_seasons_id_fk": {
"name": "participants_sports_season_id_sports_seasons_id_fk",
"tableFrom": "participants",
"tableTo": "sports_seasons",
"columnsFrom": [
"sports_season_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.season_sports": {
"name": "season_sports",
"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
},
"sports_season_id": {
"name": "sports_season_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"season_sports_season_id_seasons_id_fk": {
"name": "season_sports_season_id_seasons_id_fk",
"tableFrom": "season_sports",
"tableTo": "seasons",
"columnsFrom": [
"season_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
},
"season_sports_sports_season_id_sports_seasons_id_fk": {
"name": "season_sports_sports_season_id_sports_seasons_id_fk",
"tableFrom": "season_sports",
"tableTo": "sports_seasons",
"columnsFrom": [
"sports_season_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.season_template_sports": {
"name": "season_template_sports",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"template_id": {
"name": "template_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"sports_season_id": {
"name": "sports_season_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"season_template_sports_template_id_season_templates_id_fk": {
"name": "season_template_sports_template_id_season_templates_id_fk",
"tableFrom": "season_template_sports",
"tableTo": "season_templates",
"columnsFrom": [
"template_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
},
"season_template_sports_sports_season_id_sports_seasons_id_fk": {
"name": "season_template_sports_sports_season_id_sports_seasons_id_fk",
"tableFrom": "season_template_sports",
"tableTo": "sports_seasons",
"columnsFrom": [
"sports_season_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.season_templates": {
"name": "season_templates",
"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
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"year": {
"name": "year",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"is_active": {
"name": "is_active",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"default": true
},
"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'"
},
"template_id": {
"name": "template_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"flex_spots": {
"name": "flex_spots",
"type": "integer",
"primaryKey": false,
"notNull": true,
"default": 0
},
"invite_code": {
"name": "invite_code",
"type": "varchar(20)",
"primaryKey": false,
"notNull": true
},
"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"
},
"seasons_template_id_season_templates_id_fk": {
"name": "seasons_template_id_season_templates_id_fk",
"tableFrom": "seasons",
"tableTo": "season_templates",
"columnsFrom": [
"template_id"
],
"columnsTo": [
"id"
],
"onDelete": "set null",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"seasons_invite_code_unique": {
"name": "seasons_invite_code_unique",
"nullsNotDistinct": false,
"columns": [
"invite_code"
]
}
},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.sports": {
"name": "sports",
"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
},
"type": {
"name": "type",
"type": "sport_type",
"typeSchema": "public",
"primaryKey": false,
"notNull": true
},
"slug": {
"name": "slug",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"icon_url": {
"name": "icon_url",
"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": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"sports_slug_unique": {
"name": "sports_slug_unique",
"nullsNotDistinct": false,
"columns": [
"slug"
]
}
},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.sports_seasons": {
"name": "sports_seasons",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"sport_id": {
"name": "sport_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"year": {
"name": "year",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"start_date": {
"name": "start_date",
"type": "date",
"primaryKey": false,
"notNull": false
},
"end_date": {
"name": "end_date",
"type": "date",
"primaryKey": false,
"notNull": false
},
"status": {
"name": "status",
"type": "sports_season_status",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
"default": "'upcoming'"
},
"scoring_type": {
"name": "scoring_type",
"type": "scoring_type",
"typeSchema": "public",
"primaryKey": false,
"notNull": true
},
"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": {
"sports_seasons_sport_id_sports_id_fk": {
"name": "sports_seasons_sport_id_sports_id_fk",
"tableFrom": "sports_seasons",
"tableTo": "sports",
"columnsFrom": [
"sport_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
},
"username": {
"name": "username",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"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
},
"is_admin": {
"name": "is_admin",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"default": 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.scoring_type": {
"name": "scoring_type",
"schema": "public",
"values": [
"playoffs",
"regular_season",
"majors"
]
},
"public.season_status": {
"name": "season_status",
"schema": "public",
"values": [
"pre_draft",
"draft",
"active",
"completed"
]
},
"public.sport_type": {
"name": "sport_type",
"schema": "public",
"values": [
"team",
"individual"
]
},
"public.sports_season_status": {
"name": "sports_season_status",
"schema": "public",
"values": [
"upcoming",
"active",
"completed"
]
}
},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View file

@ -78,6 +78,13 @@
"when": 1760469105799, "when": 1760469105799,
"tag": "0010_complex_the_stranger", "tag": "0010_complex_the_stranger",
"breakpoints": true "breakpoints": true
},
{
"idx": 11,
"version": "7",
"when": 1760501621605,
"tag": "0011_gifted_mystique",
"breakpoints": true
} }
] ]
} }