Update draft board page
This commit is contained in:
parent
f17d2a3250
commit
c78067327d
8 changed files with 385 additions and 87 deletions
|
|
@ -4,7 +4,8 @@ import {
|
|||
ContextMenuItem,
|
||||
ContextMenuTrigger,
|
||||
} from "~/components/ui/context-menu";
|
||||
import { DraftPickCell } from "~/components/draft/DraftPickCell";
|
||||
import { DraftPickCell, type CoronaState } from "~/components/draft/DraftPickCell";
|
||||
import { TeamAvatar } from "~/components/TeamAvatar";
|
||||
|
||||
interface DraftCell {
|
||||
pickNumber: number;
|
||||
|
|
@ -20,6 +21,7 @@ interface DraftGridProps {
|
|||
team: {
|
||||
id: string;
|
||||
name: string;
|
||||
logoUrl?: string | null;
|
||||
};
|
||||
}>;
|
||||
draftGrid: Array<Array<DraftCell | null>>;
|
||||
|
|
@ -33,6 +35,7 @@ interface DraftGridProps {
|
|||
autodraftStatus?: Record<string, boolean>;
|
||||
connectedTeams?: Set<string>;
|
||||
ownerMap?: Record<string, string>;
|
||||
coronaStates?: Record<string, CoronaState>;
|
||||
}
|
||||
|
||||
export function DraftGrid({
|
||||
|
|
@ -48,6 +51,7 @@ export function DraftGrid({
|
|||
autodraftStatus = {},
|
||||
connectedTeams = new Set(),
|
||||
ownerMap = {},
|
||||
coronaStates = {},
|
||||
}: DraftGridProps) {
|
||||
const totalTeams = draftSlots.length;
|
||||
|
||||
|
|
@ -65,6 +69,14 @@ export function DraftGrid({
|
|||
|
||||
return (
|
||||
<div key={slot.id} className="flex-1 min-w-32 text-center">
|
||||
<div className="flex justify-center mb-1">
|
||||
<TeamAvatar
|
||||
teamId={slot.team.id}
|
||||
teamName={ownerMap[slot.team.id] || slot.team.name}
|
||||
logoUrl={slot.team.logoUrl}
|
||||
size="md"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={`font-semibold text-sm truncate px-2 ${!isConnected ? "italic text-muted-foreground" : ""}`}
|
||||
>
|
||||
|
|
@ -94,7 +106,7 @@ export function DraftGrid({
|
|||
</div>
|
||||
|
||||
{/* Draft Grid Rows */}
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-2.5">
|
||||
{draftGrid.map((roundPicks, roundIndex) => {
|
||||
const round = roundIndex + 1;
|
||||
const isEvenRound = round % 2 === 0;
|
||||
|
|
@ -129,6 +141,9 @@ export function DraftGrid({
|
|||
}
|
||||
: undefined
|
||||
}
|
||||
coronaState={
|
||||
cell ? coronaStates[cell.participant.id] : undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
|
|||
61
app/components/TeamAvatar.tsx
Normal file
61
app/components/TeamAvatar.tsx
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
const TEAM_AVATAR_COLORS = [
|
||||
"#adf661",
|
||||
"#2ce1c1",
|
||||
"#8b5cf6",
|
||||
"#f59e0b",
|
||||
"#ef4444",
|
||||
"#3b82f6",
|
||||
];
|
||||
|
||||
function hashTeamId(id: string): number {
|
||||
let hash = 0;
|
||||
for (let i = 0; i < id.length; i++) {
|
||||
hash = (hash * 31 + id.charCodeAt(i)) & 0xffff;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
interface TeamAvatarProps {
|
||||
teamId: string;
|
||||
teamName: string;
|
||||
logoUrl?: string | null;
|
||||
size?: "sm" | "md" | "lg";
|
||||
}
|
||||
|
||||
const sizeClasses = {
|
||||
sm: "h-6 w-6 text-[10px]",
|
||||
md: "h-9 w-9 text-sm",
|
||||
lg: "h-12 w-12 text-base",
|
||||
};
|
||||
|
||||
export function TeamAvatar({ teamId, teamName, logoUrl, size = "md" }: TeamAvatarProps) {
|
||||
if (logoUrl) {
|
||||
return (
|
||||
<img
|
||||
src={logoUrl}
|
||||
alt={teamName}
|
||||
className={`${sizeClasses[size]} object-cover shrink-0`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const color = TEAM_AVATAR_COLORS[hashTeamId(teamId) % TEAM_AVATAR_COLORS.length];
|
||||
const initials =
|
||||
teamName
|
||||
.split(/\s+/)
|
||||
.filter(Boolean)
|
||||
.slice(0, 2)
|
||||
.map((w) => w[0].toUpperCase())
|
||||
.join("") || "?";
|
||||
|
||||
return (
|
||||
<div
|
||||
role="img"
|
||||
aria-label={teamName}
|
||||
className={`${sizeClasses[size]} flex shrink-0 items-center justify-center font-bold`}
|
||||
style={{ backgroundColor: "#000", color }}
|
||||
>
|
||||
{initials}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -88,7 +88,7 @@ describe('DraftGrid Component', () => {
|
|||
/>
|
||||
);
|
||||
|
||||
const currentPickCell = screen.getByText('On Clock').parentElement;
|
||||
const currentPickCell = screen.getByText('On Clock').closest('[class*="border-electric"]');
|
||||
expect(currentPickCell).toHaveClass('border-electric');
|
||||
expect(currentPickCell).toHaveClass('bg-electric/20');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { memo, useMemo, useState } from "react";
|
||||
import { TeamAvatar } from "~/components/TeamAvatar";
|
||||
import {
|
||||
ContextMenu,
|
||||
ContextMenuContent,
|
||||
|
|
@ -28,6 +29,7 @@ interface DraftGridSectionProps {
|
|||
team: {
|
||||
id: string;
|
||||
name: string;
|
||||
logoUrl?: string | null;
|
||||
};
|
||||
}>;
|
||||
draftGrid: Array<
|
||||
|
|
@ -99,6 +101,14 @@ export const DraftGridSection = memo(function DraftGridSection({
|
|||
|
||||
const headerInner = (
|
||||
<div className="relative">
|
||||
<div className="flex justify-center mb-1">
|
||||
<TeamAvatar
|
||||
teamId={slot.team.id}
|
||||
teamName={ownerMap[slot.team.id] || slot.team.name}
|
||||
logoUrl={slot.team.logoUrl}
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={`font-semibold text-sm truncate px-2 ${
|
||||
slot.team.id === currentTeamId
|
||||
|
|
@ -164,7 +174,7 @@ export const DraftGridSection = memo(function DraftGridSection({
|
|||
</div>
|
||||
|
||||
{/* Draft Grid Rows */}
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-2.5">
|
||||
{draftGrid.map((roundPicks, roundIndex) => {
|
||||
const round = roundIndex + 1;
|
||||
const isEvenRound = round % 2 === 0;
|
||||
|
|
|
|||
|
|
@ -134,3 +134,107 @@ export const CoronaVariantsRow: Story = {
|
|||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
export const CoronaStateEliminated: Story = {
|
||||
name: "Corona State — Eliminated (0 pts)",
|
||||
args: {
|
||||
...basePickArgs,
|
||||
coronaState: { type: "eliminated", points: 0 },
|
||||
},
|
||||
};
|
||||
|
||||
export const CoronaStatePending: Story = {
|
||||
name: "Corona State — Pending (no result)",
|
||||
args: {
|
||||
...basePickArgs,
|
||||
coronaState: { type: "pending" },
|
||||
},
|
||||
};
|
||||
|
||||
export const CoronaStateScored100: Story = {
|
||||
name: "Corona State — Scored 100%",
|
||||
args: {
|
||||
...basePickArgs,
|
||||
coronaState: { type: "scored", brightness: 1, points: 100 },
|
||||
},
|
||||
};
|
||||
|
||||
export const CoronaStateScored70: Story = {
|
||||
name: "Corona State — Scored 70%",
|
||||
args: {
|
||||
...basePickArgs,
|
||||
coronaState: { type: "scored", brightness: 0.7, points: 70 },
|
||||
},
|
||||
};
|
||||
|
||||
export const CoronaStateScored45: Story = {
|
||||
name: "Corona State — Scored 45%",
|
||||
args: {
|
||||
...basePickArgs,
|
||||
coronaState: { type: "scored", brightness: 0.45, points: 45 },
|
||||
},
|
||||
};
|
||||
|
||||
export const CoronaStateScored25: Story = {
|
||||
name: "Corona State — Scored 25%",
|
||||
args: {
|
||||
...basePickArgs,
|
||||
coronaState: { type: "scored", brightness: 0.25, points: 25 },
|
||||
},
|
||||
};
|
||||
|
||||
export const CoronaStatesRow: Story = {
|
||||
name: "Corona States — All Side by Side",
|
||||
render: () => (
|
||||
<div className="flex gap-2 items-stretch">
|
||||
<DraftPickCell
|
||||
pickNumber={1}
|
||||
round={1}
|
||||
pickInRound={1}
|
||||
state="picked"
|
||||
pick={basePickArgs.pick}
|
||||
coronaState={{ type: "eliminated", points: 0 }}
|
||||
/>
|
||||
<DraftPickCell
|
||||
pickNumber={2}
|
||||
round={1}
|
||||
pickInRound={2}
|
||||
state="picked"
|
||||
pick={basePickArgs.pick}
|
||||
coronaState={{ type: "pending" }}
|
||||
/>
|
||||
<DraftPickCell
|
||||
pickNumber={3}
|
||||
round={1}
|
||||
pickInRound={3}
|
||||
state="picked"
|
||||
pick={basePickArgs.pick}
|
||||
coronaState={{ type: "scored", brightness: 1, points: 100 }}
|
||||
/>
|
||||
<DraftPickCell
|
||||
pickNumber={4}
|
||||
round={1}
|
||||
pickInRound={4}
|
||||
state="picked"
|
||||
pick={basePickArgs.pick}
|
||||
coronaState={{ type: "scored", brightness: 0.7, points: 70 }}
|
||||
/>
|
||||
<DraftPickCell
|
||||
pickNumber={5}
|
||||
round={1}
|
||||
pickInRound={5}
|
||||
state="picked"
|
||||
pick={basePickArgs.pick}
|
||||
coronaState={{ type: "scored", brightness: 0.45, points: 45 }}
|
||||
/>
|
||||
<DraftPickCell
|
||||
pickNumber={6}
|
||||
round={1}
|
||||
pickInRound={6}
|
||||
state="picked"
|
||||
pick={basePickArgs.pick}
|
||||
coronaState={{ type: "scored", brightness: 0.25, points: 25 }}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
import type { ReactNode } from "react";
|
||||
import type { CSSProperties, ReactNode } from "react";
|
||||
import { cn } from "~/lib/utils";
|
||||
|
||||
export type CoronaState =
|
||||
| { type: "eliminated"; points: 0 }
|
||||
| { type: "pending" }
|
||||
| { type: "scored"; brightness: number; points: number };
|
||||
|
||||
type CoronaVariant =
|
||||
| "gradient"
|
||||
| "electric"
|
||||
|
|
@ -18,6 +23,7 @@ interface DraftPickCellProps {
|
|||
sport: { name: string };
|
||||
};
|
||||
corona?: CoronaVariant;
|
||||
coronaState?: CoronaState;
|
||||
className?: string;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
|
@ -30,6 +36,20 @@ const coronaClasses: Record<CoronaVariant, string> = {
|
|||
coral: "bg-coral-accent",
|
||||
};
|
||||
|
||||
function getCoronaStyle(cs: CoronaState): CSSProperties {
|
||||
switch (cs.type) {
|
||||
case "eliminated":
|
||||
return { background: "oklch(0.45 0.20 25)" };
|
||||
case "pending":
|
||||
return { background: "rgba(255, 255, 255, 0.08)" };
|
||||
case "scored":
|
||||
return {
|
||||
background: "linear-gradient(to bottom, #adf661, #2ce1c1)",
|
||||
opacity: cs.brightness,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function DraftPickCell({
|
||||
pickNumber,
|
||||
round,
|
||||
|
|
@ -37,6 +57,7 @@ export function DraftPickCell({
|
|||
state,
|
||||
pick,
|
||||
corona = "gradient",
|
||||
coronaState,
|
||||
className,
|
||||
children,
|
||||
}: DraftPickCellProps) {
|
||||
|
|
@ -49,17 +70,22 @@ export function DraftPickCell({
|
|||
className,
|
||||
)}
|
||||
>
|
||||
{showCorona && (
|
||||
{showCorona && coronaState ? (
|
||||
<div
|
||||
className="absolute top-0.5 bottom-0.5 left-0 right-0 rounded-lg"
|
||||
style={getCoronaStyle(coronaState)}
|
||||
/>
|
||||
) : showCorona ? (
|
||||
<div
|
||||
className={cn(
|
||||
"absolute top-0.5 bottom-0.5 left-0 right-0 rounded-lg",
|
||||
coronaClasses[corona],
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
) : null}
|
||||
<div
|
||||
className={cn(
|
||||
"relative z-10 h-20 border-2 rounded-lg p-2 transition-all",
|
||||
"relative z-10 h-14 border-2 rounded-lg p-2 transition-all",
|
||||
showCorona && "mr-1",
|
||||
state === "current"
|
||||
? "border-electric bg-electric/20 shadow-lg shadow-electric/30 ring-2 ring-electric/50 animate-pulse"
|
||||
|
|
@ -69,19 +95,28 @@ export function DraftPickCell({
|
|||
)}
|
||||
title={`Overall Pick #${pickNumber}`}
|
||||
>
|
||||
<div className="text-xs font-mono text-muted-foreground mb-1">
|
||||
{round}.{String(pickInRound).padStart(2, "0")}
|
||||
</div>
|
||||
<div className="flex gap-1 items-start">
|
||||
{state === "picked" && pick ? (
|
||||
<div className="text-xs">
|
||||
<div className="font-semibold truncate">{pick.participant.name}</div>
|
||||
<div className="text-muted-foreground truncate">
|
||||
<div className="text-sm min-w-0 flex-1">
|
||||
<div className="font-semibold truncate text-sm">{pick.participant.name}</div>
|
||||
<div className="text-muted-foreground truncate text-xs">
|
||||
{pick.sport.name}
|
||||
</div>
|
||||
</div>
|
||||
) : state === "current" ? (
|
||||
<div className="text-sm font-bold text-electric">On Clock</div>
|
||||
) : null}
|
||||
<div className="flex flex-col items-end shrink-0">
|
||||
<div className="text-xs font-mono text-muted-foreground leading-5">
|
||||
{round}.{String(pickInRound).padStart(2, "0")}
|
||||
</div>
|
||||
{coronaState && coronaState.type !== "pending" && (
|
||||
<span className={`text-xs font-mono font-bold mt-0.5 ${coronaState.type === "eliminated" ? "text-coral-accent" : "text-emerald-400"}`}>
|
||||
{coronaState.type === "scored" && "▲"}{coronaState.points}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{children && <div className="absolute inset-0 pointer-events-none [&_*]:pointer-events-auto">{children}</div>}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,47 +3,7 @@ import { Link } from "react-router";
|
|||
import { Button } from "~/components/ui/button";
|
||||
import { Card, CardContent, CardHeader } from "~/components/ui/card";
|
||||
import { GradientIcon } from "~/components/ui/GradientIcon";
|
||||
|
||||
// ─── TeamAvatar ───────────────────────────────────────────────────────────────
|
||||
|
||||
const TEAM_AVATAR_COLORS = [
|
||||
"#adf661",
|
||||
"#2ce1c1",
|
||||
"#8b5cf6",
|
||||
"#f59e0b",
|
||||
"#ef4444",
|
||||
"#3b82f6",
|
||||
];
|
||||
|
||||
function hashTeamId(id: string): number {
|
||||
let hash = 0;
|
||||
for (let i = 0; i < id.length; i++) {
|
||||
hash = (hash * 31 + id.charCodeAt(i)) & 0xffff;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
function TeamAvatar({ teamId, teamName }: { teamId: string; teamName: string }) {
|
||||
const color = TEAM_AVATAR_COLORS[hashTeamId(teamId) % TEAM_AVATAR_COLORS.length];
|
||||
const initials =
|
||||
teamName
|
||||
.split(/\s+/)
|
||||
.filter(Boolean)
|
||||
.slice(0, 2)
|
||||
.map((w) => w[0].toUpperCase())
|
||||
.join("") || "?";
|
||||
|
||||
return (
|
||||
<div
|
||||
role="img"
|
||||
aria-label={teamName}
|
||||
className="h-9 w-9 flex shrink-0 items-center justify-center font-bold text-sm"
|
||||
style={{ backgroundColor: "#000", color }}
|
||||
>
|
||||
{initials}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
import { TeamAvatar } from "~/components/TeamAvatar";
|
||||
|
||||
// ─── Stat helpers (mirrors LeagueRow) ────────────────────────────────────────
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useLoaderData } from "react-router";
|
||||
import { eq, asc, and } from "drizzle-orm";
|
||||
import { useLoaderData, Link } from "react-router";
|
||||
import { eq, asc, and, inArray } from "drizzle-orm";
|
||||
import { getAuth } from "@clerk/react-router/server";
|
||||
import { database } from "~/database/context";
|
||||
import * as schema from "~/database/schema";
|
||||
|
|
@ -7,6 +7,10 @@ import { DraftGrid } from "~/components/DraftGrid";
|
|||
import { useDraftSocket } from "~/hooks/useDraftSocket";
|
||||
import { useState, useEffect } from "react";
|
||||
import { buildOwnerMap } from "~/lib/owner-map";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import { ArrowLeft } from "lucide-react";
|
||||
import { calculateFantasyPoints, calculateBracketPoints } from "~/models/scoring-rules";
|
||||
import type { CoronaState } from "~/components/draft/DraftPickCell";
|
||||
import type { Route } from "./+types/$leagueId.draft-board.$seasonId";
|
||||
|
||||
export function meta({ data }: Route.MetaArgs): Route.MetaDescriptors {
|
||||
|
|
@ -92,6 +96,7 @@ export async function loader(args: Route.LoaderArgs) {
|
|||
team: schema.teams,
|
||||
participant: schema.participants,
|
||||
sport: schema.sports,
|
||||
scoringPattern: schema.sportsSeasons.scoringPattern,
|
||||
})
|
||||
.from(schema.draftPicks)
|
||||
.innerJoin(schema.teams, eq(schema.draftPicks.teamId, schema.teams.id))
|
||||
|
|
@ -109,16 +114,113 @@ export async function loader(args: Route.LoaderArgs) {
|
|||
|
||||
const ownerMap = await buildOwnerMap(draftSlots);
|
||||
|
||||
const coronaStates: Record<string, CoronaState> = {};
|
||||
|
||||
if (draftPicks.length > 0) {
|
||||
const participantIds = draftPicks.map((p) => p.participant.id);
|
||||
const sportsSeasonIds = [
|
||||
...new Set(draftPicks.map((p) => p.participant.sportsSeasonId)),
|
||||
];
|
||||
|
||||
const results = await db
|
||||
.select({
|
||||
participantId: schema.participantResults.participantId,
|
||||
sportsSeasonId: schema.participantResults.sportsSeasonId,
|
||||
finalPosition: schema.participantResults.finalPosition,
|
||||
isPartialScore: schema.participantResults.isPartialScore,
|
||||
})
|
||||
.from(schema.participantResults)
|
||||
.where(inArray(schema.participantResults.participantId, participantIds));
|
||||
|
||||
const resultByParticipant = new Map(
|
||||
results.map((r) => [r.participantId, r])
|
||||
);
|
||||
|
||||
const maxPoints = season.pointsFor1st;
|
||||
|
||||
const bracketTemplateBySportsSeason = new Map<string, string | null>();
|
||||
if (sportsSeasonIds.length > 0) {
|
||||
const events = await db
|
||||
.select({
|
||||
sportsSeasonId: schema.scoringEvents.sportsSeasonId,
|
||||
bracketTemplateId: schema.scoringEvents.bracketTemplateId,
|
||||
})
|
||||
.from(schema.scoringEvents)
|
||||
.where(inArray(schema.scoringEvents.sportsSeasonId, sportsSeasonIds));
|
||||
for (const ev of events) {
|
||||
if (!bracketTemplateBySportsSeason.has(ev.sportsSeasonId)) {
|
||||
bracketTemplateBySportsSeason.set(
|
||||
ev.sportsSeasonId,
|
||||
ev.bracketTemplateId
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const scoringRules = {
|
||||
pointsFor1st: season.pointsFor1st,
|
||||
pointsFor2nd: season.pointsFor2nd,
|
||||
pointsFor3rd: season.pointsFor3rd,
|
||||
pointsFor4th: season.pointsFor4th,
|
||||
pointsFor5th: season.pointsFor5th,
|
||||
pointsFor6th: season.pointsFor6th,
|
||||
pointsFor7th: season.pointsFor7th,
|
||||
pointsFor8th: season.pointsFor8th,
|
||||
};
|
||||
|
||||
for (const pick of draftPicks) {
|
||||
const result = resultByParticipant.get(pick.participant.id);
|
||||
|
||||
if (!result || result.finalPosition === null) {
|
||||
coronaStates[pick.participant.id] = { type: "pending" };
|
||||
continue;
|
||||
}
|
||||
|
||||
if (result.finalPosition === 0 && !result.isPartialScore) {
|
||||
coronaStates[pick.participant.id] = { type: "eliminated", points: 0 };
|
||||
continue;
|
||||
}
|
||||
|
||||
if (result.finalPosition > 0) {
|
||||
const isBracket =
|
||||
pick.scoringPattern === "playoff_bracket";
|
||||
const templateId = isBracket
|
||||
? bracketTemplateBySportsSeason.get(
|
||||
pick.participant.sportsSeasonId
|
||||
) ?? null
|
||||
: null;
|
||||
const points = isBracket
|
||||
? calculateBracketPoints(
|
||||
result.finalPosition,
|
||||
scoringRules,
|
||||
templateId
|
||||
)
|
||||
: calculateFantasyPoints(result.finalPosition, scoringRules);
|
||||
const brightness =
|
||||
maxPoints > 0 ? Math.min(points / maxPoints, 1) : 0;
|
||||
coronaStates[pick.participant.id] = {
|
||||
type: "scored",
|
||||
brightness,
|
||||
points,
|
||||
};
|
||||
continue;
|
||||
}
|
||||
|
||||
coronaStates[pick.participant.id] = { type: "pending" };
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
season,
|
||||
draftSlots,
|
||||
draftPicks,
|
||||
ownerMap,
|
||||
coronaStates,
|
||||
};
|
||||
}
|
||||
|
||||
export default function DraftBoard() {
|
||||
const { season, draftSlots, draftPicks: initialPicks, ownerMap } = useLoaderData<typeof loader>();
|
||||
const { season, draftSlots, draftPicks: initialPicks, ownerMap, coronaStates } = useLoaderData<typeof loader>();
|
||||
const { isConnected, on, off } = useDraftSocket(season.id);
|
||||
const [picks, setPicks] = useState(initialPicks);
|
||||
const [currentPick, setCurrentPick] = useState(season.currentPickNumber || 1);
|
||||
|
|
@ -157,36 +259,46 @@ export default function DraftBoard() {
|
|||
draftGrid.push(roundPicks);
|
||||
}
|
||||
|
||||
const isDraftActive = season.status === "draft";
|
||||
const currentRound = totalTeams > 0 ? Math.ceil(currentPick / totalTeams) : 1;
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
{/* Header */}
|
||||
<div className="border-b bg-card sticky top-0 z-10">
|
||||
<div className="w-full px-4 py-4">
|
||||
<div className="w-full px-4 py-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold">
|
||||
{season.league.name} - {season.year} Draft Board
|
||||
<div className="flex items-center gap-3">
|
||||
<Link to="/">
|
||||
<img src="/logomark.svg" alt="Brackt" className="h-8" />
|
||||
</Link>
|
||||
<h1 className="text-2xl font-bold">
|
||||
{season.league.name} Draft Board
|
||||
</h1>
|
||||
<div className="flex gap-4 text-sm text-muted-foreground mt-1">
|
||||
<span>Round: {totalTeams > 0 ? Math.ceil(currentPick / totalTeams) : 1}</span>
|
||||
<span>Pick: {currentPick}</span>
|
||||
<span className="capitalize">
|
||||
{season.status.replace("_", " ")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{season.status === "draft" && (
|
||||
<div className="flex items-center gap-4">
|
||||
{isDraftActive && (
|
||||
<div className="flex items-center gap-3 text-sm text-muted-foreground">
|
||||
<span>Round {currentRound}</span>
|
||||
<span>Pick {currentPick}</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className={`w-3 h-3 rounded-full ${
|
||||
className={`w-2.5 h-2.5 rounded-full ${
|
||||
isConnected ? "bg-emerald-500" : "bg-coral-accent"
|
||||
}`}
|
||||
/>
|
||||
<span className="text-sm font-medium">
|
||||
<span className="font-medium">
|
||||
{isConnected ? "Connected" : "Disconnected"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<Button variant="outline" size="sm" asChild>
|
||||
<Link to={`/leagues/${season.leagueId}`}>
|
||||
<ArrowLeft className="mr-1.5 h-4 w-4" />
|
||||
Back to League
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -198,6 +310,7 @@ export default function DraftBoard() {
|
|||
draftGrid={draftGrid}
|
||||
currentPick={currentPick}
|
||||
ownerMap={ownerMap}
|
||||
coronaStates={coronaStates}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue