Fix Rosters page.
This commit is contained in:
parent
09b5193f18
commit
269ba05316
3 changed files with 108 additions and 31 deletions
|
|
@ -1,5 +1,4 @@
|
||||||
import { memo, useEffect, useMemo, useState } from "react";
|
import { memo, useEffect, useMemo, useState } from "react";
|
||||||
import { Badge } from "~/components/ui/badge";
|
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
|
|
@ -7,6 +6,7 @@ import {
|
||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "~/components/ui/select";
|
} from "~/components/ui/select";
|
||||||
|
import { DraftPickCell } from "./DraftPickCell";
|
||||||
import { type DraftPick, groupPicksByTeamAndSport } from "./picks-utils";
|
import { type DraftPick, groupPicksByTeamAndSport } from "./picks-utils";
|
||||||
|
|
||||||
interface TeamRosterViewProps {
|
interface TeamRosterViewProps {
|
||||||
|
|
@ -14,6 +14,7 @@ interface TeamRosterViewProps {
|
||||||
ownerMap?: Record<string, string>;
|
ownerMap?: Record<string, string>;
|
||||||
picks: DraftPick[];
|
picks: DraftPick[];
|
||||||
sports: Array<{ id: string; name: string }>;
|
sports: Array<{ id: string; name: string }>;
|
||||||
|
totalRounds: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TeamRosterView = memo(function TeamRosterView({
|
export const TeamRosterView = memo(function TeamRosterView({
|
||||||
|
|
@ -21,12 +22,12 @@ export const TeamRosterView = memo(function TeamRosterView({
|
||||||
ownerMap = {},
|
ownerMap = {},
|
||||||
picks,
|
picks,
|
||||||
sports,
|
sports,
|
||||||
|
totalRounds,
|
||||||
}: TeamRosterViewProps) {
|
}: TeamRosterViewProps) {
|
||||||
const [selectedTeamId, setSelectedTeamId] = useState(
|
const [selectedTeamId, setSelectedTeamId] = useState(
|
||||||
draftSlots[0]?.team.id ?? ""
|
draftSlots[0]?.team.id ?? ""
|
||||||
);
|
);
|
||||||
|
|
||||||
// Reset selection if the selected team is no longer present in the slot list
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
draftSlots.length > 0 &&
|
draftSlots.length > 0 &&
|
||||||
|
|
@ -46,16 +47,42 @@ export const TeamRosterView = memo(function TeamRosterView({
|
||||||
[picksByTeamAndSport, selectedTeamId]
|
[picksByTeamAndSport, selectedTeamId]
|
||||||
);
|
);
|
||||||
|
|
||||||
const sportsWithPicks = useMemo(
|
const flexPicksAvailable = useMemo(
|
||||||
|
() => Math.max(0, totalRounds - sports.length),
|
||||||
|
[totalRounds, sports.length]
|
||||||
|
);
|
||||||
|
|
||||||
|
const flexPicksUsed = useMemo(() => {
|
||||||
|
let used = 0;
|
||||||
|
teamPicks?.forEach((sportPicks) => {
|
||||||
|
if (sportPicks.length > 1) used += sportPicks.length - 1;
|
||||||
|
});
|
||||||
|
return used;
|
||||||
|
}, [teamPicks]);
|
||||||
|
|
||||||
|
const flexPicksRemaining = useMemo(
|
||||||
|
() => flexPicksAvailable - flexPicksUsed,
|
||||||
|
[flexPicksAvailable, flexPicksUsed]
|
||||||
|
);
|
||||||
|
|
||||||
|
const missingSports = useMemo(
|
||||||
|
() => sports.filter((s) => (teamPicks?.get(s.id)?.length ?? 0) === 0),
|
||||||
|
[sports, teamPicks]
|
||||||
|
);
|
||||||
|
|
||||||
|
const draftedSports = useMemo(
|
||||||
() => sports.filter((s) => (teamPicks?.get(s.id)?.length ?? 0) > 0),
|
() => sports.filter((s) => (teamPicks?.get(s.id)?.length ?? 0) > 0),
|
||||||
[sports, teamPicks]
|
[sports, teamPicks]
|
||||||
);
|
);
|
||||||
|
|
||||||
const totalPicks = useMemo(() => {
|
const flexColor =
|
||||||
let count = 0;
|
flexPicksAvailable === 0
|
||||||
teamPicks?.forEach((sp) => (count += sp.length));
|
? "text-muted-foreground"
|
||||||
return count;
|
: flexPicksRemaining <= 0
|
||||||
}, [teamPicks]);
|
? "text-destructive"
|
||||||
|
: flexPicksRemaining <= Math.ceil(flexPicksAvailable * 0.25)
|
||||||
|
? "text-amber-500 dark:text-amber-400"
|
||||||
|
: "text-emerald-500 dark:text-emerald-400";
|
||||||
|
|
||||||
if (draftSlots.length === 0) {
|
if (draftSlots.length === 0) {
|
||||||
return (
|
return (
|
||||||
|
|
@ -68,6 +95,7 @@ export const TeamRosterView = memo(function TeamRosterView({
|
||||||
return (
|
return (
|
||||||
<div className="h-full overflow-y-auto">
|
<div className="h-full overflow-y-auto">
|
||||||
<div className="p-4 space-y-4">
|
<div className="p-4 space-y-4">
|
||||||
|
{/* Team selector */}
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<label
|
<label
|
||||||
htmlFor="roster-team-select"
|
htmlFor="roster-team-select"
|
||||||
|
|
@ -89,31 +117,76 @@ export const TeamRosterView = memo(function TeamRosterView({
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{totalPicks === 0 ? (
|
{/* Mini dashboard */}
|
||||||
<p className="text-sm text-muted-foreground text-center py-6">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
No picks yet
|
<div className="rounded-lg border bg-card px-3 py-2.5 space-y-0.5">
|
||||||
</p>
|
<p className="text-xs text-muted-foreground font-medium">Flex Picks</p>
|
||||||
|
{flexPicksAvailable === 0 ? (
|
||||||
|
<p className="text-sm font-semibold text-muted-foreground">N/A</p>
|
||||||
) : (
|
) : (
|
||||||
sportsWithPicks.map((sport) => {
|
<>
|
||||||
|
<p className={`text-xl font-bold tabular-nums leading-none ${flexColor}`}>
|
||||||
|
{flexPicksRemaining}
|
||||||
|
<span className="text-sm font-normal text-muted-foreground ml-1">
|
||||||
|
/ {flexPicksAvailable}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-muted-foreground">remaining</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="rounded-lg border bg-card px-3 py-2.5 space-y-0.5">
|
||||||
|
<p className="text-xs text-muted-foreground font-medium">Sports Remaining</p>
|
||||||
|
<p className={`text-xl font-bold tabular-nums leading-none ${missingSports.length > 0 ? "text-foreground" : "text-emerald-500 dark:text-emerald-400"}`}>
|
||||||
|
{missingSports.length}
|
||||||
|
<span className="text-sm font-normal text-muted-foreground ml-1">
|
||||||
|
/ {sports.length}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-muted-foreground">to draft</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Missing sports list */}
|
||||||
|
{missingSports.length > 0 && (
|
||||||
|
<div>
|
||||||
|
<p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-1.5">Still Needed</p>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
{missingSports.map((s) => s.name).join(" · ")}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Per-sport pick cells */}
|
||||||
|
{draftedSports.length === 0 ? (
|
||||||
|
<p className="text-sm text-muted-foreground text-center py-6">No picks yet</p>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-3">
|
||||||
|
{draftedSports.map((sport) => {
|
||||||
const sportPicks = teamPicks?.get(sport.id) ?? [];
|
const sportPicks = teamPicks?.get(sport.id) ?? [];
|
||||||
return (
|
return (
|
||||||
<div key={sport.id}>
|
<div key={sport.id}>
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-1.5">
|
||||||
<h3 className="text-sm font-semibold">{sport.name}</h3>
|
{sport.name}
|
||||||
<Badge variant="secondary" className="text-xs px-1.5 py-0">
|
</p>
|
||||||
{sportPicks.length}
|
<div className="flex flex-wrap gap-1">
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
<ul className="space-y-1 pl-2">
|
|
||||||
{sportPicks.map((pick) => (
|
{sportPicks.map((pick) => (
|
||||||
<li key={pick.id} className="text-sm text-foreground">
|
<DraftPickCell
|
||||||
{pick.participant.name}
|
key={pick.id}
|
||||||
</li>
|
pickNumber={pick.pickNumber}
|
||||||
|
round={pick.round}
|
||||||
|
pickInRound={pick.pickInRound}
|
||||||
|
state="picked"
|
||||||
|
pick={pick}
|
||||||
|
corona="gradient"
|
||||||
|
className="w-44 flex-none"
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})
|
})}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
export type DraftPick = {
|
export type DraftPick = {
|
||||||
id: string;
|
id: string;
|
||||||
|
pickNumber: number;
|
||||||
|
round: number;
|
||||||
|
pickInRound: number;
|
||||||
team: { id: string; name: string };
|
team: { id: string; name: string };
|
||||||
participant: { id: string; name: string };
|
participant: { id: string; name: string };
|
||||||
sport: { id: string; name: string };
|
sport: { id: string; name: string };
|
||||||
|
|
|
||||||
|
|
@ -1577,6 +1577,7 @@ export default function DraftRoom() {
|
||||||
picks,
|
picks,
|
||||||
sports: seasonSportsData,
|
sports: seasonSportsData,
|
||||||
ownerMap,
|
ownerMap,
|
||||||
|
totalRounds: season.draftRounds,
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAutodraftUpdate = useCallback(
|
const handleAutodraftUpdate = useCallback(
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue