Show commish-only leagues in a separate dashboard section, fixes #336
Leagues where the user is a commissioner but has no team are now shown in a "Leagues I Commish" subsection below their member leagues, rather than silently appearing with no stats. A live draft always takes priority over the commish-only row so commissioners still see the draft banner. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
27e8ae505a
commit
e0ba84669c
3 changed files with 45 additions and 3 deletions
|
|
@ -18,6 +18,7 @@ export interface LeagueRowProps {
|
||||||
draftDateTime?: string | null;
|
draftDateTime?: string | null;
|
||||||
picksUntilMyTurn?: number;
|
picksUntilMyTurn?: number;
|
||||||
draftPosition?: number;
|
draftPosition?: number;
|
||||||
|
isCommishOnly?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
||||||
|
|
@ -205,10 +206,28 @@ function CompletedRow({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Commish-only row ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function CommishOnlyRow({ leagueId, leagueName }: Pick<LeagueRowProps, "leagueId" | "leagueName">) {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
to={`/leagues/${leagueId}`}
|
||||||
|
className="flex items-center gap-3 rounded-lg bg-card px-3 py-3 sm:px-5 sm:py-4 hover:bg-white/[0.06] transition-colors"
|
||||||
|
>
|
||||||
|
<LeagueAvatar leagueId={leagueId} leagueName={leagueName} />
|
||||||
|
<div className="min-w-0">
|
||||||
|
<p className="font-semibold leading-tight truncate">{leagueName}</p>
|
||||||
|
<p className="text-xs text-muted-foreground mt-0.5">Commissioner</p>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Public export ────────────────────────────────────────────────────────────
|
// ─── Public export ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export function LeagueRow(props: LeagueRowProps) {
|
export function LeagueRow(props: LeagueRowProps) {
|
||||||
if (props.status === "draft") return <DraftRow {...props} />;
|
if (props.status === "draft") return <DraftRow {...props} />;
|
||||||
|
if (props.isCommishOnly) return <CommishOnlyRow {...props} />;
|
||||||
if (props.status === "active") return <ActiveRow {...props} />;
|
if (props.status === "active") return <ActiveRow {...props} />;
|
||||||
if (props.status === "pre_draft") return <PreDraftRow {...props} />;
|
if (props.status === "pre_draft") return <PreDraftRow {...props} />;
|
||||||
return <CompletedRow {...props} />;
|
return <CompletedRow {...props} />;
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,12 @@ interface MyLeaguesCardProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MyLeaguesCard({ leagues, viewAllUrl }: MyLeaguesCardProps) {
|
export function MyLeaguesCard({ leagues, viewAllUrl }: MyLeaguesCardProps) {
|
||||||
const sorted = leagues.toSorted((a, b) => {
|
const memberLeagues = leagues.filter((l) => !l.isCommishOnly);
|
||||||
|
const commishOnlyLeagues = leagues
|
||||||
|
.filter((l) => l.isCommishOnly)
|
||||||
|
.toSorted((a, b) => a.leagueName.localeCompare(b.leagueName));
|
||||||
|
|
||||||
|
const sortedMember = memberLeagues.toSorted((a, b) => {
|
||||||
const statusDiff = STATUS_ORDER[a.status] - STATUS_ORDER[b.status];
|
const statusDiff = STATUS_ORDER[a.status] - STATUS_ORDER[b.status];
|
||||||
if (statusDiff !== 0) return statusDiff;
|
if (statusDiff !== 0) return statusDiff;
|
||||||
if (a.status === "active") {
|
if (a.status === "active") {
|
||||||
|
|
@ -35,11 +40,13 @@ export function MyLeaguesCard({ leagues, viewAllUrl }: MyLeaguesCardProps) {
|
||||||
</Link>
|
</Link>
|
||||||
) : undefined;
|
) : undefined;
|
||||||
|
|
||||||
|
const isEmpty = sortedMember.length === 0 && commishOnlyLeagues.length === 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="bg-[#181b22] gap-2">
|
<Card className="bg-[#181b22] gap-2">
|
||||||
<SectionCardHeader icon={Shield} title="My Leagues" right={right} />
|
<SectionCardHeader icon={Shield} title="My Leagues" right={right} />
|
||||||
<CardContent className="px-3 sm:px-6">
|
<CardContent className="px-3 sm:px-6">
|
||||||
{sorted.length === 0 ? (
|
{isEmpty ? (
|
||||||
<div className="py-4 text-center">
|
<div className="py-4 text-center">
|
||||||
<p className="text-sm text-muted-foreground mb-3">
|
<p className="text-sm text-muted-foreground mb-3">
|
||||||
You aren't a member of any leagues yet.
|
You aren't a member of any leagues yet.
|
||||||
|
|
@ -53,9 +60,20 @@ export function MyLeaguesCard({ leagues, viewAllUrl }: MyLeaguesCardProps) {
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{sorted.map((league) => (
|
{sortedMember.map((league) => (
|
||||||
<LeagueRow key={league.leagueId} {...league} />
|
<LeagueRow key={league.leagueId} {...league} />
|
||||||
))}
|
))}
|
||||||
|
{commishOnlyLeagues.length > 0 && (
|
||||||
|
<>
|
||||||
|
{sortedMember.length > 0 && <div className="border-t border-border/40 pt-1" />}
|
||||||
|
<p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground px-1">
|
||||||
|
Leagues I Commish
|
||||||
|
</p>
|
||||||
|
{commishOnlyLeagues.map((league) => (
|
||||||
|
<LeagueRow key={league.leagueId} {...league} />
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|
|
||||||
|
|
@ -63,9 +63,12 @@ export async function loader(args: Route.LoaderArgs) {
|
||||||
let completionPercentage = 0;
|
let completionPercentage = 0;
|
||||||
let picksUntilMyTurn: number | undefined;
|
let picksUntilMyTurn: number | undefined;
|
||||||
let draftPosition: number | undefined;
|
let draftPosition: number | undefined;
|
||||||
|
// False when there's no current season — a commish with no season has no team by definition.
|
||||||
|
let hasTeam = false;
|
||||||
|
|
||||||
if (league.currentSeasonId && seasonWithSports?.seasonSports) {
|
if (league.currentSeasonId && seasonWithSports?.seasonSports) {
|
||||||
const myTeam = await findTeamByOwnerAndSeason(userId, league.currentSeasonId);
|
const myTeam = await findTeamByOwnerAndSeason(userId, league.currentSeasonId);
|
||||||
|
hasTeam = !!myTeam;
|
||||||
|
|
||||||
if (myTeam) {
|
if (myTeam) {
|
||||||
// Fetch standing, completion %, and calendar events in parallel
|
// Fetch standing, completion %, and calendar events in parallel
|
||||||
|
|
@ -153,6 +156,7 @@ export async function loader(args: Route.LoaderArgs) {
|
||||||
draftPosition,
|
draftPosition,
|
||||||
draftDateTime: season?.draftDateTime?.toISOString() ?? null,
|
draftDateTime: season?.draftDateTime?.toISOString() ?? null,
|
||||||
calendarEvents,
|
calendarEvents,
|
||||||
|
hasTeam,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
@ -209,6 +213,7 @@ export default function Home({ loaderData }: Route.ComponentProps) {
|
||||||
picksUntilMyTurn: league.picksUntilMyTurn,
|
picksUntilMyTurn: league.picksUntilMyTurn,
|
||||||
draftPosition: league.draftPosition,
|
draftPosition: league.draftPosition,
|
||||||
draftDateTime: league.draftDateTime,
|
draftDateTime: league.draftDateTime,
|
||||||
|
isCommishOnly: !league.hasTeam,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue